Skip to content

Builder Integration Guide

Overview

This guide explains how to integrate your application with GRVT and enable trading on behalf of your users.

Integration steps

1. Connect User Wallet

2. Authorize Builder

Choose the environment you want to authenticate against.

# stg
GRVT_AUTH_ENDPOINT="https://edge.staging.gravitymarkets.io/auth/builder/authorize"
# testnet
GRVT_AUTH_ENDPOINT="https://edge.testnet.grvt.io/auth/builder/authorize"
# prod
GRVT_AUTH_ENDPOINT="https://edge.grvt.io/auth/builder/authorize"

Request Parameters

Retrieving Funding Addresses

Both main_account_id and builder_account_id are funding addresses that can be retrieved by accessing the GRVT Exchange API Keys page: https://grvt.io/exchange/account/api-keys

This endpoint supports two modes depending on whether you provide the API key fields:

  • With API key (provide builder_api_key_signer + builder_api_key_permissions + builder_api_key_label): Authorizes the builder on-chain via AddAccountSignerWithBuilder and creates a GRVT API key. The API key is returned in the response.
  • Without API key (omit all three builder_api_key_* fields): Authorizes the builder on-chain via AuthorizeBuilder without creating an API key. Returns an empty response.
"name" Type Required Description
main_account_id string True The funding address of the user granting the authorization.
builder_account_id string True The funding address of the Builder receiving the authorization.
max_futures_fee_rate string True The maximum fee rate cap in percentage for Futures trades executed by this builder. The builder cannot charge fees exceeding this limit. Eg. "0.1" means 0.1%
max_spot_fee_rate string True The maximum fee rate cap in percentage for Spot trades executed by this builder. The builder cannot charge fees exceeding this limit. Eg. "0.1" means 0.1%
signature Signature True The cryptographic signature authenticating this request. Must be signed by the private key associated with mainAccountID. See Signing Payload section below for details.
builder_api_key_label string False Required when builder_api_key_signer and builder_api_key_permissions are provided. The user will see this label on the gravity api list UI.
builder_api_key_signer string False An Ethereum public key pair that you generate for your user. This key can sign trades on behalf of your user across all sub-accounts they have. You can use the private key to sign trades on behalf of your user without sending Grvt the private key. Must be provided together with builder_api_key_permissions.
builder_api_key_permissions string False Permissions as a sorted string (lowest bit to highest bit), separated by &:
- Examples: "Trade", "Admin", "Admin&Trade"
- Bit mapping: ADMIN=1, INTERNAL_TRANSFER=2, EXTERNAL_TRANSFER=3, WITHDRAW=4, VAULT_INVESTOR=5, TRADE=6

Please use TRADE for now
Must be provided together with builder_api_key_signer.

Example Request

With API key creation:

{
    "main_account_id": "'0x...'",
    "builder_account_id": "'0x....'",
    "max_futures_fee_rate": "0.001",
    "max_spot_fee_rate": "0.0001",
    "signature": {
        "signer": "0xc73c0c2538fd9b833d20933ccc88fdaa74fcb0d0",
        "r": "0xb788d96fee91c7cdc35918e0441b756d4000ec1d07d900c73347d9abbc20acc8",
        "s": "0x3d786193125f7c29c958647da64d0e2875ece2c3f845a591bdd7dae8c475e26d",
        "v": 28,
        "expiration": "1697788800000000000",
        "nonce": 1234567890,
        "chain_id": "327"
    },
    "builder_api_key_label": "superbuilder",
    "builder_api_key_signer": "0x....",
    "builder_api_key_permissions": "Admin&Trade"
}

Without API key (on-chain authorization only):

{
    "main_account_id": "'0x...'",
    "builder_account_id": "'0x....'",
    "max_futures_fee_rate": "0.001",
    "max_spot_fee_rate": "0.0001",
    "signature": {
        "signer": "0xc73c0c2538fd9b833d20933ccc88fdaa74fcb0d0",
        "r": "0xb788d96fee91c7cdc35918e0441b756d4000ec1d07d900c73347d9abbc20acc8",
        "s": "0x3d786193125f7c29c958647da64d0e2875ece2c3f845a591bdd7dae8c475e26d",
        "v": 28,
        "expiration": "1697788800000000000",
        "nonce": 1234567890,
        "chain_id": "327"
    }
}

