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:
| Name | Type | Required | Description |
|---|---|---|---|
sourceChainId | number | Yes | Source 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:
| Field | Description |
|---|---|
address | Token contract address on the source chain. 0x000...000 represents native ETH |
symbol | Human-readable token symbol |
decimals | Token decimal places (source chain) |
destinationAddress | Token contract address on the destination chain. Use this when verifying arrival or displaying the output token |
feeBps | Service 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:
| Name | Type | Required | Description |
|---|---|---|---|
sourceChainId | number | Yes | Source chain ID |
destinationChainId | number | Yes | Destination chain ID |
token | address | Yes | Token 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" }
}
}
}
| Field | Description |
|---|---|
bridges | Object 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:
| Name | Type | Required | Description |
|---|---|---|---|
recipient | address | Yes | Destination address that receives forwarded tokens |
custodialWithdrawer | address | Yes | Emergency recovery address that can withdraw stuck funds after a timelock. For most integrations, use the platform's address. See custodialWithdrawer |
destinationChainId | number | Yes | Target chain ID where assets will be delivered |
salt | bytes32 | No | Optional 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.
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
sourceChainIdsto accept deposits on the destination chain itself.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
recipient | address | Yes | Destination recipient address |
custodialWithdrawer | address | Yes | Withdrawal-authorized address. See custodialWithdrawer |
destinationChainId | number | Yes | Target chain ID |
sourceChainIds | number[] | Yes | Array of source chain IDs to activate monitoring on |
salt | bytes32 | No | Optional 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
}
}
| Field | Description |
|---|---|
expiresAt | Unix 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.
This endpoint tracks activation status (whether the relayer is monitoring this address), not deposit or forwarding completion status.
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
address | address | Yes | The 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:
| Name | Type | Required | Description |
|---|---|---|---|
sourceChainId | number | Yes | Source chain where the deposit originates |
destinationChainId | number | Yes | Destination chain ID |
token | address | Yes | Token address on source chain (0x000...000 for native ETH) |
amount | string | Yes | Input 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"
}
}
| Field | Description |
|---|---|
bridge | Identifier of the bridge selected for this route (e.g. "across", "oft") |
outputToken | Token contract address on the destination chain |
outputAmount | Amount the recipient receives, in the smallest unit of the output token |
relayerBotFee | Relayer service fee in smallest unit (source token decimals). Same-chain forwards return "0" |
bridgeProtocolFee | Bridge protocol fee in smallest unit (source token decimals) |
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.
forwarding_getStatusforwarding_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:
| Name | Type | Required | Description |
|---|---|---|---|
recipient | address | Yes | Destination recipient address |
destinationChainId | number | Yes | Destination 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:
| Name | Type | Required | Description |
|---|---|---|---|
sourceChainId | number | Yes | Source chain ID |
sourceTxHash | hash | Yes | Source 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:
| Name | Type | Required | Description |
|---|---|---|---|
forwardId | string | Yes | Opaque 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.
| Field | Description |
|---|---|
forwardId | Opaque round-trippable UUID. Pass it to forwarding_getForwardById for a targeted refresh |
route | "across", "layerzero", "cctp", or "same_chain" |
status | Normalized delivery state: "pending", "delivered", or "failed" |
recipient | Destination recipient address |
sourceChainId | Chain ID the deposit originated on |
sourceTxHash | Source transaction hash |
proxyAddress | Forwarding address the deposit was sent to (present once known) |
sourceBlockTimestamp | Unix timestamp (seconds) of the source block (present once known) |
destinationChainId | Destination chain ID (present once known) |
destinationTxHash | Delivery transaction hash on the destination chain (present once known) |
refundTxHash | Refund transaction hash, only set on Across forwards refunded to the sender |
failureReason | Set only when status is "failed": "refunded", "expired", or "reverted" |
providerSubStatus | Raw upstream provider status string, for display or debugging |
Route-specific fields, present only on the matching route:
| Field | Route | Description |
|---|---|---|
depositId | across | Across deposit ID |
guid | layerzero | LayerZero message GUID |
sourceEid / destinationEid | layerzero | LayerZero endpoint IDs for the source/destination chain |
lzScanUpdatedAt | layerzero | Timestamp of the last LayerZero Scan update |
eventNonce | cctp | Circle CCTP message nonce |
sourceDomain / destinationDomain | cctp | Circle CCTP domain IDs for the source/destination chain |
messageHash | cctp | Circle CCTP message hash |
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
forwarding_getStatushas been renamed toforwarding_getForwardsByRecipient. The response reshaped from a per-tx{transfers: [{bridgeStatuses}]}grouping into a flat{forwards: Forward[]}list of self-describing entries. See TheForwardobject.
Added
- New
forwarding_getForwardsByTxendpoint. Returns every forward that fanned out from a single source transaction, with no recipient or destination filter. - New
forwarding_getForwardByIdendpoint. Resolves a single forward by its opaqueforwardId, for cheaper targeted status polling. - Every
Forwardnow carries an opaque, round-trippableforwardId(UUID).
2026-05-06
Changed
forwarding_activatehas been renamed toaccount_activateForwardingAddressand now requiresAuthorization: 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_getStatusendpoint. Returns confirmed forwards for a recipient on a destination chain, with per-bridge delivery statuses (pending,attested,delivered,failed,expired,refunded). Renamed toforwarding_getForwardsByRecipienton 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), andUSDT0. Queryforwarding_getRouteswith Tempo's chain ID to see accepted tokens and fees.
2026-04-14
Added
- New
forwarding_getMinimumAmountendpoint. Returns per-bridge minimum deposit amounts for a given source chain, destination chain, and token. Call this beforeforwarding_estimateOutputto validate user input. - New source and destination chain support: BNB Chain and Base. Query
forwarding_getRouteswith the chain's ID to see the accepted tokens and fees. forwarding_estimateOutputresponse now includes abridgefield identifying which bridge was selected for the route.forwarding_activatenow automatically includes the destination chain in the monitored set, so same-chain forwarding works without passing it insourceChainIds.
Changed
forwarding_getRoutesparametersourceChainIdis 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.RouteTokenno longer includes aminAmountfield. Minimums are now bridge-specific and returned byforwarding_getMinimumAmount.forwarding_estimateOutputresponse no longer includesoutputDecimals. To formatoutputAmount, read the output token'sdecimalsfromforwarding_getRoutes(match on the route'sdestinationAddress).
Removed
forwarding_estimateOutputno longer accepts amodeparameter. The service selects the best available bridge automatically and returns it in the responsebridgefield.forwarding_setModehas been removed. Bridging strategy is no longer user-configurable per address.forwarding_deployhas 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.