API Doc - Claim Link Webhook
AllScale Open API Version: v4 Last Updated: 2026-07-15
Overview
When a Claim Link reaches a terminal state and the corresponding on-chain settlement succeeds, AllScale POSTs a notification to your configured webhook endpoint. This lets your backend react to a claim, an expiry/refund, or a sender cancellation/refund without polling.
There are exactly three terminal events:
event
Emitted when
claimed
The recipient claimed the link and the payout transaction was persisted on-chain.
expired
The link expired unclaimed and the refund transaction succeeded on-chain.
cancelled
The sender cancelled (claimed back) a funded, unclaimed link and the refund transaction succeeded on-chain.
β οΈ
cancelledis new. Earlier versions of this document stated that there is nocancelledevent β that is no longer true. Sender cancel / claim-back is live (see the Cancel (Claim-Back) API), and its refund settles through the same path as an expiry refund, so it emits the same payload shape withrefund_tx_hashset. If your handler was written against the old two-event contract, it must now tolerate a thirdeventvalue β do not treat an unknown event as an error.The callback fires after on-chain settlement, not on the status change alone. A link whose status becomes expired or cancelled does not emit its event until the refund succeeds β if a refund is still pending or has failed, no callback is sent (yet). Do not treat "status = cancelled" as equivalent to "a
cancelledwebhook will arrive"; reconcile via the Claim Link status API for the authoritative state.
The claim webhook reuses the same HMAC-SHA256 request-signing scheme as every other AllScale webhook β see Webhook Callback Signing & Payload Guide for the canonical-string construction, header definitions, and verification flow. This document covers only what is specific to the Claim Link webhook: which links emit it, the payload shape, and the delivery/retry behavior.
Which links emit a webhook
A callback is sent only when a webhook URL is configured for the link:
Batch-created links
webhook_url on the batch
batch_id
claimed, expired
Merchant single-create (auto-payout) links
claim_link_webhook_url on your store
store_id
claimed, expired, cancelled
In-app single links
β (never emit)
β
β
Exactly one of batch_id / store_id is populated in each payload, identifying how the link was created. The store-level claim_link_webhook_url must be an https:// URL (plain http:// is accepted only in the sandbox environment).
cancelledonly ever carriesstore_id. A batch sub-link cannot be cancelled individually (its funds are pooled and returned through the batch's aggregate refund flow), so acancelledevent is never emitted for a batch link βbatch_idis alwaysnullon acancelledpayload.
The callback is signed with the same store credentials (api_key + api_secret) that authenticated the create request β i.e. the store that owns the batch or the single-create link.
Webhook Request
Method
Content-Type
Required Headers
Identical to all AllScale webhooks (see the signing guide):
X-API-Key
Your store's API key
X-Webhook-Id
Unique webhook ID (dedupe key)
X-Webhook-Timestamp
Unix timestamp (seconds)
X-Webhook-Nonce
Unique per-request nonce
X-Webhook-Signature
v1=<base64 HMAC-SHA256>
The signature is computed over the canonical string allscale:webhook:v1\nPOST\n<path>\n<query>\n<webhook_id>\n<timestamp>\n<nonce>\n<sha256_hex(body)>, signed with your api_secret. Always verify the signature against the raw request-body bytes before parsing the JSON.
Webhook Payload
The request body is exactly the following JSON structure. All money values are decimal strings in token units (never floats).
JSON Field Definitions
webhook_id
string
β
Unique webhook ID; must match X-Webhook-Id. Use it for idempotency (see below).
event
string
β
"claimed", "expired", or "cancelled". Tolerate unknown values β new terminal events may be added.
claim_link_id
string
β
The Claim Link this event is about.
occurred_at
string
β
Time AllScale generated this callback, ISO-8601 UTC with Z suffix (e.g. "2026-07-14T03:21:07Z"). This is the enqueue time, not the exact on-chain event time β on a retry or catch-up re-delivery it can be later than the actual claim/refund. For the precise on-chain moment, use the transaction hash.
amount
string
β
Claim/refund amount as a decimal string in token units (e.g. "12.50").
token_symbol
string
β
Stablecoin symbol, e.g. "USDT" / "USDC".
batch_id
string | null
β
Set for batch-created links; null otherwise. Exactly one of batch_id / store_id is non-null.
store_id
string | null
β
Set for merchant single-create (auto-payout) links; null otherwise.
reference_id
string | null
β
Your per-link reference id, if one was supplied at create time; null otherwise.
claim_tx_hash
string | null
β
On-chain payout tx hash. Set when event = "claimed"; null for expired / cancelled.
refund_tx_hash
string | null
β
On-chain refund tx hash. Set when event = "expired" or "cancelled" (both settle via a refund); null for claimed.
Null keys are always present. Fields that do not apply to a given event/link type are serialized as
null(not omitted). Your parser should toleratenullfor every optional field above. The HMAC covers the exact bytes sent, so do not re-serialize before verifying.
Example β claimed (merchant single-create link)
Example β expired (batch-created link)
Example β cancelled (merchant single-create link)
Emitted after you called the Cancel (Claim-Back) API and the refund settled on-chain. Note batch_id is always null here, and refund_tx_hash is set exactly as for expired.
Idempotency & Dedupe
webhook_id is stable and deterministic for a given (claim_link_id, event) pair β every retry and every at-least-once re-delivery of the same event carries the same webhook_id.
Treat
webhook_idas the dedupe key: record processed IDs and ignore repeats.Because delivery is at-least-once, you will occasionally receive the same event more than once (e.g. after a retry or a catch-up sweep). Idempotent handling is required.
Delivery & Retry Behavior
At-least-once, best-effort. The first delivery attempt is synchronous; on any non-
200response (or timeout / connection error) AllScale schedules up to 35 exponential-backoff retries.Retry window. Backoff grows (~5s, 10s, 20s, 40s, 80s) then caps at 120s per attempt, giving a real delivery window of roughly 30 minutes (with jitter) up to ~1 hour β enough to survive a brief outage or deploy on your side.
No replay after exhaustion. Once the retries are exhausted the job is dead-lettered and not re-delivered by any drain/replay mechanism. If your endpoint is down for longer than the retry window, you will miss that callback β reconcile via the Claim Link status API rather than relying solely on the webhook.
Missed-
expiredcatch-up. For batch links, AllScale runs a catch-up sweep that re-enqueuesexpiredwebhooks that were missed within the last 7 days. This is an additional at-least-once path (hence the dedupe requirement above); it does not cover theclaimedorcancelledevents, nor store single-create links. Becausecancelledis store single-create only, it has no catch-up path β if your endpoint is down past the retry window you will miss it, so reconcile rather than relying on the callback alone.Request timeout. Each attempt uses a 5-second timeout; respond quickly (return
200and process asynchronously if needed).
Response Expectations
Your endpoint should return:
200 OKβ the callback is considered delivered; no retry.Any non-
200β treated as a failure; the callback is retried per the policy above.
Best Practices
β
Verify X-Webhook-Signature against the raw body bytes before processing
β
Dedupe on webhook_id β you will receive duplicates
β
Branch on event (claimed / expired / cancelled) and read the matching *_tx_hash (claim_tx_hash for claimed, refund_tx_hash for expired and cancelled)
β
Treat an unrecognized event as a no-op you ack with 200, not as an error β new terminal events may be added (as cancelled was)
β
Parse amount with a Decimal type, not a float
β
Return 200 fast; do heavy work asynchronously
β
Reconcile against the Claim Link status API for anything longer than the ~1h retry window
β Never log api_secret or signatures
End of Document
Last updated