Signing Payload

The signature must be created using EIP-712 typed data signing. The structure differs based on the authorization mode:

With API key (when providing builder_api_key_signer + builder_api_key_permissions):

{
  "domain": {
    "chainId": 327,
    "name": "GRVT Exchange",
    "version": "0"
  },
  "message": {
    "accountID": "'0x...'",
    "signer": "'0x....'",
    "permissions": "Trade",
    "builderAccountID": "'0x....'",
    "maxFutureFeeRate": 100,
    "maxSpotFeeRate": 10,
    "nonce": 1234567890,
    "expiration": 1697788800000000000
  },
  "primaryType": "AddAccountSignerWithBuilder",
  "types": {
    "EIP712Domain": [
      { "name": "name", "type": "string" },
      { "name": "version", "type": "string" },
      { "name": "chainId", "type": "uint256" }
    ],
    "AddAccountSignerWithBuilder": [
      { "name": "accountID", "type": "address" },
      { "name": "signer", "type": "address" },
      { "name": "permissions", "type": "string" },
      { "name": "builderAccountID", "type": "address" },
      { "name": "maxFutureFeeRate", "type": "uint32" },
      { "name": "maxSpotFeeRate", "type": "uint32" },
      { "name": "nonce", "type": "uint32" },
      { "name": "expiration", "type": "int64" }
    ]
  }
}

Without API key (on-chain authorization only):

{
  "domain": {
    "chainId": 327,
    "name": "GRVT Exchange",
    "version": "0"
  },
  "message": {
    "mainAccountID": "'0x...'",
    "builderAccountID": "'0x....'",
    "maxFutureFeeRate": 100,
    "maxSpotFeeRate": 10,
    "nonce": 1234567890,
    "expiration": 1697788800000000000
  },
  "primaryType": "AuthorizeBuilder",
  "types": {
    "EIP712Domain": [
      { "name": "name", "type": "string" },
      { "name": "version", "type": "string" },
      { "name": "chainId", "type": "uint256" }
    ],
    "AuthorizeBuilder": [
      { "name": "mainAccountID", "type": "address" },
      { "name": "builderAccountID", "type": "address" },
      { "name": "maxFutureFeeRate", "type": "uint32" },
      { "name": "maxSpotFeeRate", "type": "uint32" },
      { "name": "nonce", "type": "uint32" },
      { "name": "expiration", "type": "int64" }
    ]
  }
}

Chain ID

The chainId value in the domain field must match the GRVT L2 Chain ID for your target environment. The example above uses 327 (Sepolia Stg). Refer to the Chain IDs table for all network-specific values.

Fee Rate Units

The maxFutureFeeRate and maxSpotFeeRate fields in the signing payload are expressed as integers multiplied by 10,000 (1e4): - Calculation: fee_rate × 10,000 - Example conversions:

- `0.001` fee rate = `10` (0.001 × 10,000)
- `0.0005` fee rate = `5` (0.0005 × 10,000)
- `0.0001` fee rate = `1` (0.0001 × 10,000)

Payload Fields:

Field Type Mode Description
chainId uint256 Both The GRVT L2 Chain ID for the target network (in domain field). Refer to Chain IDs table for network-specific values.
mainAccountID address Without API key The Main Account ID (Ethereum address) of the user granting authorization (used in AuthorizeBuilder type)
accountID address With API key The Main Account ID (Ethereum address) of the user granting authorization (used in AddAccountSignerWithBuilder type)
builderAccountID address Both The builder_account_id - Main Account ID of the Builder
maxFutureFeeRate uint32 Both The max_futures_fee_rate - Maximum fee rate for Futures trades (multiplied by 10,000)
maxSpotFeeRate uint32 Both The max_spot_fee_rate - Maximum fee rate for Spot trades (multiplied by 10,000)
nonce uint32 Both Random value for signature deconflicting (0 to 4,294,967,295)
expiration int64 Both Timestamp in unix nanoseconds when signature expires (max 30 days)
signer address With API key The builder_api_key_signer - Ethereum public key that can sign trades on behalf of the user
permissions string With API key The builder_api_key_permissions as a sorted string (lowest bit to highest bit), separated by &:
- Single permission: "Admin" or "Trade"
- Multiple permissions: "Admin&InternalTransfer", "Admin&InternalTransfer&Trade"
- Bit mapping: ADMIN=1, INTERNAL_TRANSFER=2, EXTERNAL_TRANSFER=3, WITHDRAW=4, VAULT_INVESTOR=5, TRADE=6
- Note: Permissions are always sorted by bit position (e.g., "Admin&Trade", never "Trade&Admin")

