Skip to main content

Forwarding Address API Reference

Protocol

The API uses JSON-RPC 2.0 over HTTP POST with Content-Type: application/json.

Authentication

Most methods are public. Only the activation call (account_activateForwardingAddress) requires authentication: send Authorization: Bearer <account_api_key>. Account API keys are issued by Candide on request.


Discovery

forwarding_getRoutes

Returns all routes from a given source chain with their available tokens and fees. This is the single source of truth for which destination chains and tokens are supported. Routes, tokens, and fees can change dynamically.

Parameters:

NameTypeRequiredDescription
sourceChainIdnumberYesSource chain ID to get routes from

Request:

{
"jsonrpc": "2.0",
"id": 1,
"method": "forwarding_getRoutes",
"params": [{ "sourceChainId": 1 }]
}

Response:

{
"jsonrpc": "2.0",
"id": 1,
"result": {
"routes": [
{
"sourceChainId": 1,
"sourceChainName": "Ethereum",
"destinationChainId": 42161,
"destinationChainName": "Arbitrum One",
"tokens": [
{
"address": "0x0000000000000000000000000000000000000000",
"symbol": "ETH",
"decimals": 18,
"destinationAddress": "0x0000000000000000000000000000000000000000",
"feeBps": 50
},
{
"address": "0xdAC17F958D2ee523a2206206994597C13D831ec7",
"symbol": "USDT",
"decimals": 6,
"destinationAddress": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
"feeBps": 50
}
]
}
]
}
}

Token fields:

FieldDescription
addressToken contract address on the source chain. 0x000...000 represents native ETH
symbolHuman-readable token symbol
decimalsToken decimal places (source chain)
destinationAddressToken contract address on the destination chain. Use this when verifying arrival or displaying the output token
feeBpsService fee in basis points (50 = 0.5%)

forwarding_getMinimumAmount

Returns the minimum deposit amount per bridge for a specific route. Use this to validate user input before calling forwarding_estimateOutput. Minimums are bridge-specific because each bridge has its own threshold below which deposits are not processed.

Parameters:

NameTypeRequiredDescription
sourceChainIdnumberYesSource chain ID
destinationChainIdnumberYesDestination chain ID
tokenaddressYesToken address on the source chain

Request:

{
"jsonrpc": "2.0",
"id": 1,
"method": "forwarding_getMinimumAmount",
"params": [{
"sourceChainId": 1,
"destinationChainId": 42161,
"token": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
}]
}

Response:

{
"jsonrpc": "2.0",
"id": 1,
"result": {
"bridges": {
"across": { "minAmount": "500000" },
"oft": { "minAmount": "1000000" }
}
}
}
FieldDescription
bridgesObject keyed by bridge identifier. Each entry contains a minAmount in the smallest unit of the source token. A deposit below every bridge's minimum will not be forwarded

Core

forwarding_getAddress

Computes the deterministic CREATE2 proxy address for a given parameter set. Pure computation with no side effects. Safe to call repeatedly. The same inputs always produce the same address.

Parameters:

NameTypeRequiredDescription
recipientaddressYesDestination address that receives forwarded tokens
custodialWithdraweraddressYesEmergency recovery address that can withdraw stuck funds after a timelock. For most integrations, use the platform's address. See custodialWithdrawer
destinationChainIdnumberYesTarget chain ID where assets will be delivered
saltbytes32NoOptional 32-byte hex value. Use different salts to generate multiple forwarding addresses for the same recipient

Request:

{
"jsonrpc": "2.0",
"id": 1,
"method": "forwarding_getAddress",
"params": [{
"recipient": "0xAbCdEf0123456789AbCdEf0123456789AbCdEf01",
"custodialWithdrawer": "0xAbCdEf0123456789AbCdEf0123456789AbCdEf01",
"destinationChainId": 10
}]
}

Response:

{
"jsonrpc": "2.0",
"id": 1,
"result": {
"address": "0xDEF456..."
}
}

