The recipient.* family, covering the entitlement as a whole, its running balance, and the four transitions we notify on.
payout.* events report one payout moving through its lifecycle, meaning a single crypto send or a single gift card. They fire for any payout order with a callback_url. See Subscribing.
For events about the recipient's entitlement as a whole, such as viewed, partially redeemed, and expired, see Recipient events.
Two payout types, two bodies
The payout.* family covers two payout types with two different body shapes. The event name on its own does not tell you which body you are holding.
| Crypto | Gift cards | |
|---|---|---|
payout_type | crypto | gift_cards |
| Body | event, payout_type, data | Flat GIFQ envelope |
| Identifiers | Inside data | Top level |
| Amounts | Decimal strings in data | JSON numbers |
| Events | 6 status, 3 quote | 4 status |
Route on event and payout_type together. payout.processing, payout.failed, and payout.cancelled exist on both sides.
Gift-card payout events
The subject is one gift card, and subject_type is "payout".
A recipient can hold several gift cards, one per brand and denomination they pick, so each body carries enough brand and amount detail to reconcile a specific card without another API call.
Events
| Event | Status | Fires when |
|---|---|---|
payout.processing | processing | The card has been requested from the brand and is not yet issued. |
payout.fulfilled | fulfilled | The card was issued to the recipient. Terminal. |
payout.failed | failed | Issuance failed. Terminal. The recipient's balance goes back to them. |
payout.cancelled | cancelled | The card was cancelled. Terminal. |
Each transition emits exactly one delivery.
Body
The GIFQ envelope, plus these keys.
| Field | Type | Notes |
|---|---|---|
payout_uuid | String | This gift card. Use it as your key for card-level reconciliation. |
brand_uuid | String | The brand the card is drawn on. |
brand_name | String | Readable brand name, such as "Amazon UK". |
amount | Number | Card denomination, in currency. |
currency | String | The currency amount is denominated in. This is not always the recipient's wallet currency. |
Wallet balances, initial_amount and remaining_amount, are not on this body. They live on recipient events, because an FX redemption leaves those balances in the recipient's currency while the card is denominated in the brand's.
Example
{
"event": "payout.fulfilled",
"payout_order_uuid": "11111111-2222-3333-4444-555555555555",
"recipient_email": "[email protected]",
"recipient_uuid": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"recipient_name": "Alice Doe",
"subject_type": "payout",
"status": "fulfilled",
"updated_at": "2026-05-21T10:05:00Z",
"payout_type": "gift_cards",
"currency": "GBP",
"sent_at": "2026-05-20T09:00:00Z",
"viewed_at": "2026-05-21T10:00:00Z",
"expires_at": "2026-08-20T09:00:00Z",
"payout_uuid": "99999999-8888-7777-6666-555555555555",
"brand_uuid": "77777777-6666-5555-4444-333333333333",
"brand_name": "Amazon UK",
"amount": 25.0
}What to do on receipt
- Key your ledger on
payout_uuid. A recipient produces one of these per card, sorecipient_uuidalone will collide. - On
payout.fulfilledthe card exists and the recipient has it. The code, PIN, and serial are never in this body. - On
payout.failedthe amount returns to the recipient's remaining balance. Expect them to redeem again, which produces a newpayout_uuid. - Expect a
recipient.partially_redeemedorrecipient.redeemedalongsidepayout.fulfilled. We write them together, so a crash cannot leave you holding one without the other.
Crypto payout events
The subject is one crypto send.
Crypto bodies have three keys. No GIFQ envelope fields appear. Every detail lives inside data, which is our crypto provider's send request passed through.
Status events
These fire as the send progresses.
| Event | Fires when |
|---|---|
payout.in_progress | The send request was accepted. No funds have moved. |
payout.processing | An FX exchange is in flight, or the chain broadcast is pending. |
payout.completed | The send is finalized. Terminal. |
payout.failed | The send did not go through. Terminal. |
payout.expired | Nobody actioned the send inside its window. Terminal. |
payout.cancelled | The send was cancelled and no funds moved. Terminal. |
A draft send emits nothing, since payout.quote_created already covers that moment.
Quote events
These fire from the recipient's redeem flow as they pick a currency and network.
| Event | Fires when |
|---|---|
payout.quote_created | A quote was issued. This is the earliest event for a given send. |
payout.quote_confirmed | The recipient confirmed a cross-currency quote. Same-currency sends never emit this. |
payout.quote_cancelled | The recipient cancelled an open quote. That send cannot be re-confirmed, and a new quote produces a new send. |
One quote event per send, not per requestIdempotent retries of the same quote call do not re-emit. If a quote expires and the recipient quotes again, that is a new send with its own
payout.quote_created. Treat each as a separate lifecycle.
Crypto bodies
POST <your callback_url>
Content-Type: application/json
X-Gifq-Event: payout.completed
X-Gifq-Signature: sha256=<hex digest>
X-Gifq-Delivery: <uuid>
X-Gifq-Timestamp: 1774000000
{
"event": "payout.completed",
"payout_type": "crypto",
"data": { "…": "provider send request" }
}| Field | Type | Notes |
|---|---|---|
event | String | The mapped GIFQ status. Mirrors X-Gifq-Event. |
payout_type | String | Always crypto on these bodies. |
data | Hash | null | The provider's send request as we received it. null when the emitting path had no payload to attach. |
datacan benull
datacarries a payload when the emitter had one, meaning an inbound provider callback, a pull-sync read, or a quote response. Otherwise it isnull. Guard before reaching into it. Onlyeventandpayout_typeare guaranteed non-null.
data.status holds the provider's raw string rather than GIFQ's. See the mapping callout.
What to do on receipt
- Key on
data.id, the send identifier. One recipient can produce several sends over time if quotes expire or get cancelled. - Treat
payout.completedas the authoritative outcome, and cross-check through the GIFQ API rather than trusting the body alone. - If
dataisnullon a terminal event, fetch the payout from the API to get the detail. - A crypto recipient reaching full redemption also produces
recipient.redeemed.
Multiple recipients, multiple cards
One payout order fans out. For an order of N recipients where each redeems M gift cards, expect roughly N recipient event streams and N × M payout event streams.
Deliveries are capped at 5 per second per account and are delayed rather than dropped above that, so a large expiry sweep arrives spread over time and out of order. Sort on updated_at.