Permission String Format

  • Both the permissions field in the signing payload and the builder_api_key_permissions request parameter must use the sorted string format
  • When multiple permissions are granted, they must be sorted by bit position (lowest to highest) and joined with &
  • Examples:
    • Single permission: "Trade" or "Admin"
    • Multiple permissions: "Admin&InternalTransfer" (correct) vs "InternalTransfer&Admin" (incorrect - wrong order)
    • Combined: "Admin&InternalTransfer&Trade"
  • Note: The permission string format ensures a 1-to-1 mapping between the string and its bitmask value

Signing Process:

  1. The user must sign this EIP-712 typed data payload with their private key associated with main_account_id
  2. The signing produces three values: r, s, and v
  3. These values, along with the signer address, nonce, expiration, and chain_id are included in the signature field of the request

Note

The signature fields correspond to the Signature type used throughout the GRVT API.

Example Response

With API key creation — the API key is used for authentication in step 3:

{
    "api_key": "abc....."
}

Without API key — returns an empty response on success:

{}

3. Authenticate with API Key

Note

This step applies only when using the with API key authorization mode.

Use the api_key returned from step 2 to authenticate with the GRVT API and use trading functions.

For detailed authentication instructions, please refer to the Authentication page.

4. Manage Sub-Accounts

Query available sub-accounts that your builder can trade on.

Reference: Builder Codes - Get Sub Accounts

5. Execute Trades

Your builder can now execute trades on all authorized sub-accounts using the Trade permission and the builder_api_signer key pair from one of the sub accounts.

Reference: Builder Codes - Trading on Behalf of Users

Get Deposit Addresses

Returns the chains and tokens supported for depositing into the authenticated user's GRVT account, along with the on-chain deposit address to send funds to on each chain.

Use this endpoint to discover where a user should send funds when topping up their GRVT account. Each chain entry includes the deposit address generated for the user, the supported tokens, and their on-chain contract addresses.

Endpoint

# staging
GRVT_DEPOSIT_ENDPOINT="https://edge.staging.gravitymarkets.io/api/v1/deposit/addresses"
# testnet
GRVT_DEPOSIT_ENDPOINT="https://edge.testnet.grvt.io/api/v1/deposit/addresses"
# prod
GRVT_DEPOSIT_ENDPOINT="https://edge.grvt.io/api/v1/deposit/addresses"
Method Path
GET /api/v1/deposit/addresses

Authentication

This endpoint accepts either authentication method:

  • API key — pass your API key in the X-API-Key header. The account is resolved directly from the key, so no session cookie is required.
  • Session cookie — authenticate via login to obtain a session cookie (gravity=...) and the X-Grvt-Account-Id header value, then pass both on the request.

See the Authentication page for details on both methods.

The resolved account must have completed chain account setup. Requests are rejected when the bridge feature is disabled or the account has no chain account address.

Request Parameters

This endpoint takes no query parameters or request body. The chains and tokens are derived from the resolved account.

Example Request

Using an API key:

GRVT_API_KEY="<insert_key_here>"

curl "$GRVT_DEPOSIT_ENDPOINT" \
    -H "X-API-Key: $GRVT_API_KEY" \
    -s

Using a session cookie:

curl "$GRVT_DEPOSIT_ENDPOINT" \
    -H "Cookie: $GRVT_COOKIE" \
    -H "X-Grvt-Account-Id: $GRVT_ACCOUNT_ID" \
    -s

Response Fields

Field Type Description
chains array of ChainInfo List of chains supported for deposits, one per chain.

ChainInfo

Field Type Description
chain string The GRVT chain name (e.g. ARBITRUM, BASE, BSC, KAIA, POLYGON, SOLANA, TRON).
chain_id string The native chain ID, derived from chain (e.g. 42161 for ARBITRUM, 56 for BSC). For non-EVM chains this is a chain name such as solana or tron.
deposit_address string The on-chain address to send deposits to for this chain. Empty for on-demand-only chains where a deposit address has not yet been generated for the user (see note below).
supported_tokens array of TokenInfo The tokens that can be deposited on this chain.

