Webhook Signature Verification & Idempotency
This page follows Agent Java SDK WEBHOOKS.md at commit fa6c7b6. This is a documentation-only update; no new SDK artifact containing warning helper methods was released.
All four event types use one merchant callback URL and are dispatched by the body event. See Webhook Events & Liquidation Warnings for event fields and payload examples.
Webhook headers
Event idempotency
Persist deduplication within the merchant and environment scope, atomically with business processing. Different notificationId values identify independent FIRST / UPGRADE / REMINDER notifications; do not discard notifications by grouping on user, episode, or eventType. An empty idempotency key cannot be used for deduplication. The SDK only returns a helper key; it does not persist or process business data. Record the same identity with a different body as a conflict; do not overwrite the processed body.
Signature verification and receiving
Use the merchant API secret to calculate the HMAC-SHA256 hexadecimal signature of timestamp + nonce + rawBody. Verify the original received bytes; do not format JSON, rename fields, or re-serialize the body first.
Verify the signature first, then check the time window (recommended ±300 seconds), merchant, and environment. Next extract the idempotency identity and handle the notification reliably. Preserve unknown event values for diagnosis; never interpret them as funds success. The verification helper does not perform time-window, ownership, or persistent deduplication checks.
Return HTTP 2xx after completing processing or confirming an identical duplicate; return non-2xx on failure. The warning sender has a 5-second HTTP timeout and at most 8 attempts. Retries retain the frozen body; only the signature timestamp and nonce change. Respond promptly after reliable acceptance; long-running business work can use the merchant’s own persistent tasks.
Merchant notifications do not depend on the user’s online status. Platform user pushes are online best-effort delivery only, with no offline replay; phase one has no notification inbox.
Successful responses and stopping retries
Funds callbacks and liquidation warnings use only the HTTP status code to determine delivery success. The sender does not interpret response-body code, success, result, or message, and does not require a fixed OK / success response.
The response body may be empty, text, or JSON. The example result fields are informational for the merchant, not protocol requirements. 202 Accepted also stops platform retries, so return it only after reliably saving the notification to the merchant’s persistent task queue, not merely starting an in-memory async task.
Successful response examples
After business processing completes, or the notification has been reliably saved to the merchant’s own pending queue:
When no response body is needed:
For an already-processed notification with an identical body, skip duplicate business processing and still return 2xx:
Failure response examples
If the database is temporarily unavailable and the merchant has neither completed processing nor reliably saved the notification, return non-2xx so the platform can retry:
Reject an invalid signature without updating business data or successful deduplication records:
These response bodies do not change retry decisions. Do not expect a 401 or 409 to permanently stop retries; investigate the signature, notification content, or merchant configuration.
Incorrect example: the following response still counts as successful delivery. The platform will not retry because of success:false.
If the framework or global exception handler wraps business failures in HTTP 200, explicitly set the correct failure status at the callback endpoint.
Reliable acknowledgement and duplicate delivery
Recommended order: verify the signature and check time / merchant / environment → extract the idempotency identity → process business data and successful deduplication records in a transaction, or reliably save a pending task → return 200/204 after the commit succeeds. Do not return 2xx or mark the notification processed before a successful database write.
Duplicate delivery can still occur after merchant processing succeeds: the response may be lost, or the platform may receive 2xx but fail to persist SUCCESS. HTTP callbacks do not guarantee one-time delivery. Persist deduplication within the merchant and environment scope. Funds use orderType:orderId:targetStatus; warnings use liquidation.warning:notificationId. For an identical already-processed body, return 2xx without crediting funds or issuing a reminder again. For the same identity with a different body, record a conflict and return non-2xx; do not overwrite the original record.
Acknowledgement success means the merchant received / processed the notification, not that the funds operation succeeded. After recording a valid funds notification with targetStatus=FAILED, also return 2xx; an order failure is not a reason to reject notification acknowledgement.
Funding and environment boundaries
The original Contract transfer webhook and queryOrder flow do not automatically apply to Funding. Confirm Funding operations using queryWalletTransfer and the original order environment. Funding uses status; original Contract transfer responses use orderStatus. A successful HTTP response or code=0 does not by itself prove funds moved.
Explicit simulated Contract transfer notifications include environment=SIMULATED. Verify the raw body first, then check the user, amount, order identity and environment against the saved local business order.