account_activateForwardingAddress

Activates relayer monitoring for a forwarding address on specified source chains. Returns the address, active status, and TTL expiration timestamp.

Requires authentication

Send Authorization: Bearer <account_api_key> with this request. Keys are issued by Candide. Each account can have up to 500 active forwarding addresses; refreshing an existing address does not count against the cap. Exceeding the cap returns error -32013.

Key behaviors:

  • Idempotent: calling again resets the TTL. Use this to keep an address active.
  • TTL-based: monitoring expires after the TTL. The address must be reactivated to resume forwarding.
  • Reusable: call activate again after expiration.
  • Same-chain forwarding: the destination chain is automatically included in the monitored set. You do not need to pass it in sourceChainIds to accept deposits on the destination chain itself.

Parameters:

NameTypeRequiredDescription
recipientaddressYesDestination recipient address
custodialWithdraweraddressYesWithdrawal-authorized address. See custodialWithdrawer
destinationChainIdnumberYesTarget chain ID
sourceChainIdsnumber[]YesArray of source chain IDs to activate monitoring on
saltbytes32NoOptional salt (must match the one used in forwarding_getAddress)

Request:

{
"jsonrpc": "2.0",
"id": 1,
"method": "account_activateForwardingAddress",
"params": [{
"recipient": "0xAbCdEf0123456789AbCdEf0123456789AbCdEf01",
"custodialWithdrawer": "0xAbCdEf0123456789AbCdEf0123456789AbCdEf01",
"destinationChainId": 10,
"sourceChainIds": [1, 42161]
}]
}

Response:

{
"jsonrpc": "2.0",
"id": 1,
"result": {
"address": "0xDEF456...",
"active": true,
"expiresAt": 1741132800
}
}
FieldDescription
expiresAtUnix timestamp (seconds) when monitoring expires. Do not hardcode this value; the default TTL may change

forwarding_getActivation

Checks the activation status of a forwarding address across all source chains it was registered on.

note

This endpoint tracks activation status (whether the relayer is monitoring this address), not deposit or forwarding completion status.

Parameters:

NameTypeRequiredDescription
addressaddressYesThe forwarding address to check

Request:

{
"jsonrpc": "2.0",
"id": 1,
"method": "forwarding_getActivation",
"params": [{
"address": "0xDEF456..."
}]
}

Response:

{
"jsonrpc": "2.0",
"id": 1,
"result": {
"address": "0xDEF456...",
"sourceChains": [
{
"sourceChainId": 1,
"status": "active",
"expiresAt": 1741132800
},
{
"sourceChainId": 42161,
"status": "expired",
"expiredAt": 1740528000
}
]
}
}

Estimation

forwarding_estimateOutput

Estimates the output amount a recipient will receive after relayer and bridge protocol fees. Returns the best available bridge for the route. This method is decoupled from forwarding addresses: it takes chain IDs directly and does not require the address to be activated.

Parameters:

NameTypeRequiredDescription
sourceChainIdnumberYesSource chain where the deposit originates
destinationChainIdnumberYesDestination chain ID
tokenaddressYesToken address on source chain (0x000...000 for native ETH)
amountstringYesInput amount in smallest unit (e.g. "1000000000000000000" for 1 ETH)

Request:

{
"jsonrpc": "2.0",
"id": 1,
"method": "forwarding_estimateOutput",
"params": [{
"sourceChainId": 1,
"destinationChainId": 42161,
"token": "0x0000000000000000000000000000000000000000",
"amount": "1000000000000000000"
}]
}

Response:

{
"jsonrpc": "2.0",
"id": 1,
"result": {
"destinationChainId": 42161,
"outputToken": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
"outputTokenSymbol": "USDC",
"bridge": "across",
"outputAmount": "994005000",
"relayerBotFee": "5000000",
"bridgeProtocolFee": "995000"
}
}
FieldDescription
bridgeIdentifier of the bridge selected for this route (e.g. "across", "oft")
outputTokenToken contract address on the destination chain
outputAmountAmount the recipient receives, in the smallest unit of the output token
relayerBotFeeRelayer service fee in smallest unit (source token decimals). Same-chain forwards return "0"
bridgeProtocolFeeBridge protocol fee in smallest unit (source token decimals)
note

