Gareth Wilson Gareth Wilson

Guide to Bringg Webhooks: Features and Best Practices

Published


Bringg webhooks keep your order management system, customer notifications and analytics in step with what's happening in deliveries: an order is created, assigned to a carrier or driver, the driver arrives, the order is completed, cancelled or running late. If you run fulfilment through Bringg, the delivery orchestration platform, webhooks are how those changes reach your own systems without polling Bringg's API.

This guide covers how Bringg webhook registrations work, the three authentication methods and why Static Headers is the one to use with a fixed check, the main events, what Bringg does when delivery fails, and the best practices for production.

What are Bringg webhooks?

You register a Bringg webhook once per event. Each registration has a Webhook Name (the event), one or more URLs, and optional settings for authentication, extra headers, filters and payload fields. Every time the event occurs, Bringg sends an HTTP POST with a JSON body to each URL on the registration. Sending one event to several systems doesn't need several registrations.

The body is usually the resource that changed, such as an order (a "task" in Bringg's API), a customer or a note, with a webhook_type field naming the event. Display names in the Bringg UI can differ from that value. Bringg's own example: the webhook shown as "Order Attachment Created" still sends webhook_type note_created.

Bringg doesn't sign the request body. It offers three authentication methods for webhooks, and the simplest, Static Headers, adds a header with a fixed value that your receiver checks.

Bringg webhook features

FeatureDetails
ConfigurationSettings > Platform Integrations > Webhooks > Add Webhook, as an Admin user
Subscription modelOne registration per event, with one or more URLs
Method and formatPOST with a JSON body
Event identifierwebhook_type in the body
AuthenticationJWT, OAuth 2.0 or Static Headers. None signs the body
FiltersOptional, on any event field
Flexible WebhooksOptional choice of which fields the body includes
Delivery attemptsThree. Unconfirmed webhooks then appear in the Generate Undelivered Webhooks Report API
Timeout and success ruleNot documented
OrderingNot guaranteed
Source IPsNot published

Common events

Bringg's Webhooks Index lists 53 webhooks. These are the ones most integrations start with:

webhook_typeFires when
task_createdA new order is created
task_assigned_to_dpAn order is assigned to a carrier
accepted_by_fleetA carrier accepts an order
driver_assignedAn order is assigned to a driver
task_acceptedA driver accepts an order
driver_on_the_wayA driver is on the way to an order location
way_point_eta_changedAn order's estimated time of arrival changes
way_point_arrivedA driver arrives at a way point
way_point_leftA driver departs from an order way point
task_doneAn order is completed
task_cancelledAn order is cancelled
task_lateAn order isn't completed by the scheduled time
note_createdA note, photo, signature or form is added to an order
run_started, run_endedA driver starts or finishes a route
route_optimization_doneRoute optimisation is completed
customer_createdA new customer is created

Other webhooks cover driver shifts and breaks, driver locations, customer SMS opt-ins and tips, ratings, payments, scans and inventory.

Here is Bringg's note_created example:

{
  "id": 9492388,
  "note": "Ask foreman about 5 additional cases for return.",
  "user_id": 30218,
  "task_id": 18651509,
  "way_point_id": 22503324,
  "type": "TaskNote",
  "created_at": "2020-11-26T13:24:17.741Z",
  "updated_at": "2020-11-26T13:24:17.741Z",
  "external_id": "32286477",
  "webhook_type": "note_created",
  "merchant_id": 178
}

Two cautions about the body. First, some of Bringg's reference examples, including Order Created and Customer Created, don't show webhook_type. Until you've seen a real delivery for each event, give each registration its own URL path or query string so you can route without relying on the field. Second, turning on Flexible Webhook lets you pick the fields a webhook includes, and Bringg warns that changing that selection "may change the structure of the webhook payload".

Setting up Bringg webhooks

  1. Generate a long random token, for example with openssl rand -hex 32.
  2. In Bringg, select your username in the sidebar and go to Settings > Platform Integrations > Webhooks > Add Webhook.
  3. Choose the event from Webhook Name, and enter your HTTPS URL. Use Add URL for more destinations.
  4. For authentication, use Static Headers. Fill in the Headers key-value pairs with your header, for example Authorization with Bearer <token>, or a custom name such as X-Bringg-Token.
  5. Optionally add a filter, or turn on Flexible Webhook and choose fields. Save.
  6. Repeat for each event you need.

In Hookdeck. Create a source with the Bringg type, enter the header name in Header Name and the exact value, including any Bearer prefix, in Header Value, and use the source URL in your registrations. Hookdeck matches the header name case-insensitively and the value exactly.

Use Static Headers, not JWT or OAuth, with Hookdeck. A Bringg JWT carries issued-at and expiry claims, so its value changes over time and can't match a fixed header value. With OAuth 2.0, Bringg fetches a token from your token endpoint, and tokens rotate. Neither works with a fixed comparison.

Securing Bringg webhooks

With Static Headers, authenticating a request means comparing one header with the value you configured, in constant time.

const crypto = require('crypto');
const express = require('express');

function safeEqual(received, expected) {
  const a = Buffer.from(received, 'utf8');
  const b = Buffer.from(expected, 'utf8');
  // timingSafeEqual throws on buffers of different lengths
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

const app = express();

app.post('/webhooks/bringg', express.raw({ type: '*/*' }), (req, res) => {
  const expected = process.env.BRINGG_WEBHOOK_SECRET; // e.g. "Bearer 9f2c..."
  if (!expected) return res.sendStatus(500); // fail closed when unconfigured

  const received = req.get(process.env.BRINGG_WEBHOOK_HEADER || 'Authorization');
  if (typeof received !== 'string' || !safeEqual(received, expected)) {
    return res.sendStatus(401);
  }

  let event;
  try {
    event = JSON.parse(req.body.toString('utf8'));
  } catch {
    return res.sendStatus(400);
  }
  // Route on event.webhook_type (or on the URL path), deduplicate, process async.
  res.sendStatus(200);
});

The same check in Python:

import hmac
import os


def verify_bringg_request(headers) -> bool:
    expected = os.environ.get("BRINGG_WEBHOOK_SECRET")
    if not expected:
        return False  # fail closed
    header_name = os.environ.get("BRINGG_WEBHOOK_HEADER", "Authorization")
    received = headers.get(header_name)  # Starlette and Flask headers are case-insensitive
    if not received:
        return False
    return hmac.compare_digest(received.encode("utf-8"), expected.encode("utf-8"))

Compare the whole value, including Bearer . A matching header shows the sender knows the token. It doesn't show that the body is unchanged, and a captured request can be replayed and still pass. Bringg's JWT option doesn't change that: its signature covers the token's own claims, not the webhook body. Use HTTPS only, keep the token long and random, and rotate it if it may have leaked.

Make Bringg webhooks production-ready. Hookdeck Event Gateway checks the static header on every Bringg request, stores each delivery durably, and lets you replay anything your service failed to process.

Bringg webhook limitations and pain points

Three attempts, then a report

The Problem: A webhook that fails three times is not sent again, and the change it described never reaches your system unless you go looking for it.

Why It Happens: Bringg "tries to send each webhook three times". After the third failure, the webhook goes into the Generate Undelivered Webhooks Report, which you request through the API and receive as a link to a CSV file. Bringg doesn't document the timeout, the interval between attempts, or which responses count as confirmation.

Workarounds:

  • Respond with a 2xx as soon as the header checks out, and process afterwards.
  • Request the undelivered webhooks report on a schedule, and after any incident, and replay what it lists through your own code.

How Hookdeck Can Help: Hookdeck accepts each webhook as soon as it arrives and stores it, then delivers it to your service with retries on a schedule you control. An outage on your side no longer burns Bringg's three attempts, and you can replay anything from Hookdeck instead of working from a CSV.

Static headers, not signatures

The Problem: Anyone who learns the header value can post fake order updates, and nothing detects a changed body.

Why It Happens: None of Bringg's three methods signs the body. Static Headers sends a fixed value, OAuth sends a bearer token, and the JWT signs only its own claims.

Workarounds:

  • Use a long random token over HTTPS, and keep it out of logs.
  • Before acting on a high-impact event, such as cancelling a customer's order, fetch the order from the Bringg API.

How Hookdeck Can Help: Hookdeck's Bringg source type compares the header you name with the exact value you enter. Hookdeck answers Bringg with a 200 either way, then records a request without the right header as failed verification, so it never reaches your destination.

Events can arrive out of order

The Problem: A late way_point_eta_changed overwrites a task_done, or an assignment event arrives after the order was already accepted.

Why It Happens: Bringg notes for at least the Order Assigned to Carrier webhook that "the sequence of the webhooks cannot be guaranteed", and failed webhooks are retried later, after newer events have arrived.

Workarounds:

  • Compare updated_at with what you've stored, and only move an order forward through its states.
  • Treat terminal states such as done and cancelled as final.

How Hookdeck Can Help: Every event is stored in Hookdeck with the time it arrived, so you can trace exactly what Bringg sent and when, and replay a sequence after fixing your state handling.

Payload shapes change with configuration

The Problem: A handler breaks after someone edits a webhook's Flexible Webhook fields in the Bringg UI.

Why It Happens: Flexible Webhooks change which fields are included, and the change applies to the next delivery.

Workarounds:

  • Read fields defensively and ignore unknown ones.
  • Agree that Flexible Webhook changes go through the team that owns the receiver, and test after each change.

How Hookdeck Can Help: Hookdeck keeps every request body, so you can compare deliveries from before and after a change, and transformations can map the new shape back to what your service expects.

Best practices

Use a URL per event while you build

Because webhook_type isn't shown in every example, register each event against its own path, such as /webhooks/bringg/task_done, until you've confirmed what real bodies contain.

Deduplicate deliveries

Retries resend the same event. Build a key from webhook_type, the resource id and updated_at, and skip repeats. See our guide to webhook idempotency.

Acknowledge fast and process asynchronously

Return 2xx straight after the header check and do the work in a background job. See why to process webhooks asynchronously.

Filter at the source

Bringg's per-registration filters can cut traffic before it leaves Bringg, for example to customers who opted in to SMS. Use them, rather than filtering everything in your handler.

Watch the undelivered report

Make the Generate Undelivered Webhooks Report part of your monitoring. It's the only record Bringg gives you of webhooks that never arrived.

Conclusion

Bringg sends a JSON POST for each event you register, and with Static Headers authentication, a fixed header is the only credential. Compare that header in constant time over HTTPS, route on webhook_type or a per-event path, handle events arriving out of order, and keep an eye on the undelivered report, because Bringg stops after three attempts.

Hookdeck Event Gateway sits in front of your receiver, checks the static header, and stores every Bringg event until your service has processed it.

Get started with Hookdeck for free and handle Bringg webhooks reliably in minutes.


Gareth Wilson

Gareth Wilson

Product Marketing

Multi-time founding marketer, Gareth is PMM at Hookdeck and author of the newsletter, Community Inc.