TokenInfo

Field Type Description
name string The token symbol (e.g. USDT, USDC).
contract_address string The token's on-chain contract address on the given chain. May be empty for native chain assets.

Example Response

{
    "chains": [
        {
            "chain": "ARBITRUM",
            "chain_id": "42161",
            "deposit_address": "0xYourArbitrumDepositAddress",
            "supported_tokens": [
                {
                    "name": "USDT",
                    "contract_address": "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9"
                },
                {
                    "name": "USDC",
                    "contract_address": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831"
                }
            ]
        },
        {
            "chain": "BASE",
            "chain_id": "8453",
            "deposit_address": "0xYourBaseDepositAddress",
            "supported_tokens": [
                {
                    "name": "USDC",
                    "contract_address": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
                }
            ]
        },
        {
            "chain": "BSC",
            "chain_id": "56",
            "deposit_address": "0xYourBscDepositAddress",
            "supported_tokens": [
                {
                    "name": "USDT",
                    "contract_address": "0x55d398326f99059fF775485246999027B3197955"
                },
                {
                    "name": "USDC",
                    "contract_address": "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d"
                }
            ]
        },
        {
            "chain": "KAIA",
            "chain_id": "8217",
            "deposit_address": "0xYourKaiaDepositAddress",
            "supported_tokens": [
                {
                    "name": "USDT",
                    "contract_address": "0xd077a400968890eacc75cdc901f0356c943e4fdb"
                }
            ]
        },
        {
            "chain": "TRON",
            "chain_id": "tron",
            "deposit_address": "TYourTronDepositAddress",
            "supported_tokens": [
                {
                    "name": "USDT",
                    "contract_address": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t"
                }
            ]
        },
        {
            "chain": "SOLANA",
            "chain_id": "solana",
            "deposit_address": "",
            "supported_tokens": [
                {
                    "name": "USDT",
                    "contract_address": "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"
                },
                {
                    "name": "USDC",
                    "contract_address": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
                }
            ]
        }
    ]
}

On-demand chains

Some chains (e.g. SOLANA) are on-demand only. They are not automatically registered for wallet generation, so they are returned with an empty deposit_address until a deposit address is provisioned for the user. The chain and its supported tokens are still listed so clients can surface them in the UI.

Token contract addresses

Token contract_address values are resolved dynamically from the bridge provider's configuration. If the bridge provider cannot be reached, a chain's tokens may be returned with empty contract_address values.

Chain names

The chain value identifies the chain across every GRVT API — pass it back exactly as returned whenever a request takes a chain. chain_id and chain_type are provided for reference only.

Generate Deposit Address

Provisions the deposit address for a single chain and returns it in the same shape as one entry of Get Deposit Addresses.

Some chains are provisioned on demand rather than up front — see Appendix: Supported Chains for which ones. Call this endpoint to provision a deposit address on such a chain. It is idempotent: calling it for a chain that already has an address returns that address.

Endpoint

# staging
GRVT_DEPOSIT_ADDRESS_ENDPOINT="https://edge.staging.gravitymarkets.io/api/v1/deposit/generate-address"
# testnet
GRVT_DEPOSIT_ADDRESS_ENDPOINT="https://edge.testnet.grvt.io/api/v1/deposit/generate-address"
# prod
GRVT_DEPOSIT_ADDRESS_ENDPOINT="https://edge.grvt.io/api/v1/deposit/generate-address"
Method Path
POST /api/v1/deposit/generate-address

Authentication

Same as Get Deposit Addresses — either an X-API-Key header or a session cookie (gravity=...) plus the X-Grvt-Account-Id header. See the Authentication page.

Request Parameters

Field Type Required Description
chain string True The GRVT chain name to provision, as listed in Appendix: Supported Chains (e.g. SOLANA). Case-insensitive.

Example Request

GRVT_API_KEY="<insert_key_here>"

curl "$GRVT_DEPOSIT_ADDRESS_ENDPOINT" \
    -X POST \
    -H "X-API-Key: $GRVT_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"chain": "SOLANA"}' \
    -s