To format outputAmount for display, look up the output token's decimals from forwarding_getRoutes (the matching destinationAddress on the route's token list).


Status

Three methods return a unified, self-describing Forward object (see The Forward object below). Each Forward carries its own source context (sourceChainId, sourceTxHash, proxyAddress, sourceBlockTimestamp), so there is no wrapping "transfer" grouping type to unpack.

Renamed from forwarding_getStatus

forwarding_getStatus was renamed to forwarding_getForwardsByRecipient and its response reshaped from a per-tx {transfers: [{bridgeStatuses}]} grouping into a flat {forwards: Forward[]} list. See the changelog.

forwarding_getForwardsByRecipient

Returns every confirmed forward for a recipient on a destination chain. This is the primary method for tracking deposit status: call it after activating a forwarding address to see forwards as they're picked up and delivered.

Parameters:

NameTypeRequiredDescription
recipientaddressYesDestination recipient address
destinationChainIdnumberYesDestination chain ID

Request:

{
"jsonrpc": "2.0",
"id": 1,
"method": "forwarding_getForwardsByRecipient",
"params": [{
"recipient": "0xAbCdEf0123456789AbCdEf0123456789AbCdEf01",
"destinationChainId": 10
}]
}

Response:

{
"jsonrpc": "2.0",
"id": 1,
"result": {
"forwards": [
{
"forwardId": "550e8400-e29b-41d4-a716-446655440000",
"route": "across",
"status": "delivered",
"recipient": "0xAbCdEf0123456789AbCdEf0123456789AbCdEf01",
"sourceChainId": 1,
"sourceTxHash": "0x123...abc",
"proxyAddress": "0xDEF456...",
"sourceBlockTimestamp": 1741132800,
"destinationChainId": 10,
"destinationTxHash": "0x789...def",
"depositId": 12345
}
]
}
}

forwarding_getForwardsByTx

