For the complete documentation index, see llms.txt. This page is also available as Markdown.

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.

⚠️ cancelled is new. Earlier versions of this document stated that there is no cancelled event β€” 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 with refund_tx_hash set. If your handler was written against the old two-event contract, it must now tolerate a third event value β€” 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 cancelled webhook 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.


A callback is sent only when a webhook URL is configured for the link:

Link type
Webhook URL source
Payload identifier
Events

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).

cancelled only ever carries store_id. A batch sub-link cannot be cancelled individually (its funds are pooled and returned through the batch's aggregate refund flow), so a cancelled event is never emitted for a batch link β€” batch_id is always null on a cancelled payload.

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):

Header
Description

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

Field
Type
Required
Description

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 tolerate null for every optional field above. The HMAC covers the exact bytes sent, so do not re-serialize before verifying.

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_id as 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-200 response (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-expired catch-up. For batch links, AllScale runs a catch-up sweep that re-enqueues expired webhooks 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 the claimed or cancelled events, nor store single-create links. Because cancelled is 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 200 and 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