Skip to main content
The deposit service sends webhook notifications to your configured endpoint as deposits move through the processing pipeline. All webhooks are POST requests with Content-Type: application/json. Configure your webhook URL and optional secret via the POST /setup endpoint.

Payload envelope

Every webhook request body follows the same envelope structure:
{
  "version": "1.0",
  "type": "<event-type>",
  "time": "2025-01-15T12:00:00.000Z",
  "data": { ... }
}
FieldTypeDescription
versionstringProtocol version. Currently "1.0"
typestringEvent type identifier
timestringISO 8601 timestamp of when the event was sent
dataobjectEvent-specific payload (see below)

Event types

TypeTrigger
deposit-receivedToken transfer detected on a registered account
bridge-startedBridging intent created and submitted to the Orchestrator
bridge-progressBridge transaction progressed to a new stage (LayerZero OFT routes only)
bridge-completeTokens arrived on the target chain
bridge-failedBridging failed
post-bridge-swap-completePost-bridge token swap completed
post-bridge-swap-failedPost-bridge token swap failed

deposit-received

Sent when an incoming token transfer is detected on a registered account.
FieldTypeDescription
chainstringSource chain (CAIP-2, e.g. "eip155:8453")
tokenstringToken address
amountstringDeposit amount in raw token units
accountstringAccount address
transactionHashstringDeposit transaction hash
senderstringSender address
{
  "version": "1.0",
  "type": "deposit-received",
  "time": "2025-01-15T12:00:00.000Z",
  "data": {
    "chain": "eip155:8453",
    "token": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
    "amount": "1000000",
    "account": "0x1234567890abcdef1234567890abcdef12345678",
    "transactionHash": "0xabc123...",
    "sender": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
  }
}

bridge-started

Sent when a bridging intent is created and submitted to the Orchestrator.
FieldTypeDescription
source.chainstringSource chain (CAIP-2)
source.assetstringSource asset address
source.amountstringSource amount
destination.chainstringDestination chain (CAIP-2)
destination.assetstringDestination asset address
destination.amountstringDestination amount
accountstringAccount address
deposit.transactionHashstringOriginal deposit transaction hash
deposit.chainstringDeposit chain (CAIP-2)
deposit.assetstringDeposit asset address
deposit.amountstringDeposit amount
deposit.senderstringSender address
settlementLayerstring | undefinedSettlement layer used (e.g. "layerzero")

bridge-progress

Sent when a bridge transaction progresses to a new stage. Only emitted for LayerZero OFT routes.
FieldTypeDescription
stagestringCurrent stage: "source-confirmed", "inflight", or "delivering"
estimatedTimeRemainingSecondsnumber | nullEstimated seconds until completion
estimatedTotalTimeSecondsnumber | nullEstimated total bridge duration in seconds
layerZeroGuidstringLayerZero message GUID
source.transactionHashstringSource chain transaction hash
source.chainstringSource chain (CAIP-2)
destination.transactionHashstring | nullDestination transaction hash (available once delivering)
destination.chainstringDestination chain (CAIP-2)
deposit.transactionHashstringOriginal deposit transaction hash
deposit.chainstringDeposit chain (CAIP-2)
deposit.assetstringDeposit asset address
deposit.amountstringDeposit amount
deposit.senderstringSender address
accountstringAccount address

bridge-complete

Sent when tokens have arrived on the target chain.
FieldTypeDescription
deposit.transactionHashstringOriginal deposit transaction hash
deposit.chainstringDeposit chain (CAIP-2)
deposit.assetstringDeposit asset address
deposit.amountstringDeposit amount
deposit.senderstringSender address
source.transactionHashstringSource chain claim transaction hash
source.chainstringSource chain (CAIP-2)
source.amountstringSource amount
source.assetstringSource asset address
destination.transactionHashstringDestination chain transaction hash
destination.chainstringDestination chain (CAIP-2)
destination.amountstringDestination amount
destination.assetstringDestination asset address
accountstringAccount address
settlementLayerstring | undefinedSettlement layer used
{
  "version": "1.0",
  "type": "bridge-complete",
  "time": "2025-01-15T12:01:30.000Z",
  "data": {
    "deposit": {
      "transactionHash": "0xabc123...",
      "chain": "eip155:8453",
      "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
      "amount": "1000000",
      "sender": "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
    },
    "source": {
      "transactionHash": "0xdef456...",
      "chain": "eip155:8453",
      "amount": "1000000",
      "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"
    },
    "destination": {
      "transactionHash": "0x789ghi...",
      "chain": "eip155:42161",
      "amount": "990000",
      "asset": "0xaf88d065e77c8cc2239327c5edb3a432268e5831"
    },
    "account": "0x1234567890abcdef1234567890abcdef12345678"
  }
}