Returns every forward that fanned out from a single source transaction, with no recipient or destination filter. Mirrors LayerZero Scan's and Circle Iris's tx-keyed lookups. Useful when you already have the source tx hash (e.g. from watching the user's wallet) and want every forward it produced, including ones sent to other recipients in the same batch.

Parameters:

NameTypeRequiredDescription
sourceChainIdnumberYesSource chain ID
sourceTxHashhashYesSource transaction hash

Request:

{
"jsonrpc": "2.0",
"id": 1,
"method": "forwarding_getForwardsByTx",
"params": [{
"sourceChainId": 8453,
"sourceTxHash": "0x123...abc"
}]
}

Response:

{
"jsonrpc": "2.0",
"id": 1,
"result": {
"forwards": [
{
"forwardId": "550e8400-e29b-41d4-a716-446655440000",
"route": "layerzero",
"status": "pending",
"recipient": "0xAbCdEf0123456789AbCdEf0123456789AbCdEf01",
"sourceChainId": 8453,
"sourceTxHash": "0x123...abc",
"destinationChainId": 10,
"guid": "0x..."
}
]
}
}

An empty forwards array means the source tx has no confirmed forward rows yet, either because it isn't one of ours or because it hasn't been indexed yet.


forwarding_getForwardById

Resolves a single forward by its opaque forwardId — the round-trippable UUID returned on every Forward. Pluck an id from a forwarding_getForwardsByRecipient or forwarding_getForwardsByTx response and poll this method for cheaper, targeted status refreshes instead of refetching the whole list.

Parameters:

NameTypeRequiredDescription
forwardIdstringYesOpaque Forward UUID (public_id)

Request:

{
"jsonrpc": "2.0",
"id": 1,
"method": "forwarding_getForwardById",
"params": [{
"forwardId": "550e8400-e29b-41d4-a716-446655440000"
}]
}

Response:

{
"jsonrpc": "2.0",
"id": 1,
"result": {
"forward": {
"forwardId": "550e8400-e29b-41d4-a716-446655440000",
"route": "across",
"status": "delivered",
"recipient": "0xAbCdEf0123456789AbCdEf0123456789AbCdEf01",
"sourceChainId": 1,
"sourceTxHash": "0x123...abc",
"destinationChainId": 10,
"destinationTxHash": "0x789...def"
}
}
}

Returns { "forward": null } if the id is unknown.


The Forward object

Every Forward is discriminated on route and carries a normalized status, regardless of which bridge protocol moved the funds.

FieldDescription
forwardIdOpaque round-trippable UUID. Pass it to forwarding_getForwardById for a targeted refresh
route"across", "layerzero", "cctp", or "same_chain"
statusNormalized delivery state: "pending", "delivered", or "failed"
recipientDestination recipient address
sourceChainIdChain ID the deposit originated on
sourceTxHashSource transaction hash
proxyAddressForwarding address the deposit was sent to (present once known)
sourceBlockTimestampUnix timestamp (seconds) of the source block (present once known)
destinationChainIdDestination chain ID (present once known)
destinationTxHashDelivery transaction hash on the destination chain (present once known)
refundTxHashRefund transaction hash, only set on Across forwards refunded to the sender
failureReasonSet only when status is "failed": "refunded", "expired", or "reverted"
providerSubStatusRaw upstream provider status string, for display or debugging

Route-specific fields, present only on the matching route:

FieldRouteDescription
depositIdacrossAcross deposit ID
guidlayerzeroLayerZero message GUID
sourceEid / destinationEidlayerzeroLayerZero endpoint IDs for the source/destination chain
lzScanUpdatedAtlayerzeroTimestamp of the last LayerZero Scan update
eventNoncecctpCircle CCTP message nonce
sourceDomain / destinationDomaincctpCircle CCTP domain IDs for the source/destination chain
messageHashcctpCircle CCTP message hash
note

CCTP forwards top out at "pending" — Circle's attestation API only confirms the message was signed, not that the destination-chain mint executed. same_chain forwards are always "delivered" immediately, since there is no bridge: delivery is the source-tx confirmation itself.


Emergency Recovery

The relayer auto-deploys the proxy contract on first deposit, so integrators typically do not need to deploy it manually. If tokens are stuck in a forwarding address (e.g. the relayer did not process them), the proxy must be deployed before funds can be withdrawn directly on-chain.

There is no JSON-RPC method for deploy or withdraw. Both actions are performed on-chain from a user's wallet. Direct users to the recovery frontend, which handles the deploy and withdraw transactions for the recipient or custodialWithdrawer.


TypeScript Types

These types match the API response shapes and can serve as a reference for building your integration.

interface RouteToken {
address: string;
symbol: string;
decimals: number;
destinationAddress: string; // token address on destination chain
feeBps: number; // basis points (50 = 0.5%)
}

interface Route {
sourceChainId: number;
sourceChainName: string;
destinationChainId: number;
destinationChainName: string;
tokens: RouteToken[];
}

interface RoutesResult {
routes: Route[];
}

interface MinimumAmountResult {
bridges: {
[bridgeName: string]: { minAmount: string }; // smallest unit, source token decimals
};
}

interface AddressResult {
address: string;
}

interface ActivationResult {
address: string;
active: boolean;
expiresAt: number; // Unix timestamp (seconds)
}

interface SourceChainActivation {
sourceChainId: number;
status: "active" | "expired";
expiresAt?: number; // present when active
expiredAt?: number; // present when expired
}

interface ActivationStatus {
address: string;
sourceChains: SourceChainActivation[];
}

interface EstimateResult {
destinationChainId: number;
outputToken: string;
outputTokenSymbol: string;
bridge: string; // bridge identifier selected for the route
outputAmount: string; // smallest unit, output token decimals
relayerBotFee: string; // smallest unit, source token decimals
bridgeProtocolFee: string; // smallest unit, source token decimals
}

type ForwardRoute = "across" | "layerzero" | "cctp" | "same_chain";
type ForwardStatus = "pending" | "delivered" | "failed";

interface Forward {
forwardId: string | null;
route: ForwardRoute | string;
status: ForwardStatus | string;
recipient: string;
sourceChainId: number;
sourceTxHash: string;
proxyAddress?: string;
sourceBlockTimestamp?: number; // unix seconds
destinationChainId?: number;
destinationTxHash?: string;
refundTxHash?: string;
failureReason?: string; // "refunded" | "expired" | "reverted" | ...
providerSubStatus?: string;
// Across-specific
depositId?: number;
// LayerZero-specific
guid?: string;
sourceEid?: number;
destinationEid?: number;
lzScanUpdatedAt?: string;
// CCTP-specific
eventNonce?: string;
sourceDomain?: number;
destinationDomain?: number;
messageHash?: string;
}

interface ForwardsResult {
forwards: Forward[];
}

Changelog

2026-07-13

Changed

Added

  • New forwarding_getForwardsByTx endpoint. Returns every forward that fanned out from a single source transaction, with no recipient or destination filter.
  • New forwarding_getForwardById endpoint. Resolves a single forward by its opaque forwardId, for cheaper targeted status polling.
  • Every Forward now carries an opaque, round-trippable forwardId (UUID).

2026-05-06

Changed

  • forwarding_activate has been renamed to account_activateForwardingAddress and now requires Authorization: Bearer <account_api_key>. Other methods remain public. Request an account API key from Candide.

Added

  • Per-account cap of 500 active forwarding addresses. Refreshing an existing address does not count against the cap. Exceeding the cap returns error code -32013.
  • New forwarding_getStatus endpoint. Returns confirmed forwards for a recipient on a destination chain, with per-bridge delivery statuses (pending, attested, delivered, failed, expired, refunded). Renamed to forwarding_getForwardsByRecipient on 2026-07-13, see above.
  • Same-chain forwards is now fee free. Only the gas cost is taken into account in the relayer fee.

2026-04-22

Added

  • New source and destination chain support: Tempo. Supported tokens are USDC.e, USDT (Tempo's native token), and USDT0. Query forwarding_getRoutes with Tempo's chain ID to see accepted tokens and fees.

2026-04-14

Added

  • New forwarding_getMinimumAmount endpoint. Returns per-bridge minimum deposit amounts for a given source chain, destination chain, and token. Call this before forwarding_estimateOutput to validate user input.
  • New source and destination chain support: BNB Chain and Base. Query forwarding_getRoutes with the chain's ID to see the accepted tokens and fees.
  • forwarding_estimateOutput response now includes a bridge field identifying which bridge was selected for the route.
  • forwarding_activate now automatically includes the destination chain in the monitored set, so same-chain forwarding works without passing it in sourceChainIds.

Changed

  • forwarding_getRoutes parameter sourceChainId is now required. The endpoint returns all routes originating from that source chain. Call it once per source chain to build the full map of supported destinations.
  • RouteToken no longer includes a minAmount field. Minimums are now bridge-specific and returned by forwarding_getMinimumAmount.
  • forwarding_estimateOutput response no longer includes outputDecimals. To format outputAmount, read the output token's decimals from forwarding_getRoutes (match on the route's destinationAddress).

Removed

  • forwarding_estimateOutput no longer accepts a mode parameter. The service selects the best available bridge automatically and returns it in the response bridge field.
  • forwarding_setMode has been removed. Bridging strategy is no longer user-configurable per address.
  • forwarding_deploy has been removed as a JSON-RPC method. The relayer auto-deploys the proxy on first deposit. For emergency recovery of stuck funds, users deploy and withdraw on-chain from their own wallet via the recovery frontend.