Skip to main content

Events

The gateway emits six event types. Each is opt-in — your webhook receives only events you've subscribed to in the dashboard. There is no per-event API-key permission; subscription is managed in your dashboard's Webhooks page.

EventFires when
deposit.detectedA pending deposit is visible on-chain but not yet confirmed.
deposit.confirmedRequired confirmations reached; funds credited to your internal balance.
deposit.sweptGateway moved the deposit from the per-deposit address into your receiving wallet.
withdrawal.broadcastedGateway signed and broadcast your withdrawal transaction.
withdrawal.confirmedWithdrawal reached required confirmations; balance debited.
withdrawal.failedWithdrawal did not reach the network or was rejected.

Envelope

Every event is wrapped in the same envelope:

{
"id": 42,
"event": "deposit.confirmed",
"created_at": "2026-05-16T22:00:00.481523Z",
"api_version": "1",
"data": { ... event-specific, see below ... }
}
FieldTypeNotes
idintegerDelivery id. Stable across all retries of the same event. Receivers MUST dedupe on this.
eventstringOne of the six event codes above. Also surfaced in the X-CPG-Event header.
created_atISO-8601 UTCWhen the gateway scheduled this delivery (not when the on-chain event happened — data carries the on-chain timestamps). Z-suffixed; fractional seconds appear only when non-zero, as 6-digit microseconds (e.g. …00.481523Z, or …00Z when exactly zero).
api_versionstringCurrently "1". Bumped only on breaking payload changes — never silently.
dataobjectEvent-specific. Shape locked per event below.

All decimal amounts are strings to avoid float precision loss. All timestamps are ISO-8601 UTC.

deposit.detected

A new transaction toward one of your deposit addresses appeared on-chain. Has not yet reached min_confirmations.

{
"deposit_request_id": 42,
"user_identifier": null,
"address": "0xabc...def",
"currency": "USDT",
"network": "ETH",
"amount": "100.000000",
"tx_hash": "0x9a8...",
"block_number": 21345678,
"confirmations": 0,
"required_confirmations": 12,
"detected_at": "2026-05-16T22:00:00.000Z"
}

Use this to show "pending deposit" UI to your user. Don't credit anything internally yet — wait for deposit.confirmed.

deposit.confirmed

The detected transaction reached required_confirmations. Funds are now credited to your /v1/balances.

{
"deposit_request_id": 42,
"user_identifier": null,
"address": "0xabc...def",
"currency": "USDT",
"network": "ETH",
"amount": "100.000000",
"tx_hash": "0x9a8...",
"block_number": 21345678,
"confirmations": 12,
"credited_at": "2026-05-16T22:05:30.000Z"
}

This is the primary trigger for any business logic that depends on a deposit being final — credit the user's in-app balance, fulfill an order, etc.

deposit.swept

The gateway moved the deposit out of the per-deposit address into your configured receiving wallet. This is operational — no balance change for you (it was already credited at deposit.confirmed).

{
"deposit_request_id": 42,
"from_address": "0xabc...def",
"to_address": "0xreceiving...wallet",
"currency": "USDT",
"network": "ETH",
"amount": "100.000000",
"sweep_tx_hash": "0xsw...",
"swept_at": "2026-05-16T22:10:00.000Z"
}

Most integrators ignore this event. Useful if you're reconciling your receiving wallet's on-chain balance against your internal ledger.

withdrawal.broadcasted

The chain microservice accepted your withdrawal and broadcast it to the network. Awaiting confirmations.

{
"withdrawal_id": 17,
"to_address": "0xrecipient...",
"currency": "USDT",
"network": "ETH",
"amount": "50.000000",
"fee": "0.50",
"tx_hash": "0xwd...",
"broadcasted_at": "2026-05-16T22:15:00.000Z"
}

Use this to update your UI from "submitting" to "broadcasted — awaiting confirmation".

withdrawal.confirmed

The withdrawal reached min_confirmations. Final state — your balance has been debited by amount + fee.

{
"withdrawal_id": 17,
"tx_hash": "0xwd...",
"confirmations": 12,
"confirmed_at": "2026-05-16T22:20:00.000Z"
}

Compact on purpose — currency, network, amount, fee, to_address were already delivered in withdrawal.broadcasted. Correlate by withdrawal_id.

withdrawal.failed

The withdrawal couldn't be broadcast, was rejected by the chain, or was cancelled by an admin. The balance hold has been released (your available balance is restored). Final state.

{
"withdrawal_id": 17,
"tx_hash": null,
"reason": "no_hot_wallet",
"error_message": "no_hot_wallet",
"failed_at": "2026-05-16T22:16:00.000Z"
}

reason is a normalised enum:

ValueMeaning
no_hot_walletNo hot wallet is configured for this currency's network. Enable a currency on that network first.
amount_below_smallest_unitThe requested amount rounds to zero at the currency's on-chain precision.
broadcast_rejectedThe chain microservice could not broadcast the transaction. Raw RPC error is in error_message.
cancelled_by_adminAn admin cancelled the withdrawal via the dashboard before it was broadcast.

tx_hash is null on every reason above (all are pre-broadcast failures).

At-least-once delivery

Webhook delivery is at-least-once. The same event may arrive more than once:

  • Retries can produce duplicates if your 2xx response was lost in flight.
  • Manual admin requeues create a fresh attempt of an already-delivered row.

Always dedupe on the envelope's id — it's the same integer across every attempt of the same delivery.