Skip to main content

Webhooks

GIFQ webhooks for crypto payouts — the five events, per-order configuration, HMAC-SHA256 signature verification, and retry behavior.

GIFQ can send webhooks so your systems learn about payout progress without polling. This reference covers the events, how to configure them, how to verify signatures, and how delivery and retries behave.

Scope: webhooks currently fire for crypto payouts only. There is no webhook for gift-card order completion today — to track gift-card orders, poll GET /api/orders/:id until it reaches a terminal state.

Configuring webhooks

Webhooks are configured per payout order, not globally and not in a dashboard. Pass a callback_url (HTTPS, validated) when you call POST /api/payout-orders. Omit it and that order sends no webhooks.

Events

  • payout.status_changed — a recipient transitions into a non-terminal status (e.g. in_progress, processing).

  • payout.terminal_state — a recipient transitions into a terminal status (completed, failed, expired, canceled). This replaces status_changed for that transition — the two are mutually exclusive.

  • payout.quote_created — the recipient creates a crypto quote.

  • payout.quote_confirmed — the recipient confirms a quote (fires only on a real cross-currency confirmation).

  • payout.quote_canceled — the recipient cancels a quote.

One event equals one POST per recipient transition. A payout order with N recipients produces up to N events as each recipient progresses.

Payload

Each delivery is a POST to your callback_url with these headers and a JSON body:

  • X-Gifq-Event — the event name (e.g. payout.terminal_state).

  • X-Gifq-Signaturesha256=<hex> signature (see below).

  • X-Gifq-Delivery — a fresh UUID per delivery attempt.

  • X-Gifq-Timestamp — unix epoch seconds when the delivery attempt was sent.

Example terminal-state body:

{  "event": "payout.terminal_state",  "payout_order_uuid": "...",  "recipient_email": "[email protected]",  "external_send_id": "cg-send-42",  "status": "completed",  "updated_at": "2026-05-14T15:01:23Z",  "payout_type": "crypto",  "tx_hash": "0xabc123...",  "data": { }}

Verifying the signature

The signature is an HMAC-SHA256 over the raw request body, keyed with your company webhook secret, and should be compared in constant time. The secret is per-company, shared with you out-of-band during onboarding, and is not exposed via the API. Always verify the signature before trusting a payload.

Delivery, retries, and dedupe

  • Retries: up to 8 attempts with increasing backoff. GIFQ retries on transport errors and on merchant 5xx responses.

  • Permanent failure: a merchant 4xx is treated as non-retryable and discarded immediately (it will never succeed on retry).

  • Timeouts: 5s to open the connection, 10s to read.

  • At-least-once delivery: a sweeper re-enqueues any delivery that was lost in transit, so the same event can arrive more than once. Deduplicate on X-Gifq-Delivery (and/or the payout identifier + status) on your side.

  • Respond 2xx quickly to acknowledge; do heavy processing asynchronously.

Endpoint requirements

  • Must be HTTPS and publicly reachable.

  • Should return 2xx on success; return 5xx if you want GIFQ to retry.

  • Should verify the HMAC signature on every request.

Did this answer your question?