Overview#
This document describes all outgoing webhook event payloads sent by the platform. Webhooks are HTTP POST requests with a Content-Type: application/json header delivered to your configured endpoint URL.
Payload Conventions#
| Convention | Detail |
|---|
| Format | JSON |
| HTTP Method | POST |
| Content-Type | application/json |
| Null fields | Included as null (not omitted) |
| Timestamps | ISO 8601 — YYYY-MM-DDTHH:mm:ss.fffffffZ |
| Amounts | Decimal number, in the wallet's configured currency |
| Field casing | Envelope (id/type/createdAt) is camelCase; data fields are snake_case |
Signature Verification#
Two different HMAC schemes exist in this integration and they are not interchangeable. Use the one that matches the direction of the call.Outbound — webhooks we deliver to you#
| Header | Description |
|---|
X-Webhook-Id | wh_{subscriptionId:N} — identifies the subscription that received this delivery |
X-Webhook-Signature | Lowercase hex HMAC-SHA256 of {timestamp}.{payload}, signed with your webhook's secret |
X-Webhook-Timestamp | Unix epoch seconds at send time — same value used in the signature |
X-Event-Type | The event type, mirrored from the payload body |
Artha signs each callback with the same construction as above:signature = lowercase_hex( HMAC_SHA256( webhookSecret, timestamp + "." + rawBody ) )
X-Webhook-Signature: <signature>
X-Webhook-Timestamp: <unix epoch seconds>
Event Categories#
| Category | Events |
|---|
| Wallet Creation | wallets.wallet.created |
| Deposit | wallets.deposit.completed, wallets.depositsweep.completed, wallets.depositsweep.failed |
| Withdrawal | wallets.withdraw.completed, wallets.withdraw.failed |
| Gas-Fill | wallets.gas.completed, wallets.gas.failed |
| Callback Received | wallets.callback.received |
1. Wallet Creation Events#
Applicable event types: wallets.wallet.createdFired once a wallet finishes provisioning and its on-chain address is confirmed. One on-chain address can back multiple currencies on the same network (e.g. USDT and USDC both on ERC-20 share one address) — one event fires per currency riding on that address.Payload#
{
"id": "evt_53260e2994bd4d72901036cf21333441",
"type": "wallets.wallet.created",
"createdAt": "2026-08-05T14:26:37.8721433Z",
"data": {
"event": "wallets.wallet.created",
"customer_id": "f51b8db9-0bbf-4a91-b5fe-bf9d7b16f070",
"currency": "USDT",
"network": "TRC-20",
"address": "TXn9Y2q1a3F...Kp8"
}
}
Field Reference#
| Field | Type | Description |
|---|
event | string | Event type (wallets.wallet.created) |
customer_id | string | Customer who owns the new wallet |
currency | string | Currency of the new wallet (e.g. USDT) |
network | string | Blockchain network the wallet was created on (e.g. TRC-20) |
address | string | The on-chain deposit address provisioned for this wallet |
2. Deposit Completed / Deposit Sweep Completed / Deposit Sweep Failed#
Applicable event types: wallets.deposit.completed · wallets.depositsweep.completed · wallets.depositsweep.failedFired when a customer's on-chain crypto deposit is confirmed and credited to their wallet, when those funds are subsequently swept to the company/collection vault, and if that sweep is rejected on-chain. All three events share the exact same payload shape (WalletsTransactionEventData).Payload#
{
"id": "evt_cdb5d44b89da4e2e84d0e981b3af5b18",
"type": "wallets.deposit.completed",
"createdAt": "2026-08-05T13:27:35.2392299Z",
"data": {
"transaction_id": "41ac2f77-e2ad-4fac-b4c6-0d54a8bc3e2f",
"customer_id": "f51b8db9-0bbf-4a91-b5fe-bf9d7b16f070",
"total_amount": 50.0,
"currency": "USDT",
"authorized_at": "2026-08-05T13:27:34.0000000Z",
"fee": 0.0,
"fee_currency": "USDT",
"net_amount": 50.0,
"received_amount": 50.0,
"tx_hash": "0xabc123...",
"network": "TRC-20",
"source_wallet_address": "TExternalSender...",
"destination_wallet_address": "TXn9Y2q1a3F...Kp8",
"type": "Deposit",
"sub_type": "Crypto",
"status": "Approved",
"description": null
}
}
Field Reference#
| Field | Type | Description |
|---|
transaction_id | string | Wallets transaction identifier |
customer_id | string | Customer who owns the wallet |
total_amount | number | Total deposited amount (original deposit, unchanged on sweep events) |
currency | string | Currency of the deposit |
authorized_at | string | ISO 8601 timestamp the transaction was recorded |
fee | number | Fee deducted from the deposit |
fee_currency | string | Deposit event: currency the fee was charged in (TransactionEntity.TxWallet). Sweep events: the sweep wallet's currency code (CryptoSweeping.WalletCode) |
net_amount | number | Amount credited to the customer after fees (original deposit, unchanged on sweep events) |
received_amount | number | Deposit event: amount received on-chain. Sweep events: amount actually swept (CryptoSweeping.Amount) — can differ, since native-coin sweeps deduct the gas reserve left on the deposit address |
tx_hash | string | Deposit event: on-chain hash of the deposit transfer. Sweep events: on-chain hash of the sweep transfer itself |
network | string | Blockchain network |
source_wallet_address | string | Deposit event: external sender's address. Sweep events: customer deposit address funds were swept from |
destination_wallet_address | string | Deposit event: customer's on-chain deposit address. Sweep events: company/collection vault address funds were swept to |
type | string | Transaction type (Deposit) |
sub_type | string | Transaction sub-type |
status | string | Transaction status — Approved on wallets.deposit.completed; the sweep's own state on the depositsweep events (Rejected if the sweep failed) |
description | string | Failure reason — only populated on wallets.depositsweep.failed, otherwise null |
There are no sweep_*-prefixed fields and no contract_address field — sweep-specific data reuses the same source_wallet_address/destination_wallet_address/tx_hash/received_amount fields listed above.
3. Withdraw Completed / Withdraw Failed#
Applicable event types: wallets.withdraw.completed · wallets.withdraw.failedFired when a crypto withdrawal is confirmed on-chain (completed) or fails/is rejected (failed). Uses the same payload shape as deposit events (WalletsTransactionEventData), and no CryptoSweepingEntity is involved for either.Payload#
{
"id": "evt_9b2e0dc47f10469bb55ec089a2ff6ab1",
"type": "wallets.withdraw.completed",
"createdAt": "2026-08-05T09:12:00.0000000Z",
"data": {
"transaction_id": "207d5a05-fc89-4782-93f4-25292a6a0f8e",
"customer_id": "f51b8db9-0bbf-4a91-b5fe-bf9d7b16f070",
"total_amount": 22.0,
"currency": "USDT",
"authorized_at": "2026-08-05T09:10:00.0000000Z",
"fee": 11.0,
"fee_currency": "USDT",
"net_amount": 11.0,
"received_amount": 11.0,
"tx_hash": "0xdef456...",
"network": "TRC-20",
"source_wallet_address": "TAM9XFmGTLWgGNgZw3eXwaXmLqEExLtzFd",
"destination_wallet_address": "TNXtjoJVXzABPqawSmmX27S2iCsL3RbAhG",
"type": "Withdraw",
"sub_type": "Crypto",
"status": "Approved",
"description": null
}
}
A failed withdrawal carries the reason in description:{
"id": "evt_c3d4e5f64789a1b2c3d4e5f6a1b2f6a1",
"type": "wallets.withdraw.failed",
"createdAt": "2026-08-10T16:34:31.4590000Z",
"data": {
"transaction_id": "207d5a05-fc89-4782-93f4-25292a6a0f8e",
"customer_id": "f51b8db9-0bbf-4a91-b5fe-bf9d7b16f070",
"network": "TRC-20",
"source_wallet_address": "TAM9XFmGTLWgGNgZw3eXwaXmLqEExLtzFd",
"destination_wallet_address": "TNXtjoJVXzABPqawSmmX27S2iCsL3RbAhG",
"type": "Withdraw",
"sub_type": "Crypto",
"status": "Failed",
"description": "Balance is not sufficient to process this transaction. Please add enough balance and try again."
}
}
Field Reference#
Same fields as Deposit Events above, with type = Withdraw instead of Deposit. Address semantics are reversed from deposits: source_wallet_address is the address the withdrawal was sent from (resolved from the custodian callback's on-chain sender — e.g. owner_address on Tron), and destination_wallet_address is the external payee address (requestBody.to). status is Approved on success, Failed on failure. On failed events, description always carries a concrete, partner-facing reason — never a generic or blank message; on success it's always null (AdminRemarks is never set on the successful path).
4. Gas-Fill Completed / Gas-Fill Failed#
Applicable event types: wallets.gas.completed · wallets.gas.failedFired when a native-coin gas-funding transfer (funding an address with native coin so a pending withdrawal or deposit-sweep can pay its own on-chain fees) completes or fails.Payload#
{
"id": "evt_a1b2c3d4e5f64789a1b2c3d4e5f6a1b2",
"type": "wallets.gas.completed",
"createdAt": "2026-08-05T09:05:00.0000000Z",
"data": {
"transaction_id": "207d5a05-fc89-4782-93f4-25292a6a0f8e",
"customer_id": "f51b8db9-0bbf-4a91-b5fe-bf9d7b16f070",
"network": "TRC-20",
"currency": "TRX",
"source_wallet_address": "TAM9XFmGTLWgGNgZw3eXwaXmLqEExLtzFd",
"destination_wallet_address": "TNXtjoJVXzABPqawSmmX27S2iCsL3RbAhG",
"tx_hash": "0xghi789...",
"amount": 5.0,
"status": "Approved",
"reason": null
}
}
Field Reference#
| Field | Type | Description |
|---|
transaction_id | string | The withdrawal/sweep transaction this gas-fill supports |
customer_id | string | Customer whose transaction needed gas funding |
network | string | Network the native coin was sent on |
currency | string | Native coin used for gas (e.g. TRX, ETH) |
source_wallet_address | string | Address the gas was sent from (typically the company vault) |
destination_wallet_address | string | Address the gas was sent to |
tx_hash | string | On-chain transaction hash of the gas transfer |
amount | number | Amount of native coin sent |
status | string | Approved on wallets.gas.completed. On wallets.gas.failed, the status of the related withdrawal/sweep operation (Rejected). |
gas_fill_status | string | Not sent on wallets.gas.completed — the event type already conveys that the gas fill completed. Sent only on wallets.gas.failed, where it reflects whatever state it held before the failure was reported |
reason | string | Human-readable failure reason (e.g. insufficient gas balance) — only populated on wallets.gas.failed, otherwise null |
There is no contract_address field on gas-fill events.
Error Handling#
Your endpoint should return an HTTP 2xx status code to acknowledge receipt. If a non-2xx response is returned or the request times out, delivery is retried with exponential backoff (1 min, 5 min, 30 min, 2 hr, 24 hr — up to 5 attempts). A subscription is automatically deactivated after 50 consecutive delivery failures.| Scenario | Recommendation |
|---|
| Duplicate events | Use transaction_id for idempotency on Deposit/Withdraw/Sweep/Gas-Fill events, or customer_id + currency + network + address for Wallet Creation events — redelivered custodian callbacks are already deduplicated server-side, but treat delivery as at-least-once on your end too |
| Unknown event type | Acknowledge (200 OK) and ignore gracefully — new event types may be added over time |
| Processing failure | Return 500 to trigger retry |
Delivery history (including per-attempt status, response, and error) is available via GET /api/v1/webhooks/{webhookId}/logs.Modified at 2026-08-24 10:33:16