Response Fields

A single ChainInfo object, with the same fields as an entry of Get Deposit Addresses.

Field Type Description
chain string The GRVT chain name.
chain_id string The native chain ID, derived from chain.
deposit_address string The on-chain address to send deposits to for this chain.
supported_tokens array of TokenInfo The tokens that can be deposited on this chain.

Example Response

{
    "chain": "SOLANA",
    "chain_id": "solana",
    "deposit_address": "7CyChDU4JfcL6iYxHFF9vD83mcEdS1H9SGrFtPe6mY5G",
    "supported_tokens": [
        {
            "name": "USDT",
            "contract_address": "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"
        },
        {
            "name": "USDC",
            "contract_address": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
        }
    ]
}

Unsupported chains

A chain that is not a supported GRVT chain name, or one that is not enabled for the account, is rejected with a 400 error. See Appendix: Supported Chains for the chains available in each environment.

Withdraw via Non-Native Bridge

Withdraw funds from a GRVT funding account directly to an external wallet on another chain, using GRVT's non-native bridge — a third-party bridge provider that pays the user out of its own liquidity. It is faster and cheaper than a native withdrawal, which settles through GRVT's own on-chain bridge.

This is a quote-then-execute flow: request a firm quote, sign a standard GRVT Transfer message for the withdrawal amount, submit the two together, then poll for completion.

Step Endpoint Purpose
1. Discover GET /api/v1/bridge/withdrawal-info Supported routes and the bridge funding address to sign toward.
2. Quote POST /api/v1/bridge/withdrawal-quote Returns a quote_id with a fixed fee and receive amount.
3. Sign (local) Sign an EIP-712 Transfer message for the amount → bridge funding address.
4. Execute POST /api/v1/bridge/withdraw Submit quote_id + signature. Returns 202 Accepted.
5. Track GET /api/v1/bridge/withdrawal-status Poll until COMPLETED (or FAILED / REFUNDED).

The quote_id is the withdrawal's identifier across steps 2–5.

Authentication

All bridge withdrawal endpoints require a session cookie, obtained from the API Key Login endpoint. Send the resulting gravity=... cookie and the X-Grvt-Account-Id header on every request.

Session cookie required

Unlike the deposit endpoints, an X-API-Key header alone is not sufficient here — executing a withdrawal authenticates the underlying transfer against the trading engine using the session cookie. Log in first via API Key Login and reuse the returned cookie for all four endpoints.

Prerequisites

  • The destination address must already be registered in the account's withdrawal address book (a one-time setup step via the GRVT UI or API, which may require 2FA).
  • The signing wallet must be a main-account signer with the External Transfer or Funding Admin permission. For programmatic use this is typically the signer wallet of an account-level API key created with the External Transfer permission; the account owner's own wallet also works. Sub-account (trading) API keys and session keys cannot sign withdrawals.
  • All destination addresses are screened (KYA) at execution time; withdrawals to flagged addresses are rejected.

Withdrawal Info

GET /api/v1/bridge/withdrawal-info

Returns the bridge provider's funding address (the transfer destination when signing) and the available withdrawal routes per source token. Takes no parameters.

Response Fields

Field Type Description
provider string Identifier of the bridge provider serving the payout.
funding_address string Bridge funding address — the toAccount of the signed Transfer message (see Signing the Transfer Message).
route object Available routes, keyed by source token (token_in). A source token with no available route is present with an empty array.
route.<token_in>[].chain string Destination chain name; use as chain in the quote request.
route.<token_in>[].chain_id string Chain id of the destination network (derived from chain).
route.<token_in>[].chain_type string evm | solana | tron — must match the destination address type.
route.<token_in>[].tokens_out string[] Payout tokens available on that chain for this token_in.

Example Response

{
    "provider": "RHINO",
    "funding_address": "0xYourBridgeFundingAddress",
    "route": {
        "USDT": [
            { "chain": "ARBITRUM", "chain_id": "42161", "chain_type": "evm", "tokens_out": ["USDT", "USDC"] },
            { "chain": "BSC",      "chain_id": "56",    "chain_type": "evm", "tokens_out": ["USDT"] },
            { "chain": "SOLANA",   "chain_id": "solana","chain_type": "solana", "tokens_out": ["USDT", "USDC"] }
        ],
        "USDC": []
    }
}

