Receive Custodial Webhook
Inbound endpoint a custody provider calls to report the progress of a transaction the platform submitted to it. You only need this page if you operate the custody service the platform is connected to; partner integrations do not call it.
It is authenticated per provider with the provider's webhook secret (generated in the admin panel's custody settings and shown once), not with an OAuth access token. A provider whose webhook is configured to require a client certificate must also present the certificate issued to it in the TLS handshake (see mTLS Client Certificates; the subject is the provider key).
Request
POST /v2/webhooks/custodial
POST /v2/webhooks is a deprecated alias with the same behaviour, kept only for senders still configured with it. Do not configure a new sender with it.
Headers
| Name | Description |
|---|---|
Authorization | Required (or X-Webhook-Token). Depending on the provider's auth scheme: Basic <base64(username:webhook_secret)>, or Bearer <webhook_secret> |
X-Webhook-Token | Alternative to Authorization: Bearer for senders that cannot set Authorization. Carries the webhook secret |
X-Apex-Delivery-Id | Recommended. The sender's id for this delivery. The platform logs the delivery under it, so the same delivery received twice is one log entry |
Content-Type | Required. application/json |
Body
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | The custody transaction id: the id the platform submitted the transaction under |
custody_transaction_id | string | No | The custody service's own row id. Logged only |
external_id | string | No | Optional external reference |
type | string | Yes | Custody operation type, for example execute_contract |
status | string | Yes | pending_approval, queued, signing, broadcast, confirmed, rejected, failed, cancelled or expired |
transaction_hash | string | No | On-chain transaction hash, once broadcast |
contract_address | string | No | Contract the transaction created or called |
gas_used | number | No | Gas used, once the receipt is available |
effective_gas_price_wei | string | No | Decimal string. Logged only |
gas_fee_wei | string | No | Decimal string. Logged only |
gas_fee_native | string | No | Decimal string. Logged only |
error | string | null | No | Failure reason for rejected / failed / cancelled / expired |
created_at | string | Yes | ISO 8601 |
updated_at | string | Yes | ISO 8601 |
Unknown fields are ignored.
Example
curl --request POST \
--url '{{baseUrl}}/v2/webhooks/custodial' \
--header 'Authorization: Bearer <webhook_secret>' \
--header 'X-Apex-Delivery-Id: 3b1f6c1e-8c55-4d47-9a0e-4b1c2f0c9a11' \
--header 'Content-Type: application/json' \
--data '{
"id": "6913872755654191dede37da",
"type": "execute_contract",
"status": "confirmed",
"transaction_hash": "0xc34e006659d532a0c8212a8ec624a08cfab3ffbd102936a681ab81e3cf7e0c6d",
"contract_address": "0x39247A80f1c0ddcEfb4986fA7A59E919E1F2Fa38",
"gas_used": 142037,
"error": null,
"created_at": "2026-02-06T13:04:58.802Z",
"updated_at": "2026-02-06T13:05:09.911Z"
}'
Response
200 with an empty body once the delivery is accepted.
How a delivery is processed
- Final statuses are read back. For
confirmed,rejected,failed,cancelledandexpiredthe platform does not trust the body: it reads the transaction from the custody service and publishes custody's answer (status, hash, contract address, gas, error). The webhook secret proves who sent the delivery, not what happened. - If that read fails or finds nothing, the delivery is refused with
503(code20009) so the sender retries. The platform's own polling still settles the transaction, so a verdict is delayed, never lost. - Intermediate statuses (
pending_approval,queued,signing,broadcast) are passed on as delivered. - A transaction that already reached
doneorerroron the platform keeps its first verdict; a later delivery for it does not change it.
Errors
| Status | Code | Description |
|---|---|---|
| 400 | -1 | The body does not parse or a field has the wrong type |
| 401 | -1 | The credentials match no provider's webhook secret, or the provider requires a certificate and the handshake did not present the one issued to it |
| 503 | 20009 | The final status could not be read back from the custody service. Retry the delivery |
See Errors for the error format.