Skip to main content
Webhooks are how DropHub tells your system that a shipment moved, without you polling for it. Managing them requires the webhooks:manage scope.
A merchant has one webhook. There is no collection of endpoints — the resource is /v2/external/webhook, singular, and creating a second one replaces nothing because there is nowhere to put it.

Register it

Omit events to subscribe to all seven. To narrow it, name the ones you act on:
The 201 response carries the endpoint and, exactly once, the signing secret:
signingSecret is disclosed on creation and on rotation, and never again. GET /v2/external/webhook returns the endpoint without it. Store it immediately; the recovery path is rotation, not retrieval.

Events

These are the seven public statuses as they happen. DropHub’s internal dispatch steps do not appear here.

The delivery

data.reasonCode is present on shipment.failed and shipment.cancelled. sequence is the shipment’s version and orders events for that shipment — ignore one you have already applied.

Verifying the signature

Every delivery carries three headers: The signed message is those two values and the raw request body, joined with literal . characters:
Two details break most first attempts:
  1. The HMAC key is the decoded secret. signingSecret is 32 random bytes shown as base64url — base64url-decode it before using it as a key, do not pass the 43-character string.
  2. The signature is base64url, not hex.
Verify against the raw bytes you received, before any JSON parsing or re-serialisation. Re-encoding the body changes it, and the signature will no longer match.
Compare in constant time, as above. Also reject deliveries whose Webhook-Timestamp is far from your current clock — a valid signature on a very old request is a replay.

Responding

Return a 2xx quickly, then do the work. Acknowledge receipt and process asynchronously. Because retries exist, your handler must be idempotent: deduplicate on Webhook-Id, and ignore an event whose sequence you have already applied for that shipment.

Retries

A non-2xx, a timeout, or a transport failure is a failed attempt. DropHub retries up to 8 attempts, starting at 30 seconds and doubling each time, capped at 1 hour between attempts. A Retry-After on your 429 or 503 is honoured, up to that same 1-hour cap.
There is no delivery history, replay, or test-send API. If you need to re-drive state after an outage, read the shipments you care about with GET /v2/external/shipments/{id} — the current status is always authoritative.

Changing it

PATCH, DELETE, and rotation are conditional requests: they require the endpoint’s current version as an If-Match ETag. Read it from GET /v2/external/webhook, or from the ETag header of the response that last changed it. Pause while you deploy, rather than deleting and recreating — deletion loses the secret:
PATCH accepts any of url, status (ACTIVE or PAUSED), and events.
Omitting If-Match is rejected with 428; a stale one with 412. See Idempotency and ETags.

Rotating the secret

Rotation returns a new signingSecret and sets previousSecretValidUntil on the endpoint — 24 hours ahead. Until that instant both secrets produce valid signatures.
Deploy the new secret alongside the old one and accept either during the overlap, then drop the old one once previousSecretValidUntil has passed. Rotating and cutting over in one step will drop deliveries that were already in flight.