Source token

token_in is currently USDT only. The USDC key is present with an empty array so the response shape does not change when it is enabled.

Create Withdrawal Quote

POST /api/v1/bridge/withdrawal-quote

Creates a firm quote. The fee and receive amount are locked to the quote until it expires (~1 minute). Creating a quote has no side effects — unused quotes simply expire.

Request Fields

Field Type Required Description
to_address string True Destination wallet. Must be in the caller's withdrawal address book.
chain string True Destination chain, from Withdrawal Info (e.g. ARBITRUM).
token_in string True Token debited from the funding account. Currently USDT.
token_out string True Token received on the destination chain. May differ from token_in (conversion handled by the bridge).
amount string True Amount of token_in to withdraw, as a decimal string.

Example Request

{
    "to_address": "0xA1b2C3d4E5f6A7b8C9d0E1f2A3b4C5d6E7f8A9b0",
    "chain": "ARBITRUM",
    "token_in": "USDT",
    "token_out": "USDT",
    "amount": "1000"
}

Response Fields

Field Type Description
quote_id string Quote identifier; used in withdraw and for status polling.
pay_amount string Amount of token_in debited from the funding account.
receive_amount string Amount of token_out paid out on the destination chain.
fee string Bridge fee, denominated in token_in.
fee_usd string Fee expressed in USD (informational).
speed string fast (normal) or slow (bridge temporarily congested; longer settlement).
expires_at string Quote expiry, ISO-8601. Execute before this time.

Example Response

{
    "quote_id": "6a7d8c6f61855ec660c84db8",
    "pay_amount": "1000",
    "receive_amount": "997.8",
    "fee": "2.2",
    "fee_usd": "2.2",
    "speed": "fast",
    "expires_at": "2026-08-13T09:23:47Z"
}

Quote failures

A quote is rejected (4xx) when the route is not supported, the amount is below the bridge fee, or a withdrawal limit has been reached.

Execute Withdrawal

POST /api/v1/bridge/withdraw

Executes a previously created quote. The request carries the quote_id and an EIP-712 signature over a GRVT Transfer message (see Signing the Transfer Message). The signature object has the same shape as the standard GRVT transfer API, so existing signing code can be reused as-is. On success the funds leave the funding account immediately and the bridge payout begins.

Request Fields

Field Type Required Description
quote_id string True From the quote. Must not be expired.
signature.signer string True Wallet address signing the message. Must be a main-account signer with External Transfer or Funding Admin permission.
signature.r string True Signature R (hex).
signature.s string True Signature S (hex).
signature.v integer True Signature V (27 or 28).
signature.expiration string True Signature expiry, unix nanoseconds (string). Capped at 30 days.
signature.nonce integer True Random uint32 used in the signed message. See the idempotency note below.
signature.chain_id integer False EIP-712 domain chain id. Omit or 0 to use the GRVT chain id for the environment.

Example Request

{
    "quote_id": "6a7d8c6f61855ec660c84db8",
    "signature": {
        "signer": "0x9fE4000000000000000000000000000000007b21",
        "r": "0xb788d96f...",
        "s": "0x3d2c1e9a...",
        "v": 28,
        "expiration": "1754971200000000000",
        "nonce": 828283
    }
}

Response Fields

Field Type Description
quote_id string Withdrawal identifier (same value as the executed quote); use for status polling.
tx_id string GRVT transfer id, for cross-referencing transfer history.
state string Always SUBMITTED on acceptance.
receive_amount string Locked receive amount of token_out, echoed from the quote.
fee string Locked fee in token_in, echoed from the quote.
fee_usd string Fee in USD (informational).

Example Response202 Accepted

{
    "quote_id": "6a7d8c6f61855ec660c84db8",
    "tx_id": "214491152",
    "state": "SUBMITTED",
    "receive_amount": "997.8",
    "fee": "2.2",
    "fee_usd": "2.2"
}

Common failures

An expired quote must be re-quoted and retried. Execution is also rejected when the destination is not in the address book, KYA screening flags the address, the balance is insufficient, a daily limit is exceeded, or the signature does not match the quoted amount/account.

Withdrawal Status

GET /api/v1/bridge/withdrawal-status?quote_id=<quote_id>

