Skip to main content

Overview

Webhooks push order-lifecycle events to your systems as they happen, so you can react to a provider accepting, scheduling, or completing an order without polling GET /reseller/orders. Register an HTTPS endpoint, subscribe it to the event types you care about, and Offergrid POSTs a signed JSON envelope to it every time one of those events occurs on an order your team placed.
Every delivery attempt — success or failure — is recorded and queryable via GET /reseller/webhooks/{id}/deliveries. That log is the first place to look when something appears to be missing.

Registering a webhook

The response contains the signing secret, and it is shown only once — store it before you discard the response.
The url must be https:// — plain HTTP is rejected at validation.
To pause deliveries without losing the webhook or its secret, PATCH it with {"isActive": false} rather than deleting it.

Event types

You only receive events for orders where your team is the reseller. Providers subscribe to the same four event types and receive them scoped to their offers instead — same event, different audience.If your team is hybrid, one webhook covers both roles and you receive each event exactly once. Compare providerTeamId and resellerTeamId in the payload against your own team id to tell which side of an order you are on.

Payload structure

Every delivery is a POST with Content-Type: application/json and this envelope:

order.item.status_changed

from and to are the item’s statuses; orderStatus is the parent order’s status recomputed from all of its items — so you can update an order-level view without a follow-up read. See Order Lifecycle for the full status set.

Other event payloads

order.created, order.item.created, and order.cancelled carry the same payloads documented on the provider side: order.created · order.item.created · order.cancelled.

Verifying signatures

Signed deliveries carry a single header:
There is no separate timestamp header — the timestamp is the t= component and is also part of the signed payload, so a verifier can reject stale or replayed deliveries. The signature is HMAC-SHA256 over the string `${t}.${rawBody}`, keyed with your webhook secret, hex-encoded.
Sign over the raw request body, not a re-serialized copy. JSON.parse followed by JSON.stringify can reorder keys or change number formatting, and the signature will not match.
The scheme is identical for both roles — the worked Node and Python verifiers in Verifying signatures apply unchanged.

Delivery behaviour

Return 2xx as soon as you have durably accepted the event, then do the work in a background job:
Deduplicate on the envelope id, which is stable across retries:

Testing your endpoint

Point a webhook at a tunnel while you develop:
Register the tunnel URL, then place a test order and have the provider move it. That is the only way to exercise signature verification end to end — a hand-rolled cURL request carries no valid signature, so a correct verifier will reject it. To see what Offergrid actually sent and what your endpoint returned:

Still want to poll?

Webhooks do not replace reconciliation. Because delivery is at-least-once and unordered, a periodic sweep of GET /reseller/orders is a good safety net — daily is plenty once webhooks are live. See Tracking Orders.

Troubleshooting

  1. Confirm the webhook is isActive: true.
  2. Confirm it subscribes to the event type you expect — a webhook only receives types listed in its events array.
  3. Check the delivery log. Attempts with a non-2xx status mean the problem is on your side; no attempts at all means no matching event was emitted for your team.
  4. Confirm your team is the reseller on the order. You do not receive events for orders another reseller placed, even for the same offer.
  1. Sign the raw body, not a re-serialized copy.
  2. Read the timestamp from the t= component of X-Offergrid-Signature. There is no x-offergrid-timestamp header.
  3. The signed string is the timestamp, a literal dot, then the raw body.
  4. Compare hex-decoded buffers, checking lengths first: Node’s timingSafeEqual throws on a length mismatch.
  5. Confirm you stored the secret from the creation response — it is shown only once and cannot be retrieved later.
Expected. Delivery is at-least-once, and a retry re-sends to every subscribed target. Deduplicate on the envelope id.

Next steps

Tracking Orders

Monitoring order status in the dashboard

Order Lifecycle

Every status an order and its items can be in

Reseller API Reference

Every reseller endpoint, including webhook management

API Integration

Broader integration patterns