Webhooks push purchase, buyback, deposit and redemption events to your backend so you don't
have to poll. Manage them with the register, list, delete and delivery log endpoints
(scope webhooks:manage).
POST /api/v1/webhooks with { url, event_types? } — omit event_types to subscribe to everything. Up to 20 webhooks per
partner. Private or internal URLs are rejected.
The response includes the HMAC signing_secret — a 64-hex string that is shown exactly once, at creation. Store it immediately; GET /webhooks never returns secrets.
Each event is delivered as a POST to your URL with these headers:
X-Mystery-Event: purchase.fulfilled X-Mystery-Delivery: <event_id> # idempotency key — dedupe on this X-Mystery-Timestamp: <unix_ms> X-Mystery-Signature: t=<unix_ms>,sha256=<hex>
And this body shape:
{ "event": "purchase.fulfilled", "id": "purchase.fulfilled:5012",
"data": { "purchase_id": 5012, "status": "FULFILLED", "onchain_request_id": "0xabc…" } } HMAC_SHA256(signing_secret, "<timestamp>.<raw_body>") using the raw request body.sha256= value in X-Mystery-Signature.X-Mystery-Delivery — retries re-send the same id.Any non-2xx response (or timeout) is retried up to 6 attempts with exponential backoff. Use the delivery log to debug a consumer that is not receiving events.
| Event | Fires when |
|---|---|
deposit.credited | A USDC deposit landed and was credited to an end-user balance |
purchase.reserved | Custodial purchase accepted — credits held |
purchase.submitted | On-chain purchase broadcast |
purchase.fulfilled | The random reveal finished — purchase complete |
purchase.refunded | Purchase refunded (e.g. machine empty) — hold returned |
purchase.failed | Pre-broadcast failure — hold returned |
buyback.confirmed | The holder accepted your buyback offer on-chain |
buyback.transfer_held | Bought-back NFT is waiting because no pool wallet is set (sent once) |
buyback.card_transferred | Bought-back NFT delivered to your pool wallet |
buyback.transfer_failed | Terminal transfer failure after max retry attempts |
redemption.prepared | Redemption prepared — the transaction to send is ready (emitted inline by prepare) |
redemption.updated | Every redemption status transition thereafter (burn, fulfillment, completion, failure) |
pool.item_pulled | An item left your pool wallet (a billable pull per the partnership agreement) — includes token, recipient, tx hash and the billed amount |
The buyback-transfer and redemption payloads carry everything you need to reconcile state.
For example, buyback.card_transferred includes the token_id, contract_address, to_address and tx_hash of the card delivery, and buyback.transfer_held tells you to
set a pool wallet via PUT /mystery/pool-wallet.