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
events to subscribe to all seven. To narrow it, name the ones you act on:
201 response carries the endpoint and, exactly once, the signing secret:
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:
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 a2xx 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.
If-Match is rejected with 428; a stale one with 412. See Idempotency and ETags.
Rotating the secret
signingSecret and sets previousSecretValidUntil on the endpoint — 24 hours ahead. Until that instant both secrets produce valid signatures.