bridge-failed

Sent when a bridging operation fails. See error codes for the full list and retry behavior.
FieldTypeDescription
errorCodestringError code (e.g. "BRIDGE-1")
accountstringAccount address
messagestring | undefinedHuman-readable error description
deposit.transactionHashstringDeposit transaction hash
deposit.chainstringDeposit chain (CAIP-2)
deposit.tokenstringDeposit token address
deposit.amountstringDeposit amount
deposit.senderstringSender address
intentIdstring | undefinedIntent ID, when available

post-bridge-swap-complete

Sent when a post-bridge token swap completes successfully. This occurs when the account has token routing rules configured that require a swap after bridging.
FieldTypeDescription
deposit.transactionHashstringOriginal deposit transaction hash
deposit.chainstringDeposit chain (CAIP-2)
deposit.assetstringDeposit asset address
deposit.amountstringDeposit amount
deposit.senderstringSender address
swap.transactionHashstringSwap transaction hash
swap.chainstringChain where the swap executed (CAIP-2)
swap.tokenInstringInput token address
swap.tokenOutstringOutput token address
swap.amountstringOutput amount
swap.recipientstringRecipient address
bridge.transactionHashstring | undefinedBridge transaction hash
bridge.chainstring | undefinedBridge chain (CAIP-2)
bridge.assetstring | undefinedBridge asset address
bridge.amountstring | undefinedBridge amount
accountstringAccount address

post-bridge-swap-failed

Sent when a post-bridge token swap fails.
FieldTypeDescription
errorCodestringError code (e.g. "SWAP-1")
messagestring | undefinedHuman-readable error description
accountstringAccount address
deposit.transactionHashstringOriginal deposit transaction hash
deposit.chainstringDeposit chain (CAIP-2)
deposit.assetstringDeposit asset address
deposit.amountstringDeposit amount
deposit.senderstringSender address
swap.chainstringChain where the swap was attempted (CAIP-2)
swap.tokenInstringInput token address
swap.tokenOutstringOutput token address
swap.amountstringAttempted swap amount
swap.recipientstringIntended recipient address
swap.bridgeTransactionHashstring | undefinedBridge transaction hash

Signature verification

If you provided a webhookSecret during setup, every webhook request includes an X-Webhook-Signature header:
X-Webhook-Signature: sha256=<hex-encoded-signature>
The signature is an HMAC-SHA256 hash computed over the raw JSON request body using your secret. To verify:
  1. Read the raw request body as a string (before JSON parsing)
  2. Compute the HMAC-SHA256 of the raw body using your webhook secret
  3. Compare the result with the value in the X-Webhook-Signature header (strip the sha256= prefix)
  4. Use a constant-time comparison to prevent timing attacks
import { createHmac, timingSafeEqual } from "node:crypto";

function verifyWebhookSignature(
  rawBody: string,
  signatureHeader: string,
  secret: string,
): boolean {
  const expected =
    "sha256=" +
    createHmac("sha256", secret).update(rawBody).digest("hex");
  return (
    signatureHeader.length === expected.length &&
    timingSafeEqual(Buffer.from(signatureHeader), Buffer.from(expected))
  );
}
Always verify against the raw request body string, not a re-serialized version of the parsed JSON. Re-serialization may change key order or whitespace, which will produce a different signature.

Delivery behavior

  • Retries — failed deliveries are retried automatically.
  • Ordering — events for a single deposit are sent in lifecycle order (deposit-receivedbridge-startedbridge-complete), but there is no global ordering guarantee across deposits.
  • URL validation — the webhook URL must use HTTPS and must not target internal or private network addresses.
  • Idempotency — use data.deposit.transactionHash combined with type to deduplicate events on your end.

Conventions

  • All addresses and token addresses are lowercase.
  • All amounts are strings (raw token units, not human-readable).
  • Chains use CAIP-2 identifiers (e.g. "eip155:8453" for Base).