Returns the current state of a withdrawal, addressed by the same quote_id used across the flow. Poll every few seconds until a terminal state.

Query Parameters

Field Type Required Description
quote_id string True The withdrawal identifier returned by the quote / withdraw calls.

Response Fields

Field Type Description
quote_id string Withdrawal identifier.
tx_id string GRVT transfer id.
state string CREATED | SUBMITTED | SENDING | COMPLETED | FAILED | REFUNDED (see lifecycle below).
chain string Destination chain.
token_out string Payout token.
receive_amount string Locked receive amount of token_out.
withdraw_tx_hash string Destination-chain payout transaction hash; set once COMPLETED.
updated_at string Last state change, ISO-8601.

Example Response

{
    "quote_id": "6a7d8c6f61855ec660c84db8",
    "tx_id": "214491152",
    "state": "SENDING",
    "chain": "ARBITRUM",
    "token_out": "USDT",
    "receive_amount": "997.8",
    "withdraw_tx_hash": "0xYourPayoutTxHash",
    "updated_at": "2026-08-13T09:24:05Z"
}

State lifecycle

  • CREATED — quote created but not yet executed.
  • SUBMITTED — funds debited on GRVT; bridge processing the withdrawal.
  • SENDING — bridge accepted the withdrawal; payout transaction in flight.
  • COMPLETED — payout confirmed on the destination chain (withdraw_tx_hash set).
  • FAILED / REFUNDED — terminal failure states; contact support with the quote_id if unclear.

Unknown identifiers

An unknown quote_id, or one belonging to another account, returns 404.

Signing the Transfer Message

The withdrawal is authorized by the same EIP-712 Transfer message and Signature object used by the standard GRVT transfer API — existing transfer signing code works unchanged. The destination (toAccount) is the funding_address from Withdrawal Info. The signature does not reference the quote — it authorizes the movement of funds; the quote fixes the price.

This withdrawal is executed as an external transfer, so the External Transfer permission applies (not the Withdraw permission used by native on-chain withdrawals).

Transfer message fields

Field Type Description
fromAccount address Your main (funding) account address.
fromSubAccount uint 0 — withdrawals are from the funding account.
toAccount address funding_address from Withdrawal Info.
toSubAccount uint 0.
tokenCurrency uint GRVT currency enum id of token_in (e.g. 3 for USDT).
numTokens uint Amount in raw units (USDT/USDC: 6 decimals). Must equal the quote's pay_amount.
expiration int64 Signature deadline, unix-nanosecond timestamp (e.g. now + 24h).
nonce uint32 Random 32-bit value, unique per withdrawal.

Chain ID

The EIP-712 domain.chainId must be the GRVT L2 Chain ID for your target environment (the same value used elsewhere in this guide). Refer to the Chain IDs table.

Integration Notes

  • Amounts: all amounts (token and USD) are decimal strings to preserve precision — do not parse them as floating-point numbers.
  • Quote lifetime: quotes expire in roughly one minute. Sign and call withdraw promptly after quoting; on an expired quote simply create a new one (pricing may change slightly) and retry.
  • Idempotency: reuse the same signature/nonce when retrying a timed-out withdraw call for the same quote_id; resubmitting with the same nonce is treated as a duplicate and ignored, whereas a new nonce would be treated as a new withdrawal.
  • Timing: payouts normally complete in under 5 minutes. A quote with speed = "slow" indicates temporary congestion and longer settlement.
  • Limits: withdrawals are subject to per-account daily limits and bridge liquidity limits, enforced at both quote and execution time.

Appendix: Supported Chains

The chains GRVT supports per environment. Pass these names as chain wherever an API takes one.

Query at runtime

This list is configuration and can change without a doc update. Treat Get Deposit Addresses and Withdrawal Info as the authoritative source for what an account can use right now, including the tokens available on each chain.

Production

Chain Note
ARBITRUM
BASE
BSC
KAIA
POLYGON
TRON
SOLANA No deposit address is generated by Get Deposit Addresses — call Generate Deposit Address to provision one.

Testnet

Chain Note
BASE_SEPOLIA
BSC_TEST
KAIA_TEST
POLYGON_TEST
TRON_TEST
SOLANA_TEST No deposit address is generated by Get Deposit Addresses — call Generate Deposit Address to provision one.