On/Off-Ramp, and cross-chain Swap via CeFi


📘

Beta Feature

The Trading API is currently in beta and subject to change. For participation details, contact your Fireblocks Customer Success Manager or email [email protected].

This guide provides step-by-step examples of how to execute various order operations using the Fireblocks Trading API via account-based providers. These require you to connect an account before trading. For instructions on how to connect accounts, see Fireblocks Connected Accounts. This guide covers multiple use cases including on-ramp (fiat to crypto), off-ramp (crypto to fiat), and bridging (crypto-to-crypto).

Prerequisites

Before you begin, ensure you have completed the prerequisites outlined in the Trading API Overview:

  • ✅ Fireblocks API credentials configured
  • ✅ Fireblocks TypeScript SDK installed (@fireblocks/version 13.x or later)
  • ✅ Connected account(s) set up with your chosen provider(s) via the Fireblocks Console (see Fireblocks Connected Accounts for setup instructions)

SDK Setup

First, set up your Fireblocks SDK client:

import { Fireblocks } from '@fireblocks/ts-sdk';
import * as fs from 'fs';

// Initialize the Fireblocks SDK
const fireblocks = new Fireblocks({
  apiKey: 'your-api-key',
  secretKey: fs.readFileSync('/path/to/your/secret.key', 'utf8'),
  basePath: 'https://api.fireblocks.io',
});

Provider and account discovery

Before trading with account-based providers, find out which providers are available and which accounts you have connected. If you haven't connected any accounts yet, see Fireblocks Connected Accounts for setup instructions.

Step 1: Get Providers and Connected Accounts

Retrieve all trading providers. For account-based providers, the response includes provider information and connected accounts, allowing you to extract both providerId and accountId in a single call.

// Get all trading providers
const response = await fireblocks.trading.getTradingProviders({ pageSize: 20 });

// Filter for account-based providers
const accountBasedProviders = response.data.data.filter(p => p.accountBased);

// Access provider and account information
for (const provider of accountBasedProviders) {
  console.log(`Provider: ${provider.id} (${provider.name})`);
  provider.accounts?.forEach(account => {
    console.log(`  Account: ${account.id} (${account.name})`);
  });
}

Response Example

{
  "data": [
    {
      "id": "ALFREDPAY",
      "name": "Alfred Pay",
      "accountBased": true,
      "connected": true,
      "accounts": [
        {
          "id": "acc_5e9a2d1c4b7f3e8a",
          "name": "My Alfred Pay Account"
        }
      ],
      "manifest": {
        "assetTypes": ["FIAT", "DIGITAL"],
        "capabilities": ["TRADING"]
      }
    },
    {
      "id": "YELLOWCARD",
      "name": "Yellow Card",
      "accountBased": true,
      "connected": true,
      "accounts": [
        {
          "id": "acc_9f4e2d8b1c6a5e73",
          "name": "Yellow Card Main Account"
        }
      ],
      "manifest": {
        "assetTypes": ["FIAT", "DIGITAL"],
        "capabilities": ["TRADING"]
      }
    }
  ],
  "total": 2,
  "next": null
}

Key Fields Explained

FieldDescription
idUnique identifier for the provider (use as providerId in subsequent API calls)
nameDisplay name of the provider
accountBasedtrue for account-based providers, false for direct access providers
connectedWhether you have at least one connected account with this provider
accountsArray of connected accounts for this provider. Each account has an id (use as accountId in subsequent API calls) and name
📖

For more details about this endpoint, see the Get Providers API Reference.

Step 2: Get Supported Trading Pairs

For each connected account, discover which trading pairs are supported:

// Get supported trading pairs for a specific connected account
const response = await fireblocks.trading.getTradingPairs({
  accountId: 'acc_9f4e2d8b1c6a5e73',
});

// Response data
const tradingPairs = response.data;
// tradingPairs = [
//   {
//     "id": "7c9e8f2a-4b5d-4e1c-9a3b-6f8d2e5c7a1b",
//     "toAsset": "USDC",
//     "fromAsset": "USD",
//     "prefunded": false,
//     "fromAssetRail": "ACH"
//   }
// ]

Step 3: Get Indicative Rates

Get indicative rates for price preview (these are not executable quotes):

// Get indicative rates
const response = await fireblocks.trading.getRates({
  accountId: 'acc_9f4e2d8b1c6a5e73',
  baseAssetId: 'USD',
  quoteAssetId: 'BTC',
});
const rates = response.data;
📖

Note: Rates provide indicative prices for preview, while quotes return committed rates that can be executed until expiration.


Clarification on how to translating user intent into baseAssetId, quoteAssetId, side, and baseAmount

When creating a Quote or an Order, you express the trade using:

  • baseAssetId: the asset you receive on BUY / give on SELL
  • quoteAssetId: the counter asset used to pay/receive
  • side:
    • BUY: receive base / pay quote
    • SELL: give base / receive quote
  • baseAmount: amount in baseAssetId
    • BUY: base amount to receive
    • SELL: base amount to sell

Examples

IntentbaseAssetIdquoteAssetIdsidebaseAmount
Buying 100 BTC with USDBTCUSDBUY100
Buying 100 USD with BTCUSDBTCBUY100
Selling 100 BTC for USDBTCUSDSELL100
Selling 100 USD for BTCUSDBTCSELL100

Participants & compliance

Participant details vary by provider and rail. The participantsIdentification object defines the originator and beneficiary involved in an order.

Example structure

{
  "participantsIdentification": {
    "originator": {
      "externalReferenceId": "user_123",
      "participantRelationshipType": "FirstParty",
      "entityType": "INDIVIDUAL"
    },
    "beneficiary": {
      "participantRelationshipType": "ThirdParty",
      "fullName": {
        "firstName": "Alice",
        "lastName": "Lee"
      }
    }
  }
}

Key concepts

  • First-party: Acting for self — provider already holds KYC; minimal data required.
  • Third-party: Acting for another — provider may require full PII.

The level of PII data required depends on the provider and the relationship type (first-party vs. third-party).

Use Case Examples

The following sections demonstrate different trading use cases with account-based providers:


Use Case 1: On-Ramp - DVP Settlement with Market Execution

Use Case: Convert fiat to crypto at the market rate. The user creates the order, receives payment instructions from the provider, deposits the funds, and once payment is confirmed, the provider executes and settles the crypto, ensuring delivery versus payment (DVP).

Settlement Type: DVP (Delivery vs Payment)
Execution Type: Market

📖

Note: The baseAssetId, baseAssetRail, quoteAssetId, and quoteAssetRail values used in the order must match a supported trading pair returned from the getTradingPairs endpoint (see Step 2 in Getting Started). This ensures the provider supports the specific asset and rail combination you're requesting.

Step 1: Create the Order

// Create on-ramp order with DVP settlement and market execution
const response = await fireblocks.trading.createOrder({
  createOrderRequest: {
    via: {
      type: 'PROVIDER_ACCOUNT',
      providerId: 'bridge-provider-001',
      accountId: 'acc_9f4e2d8b1c6a5e73',
    },
    executionRequestDetails: {
      type: 'MARKET',
      side: 'BUY',
      baseAmount: '1000',
      baseAssetId: 'USD',
      baseAssetRail: 'ACH',
      quoteAssetId: 'USDC',
      quoteAssetRail: 'BLOCKCHAIN',
    },
    settlement: {
      type: 'DVP',
      sourceAccount: {
        type: 'EXTERNAL',
      },
      destinationAccount: {
        type: 'ONE_TIME_ADDRESS',
        address: '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa',
      },
    },
    participantsIdentification: {
      originator: {
        entityType: 'BUSINESS',
        participantRelationshipType: 'FirstParty',
      },
      beneficiary: {
        entityType: 'INDIVIDUAL',
        participantRelationshipType: 'ThirdParty',
        fullName: {
          lastName: 'Johnson',
          firstName: 'Alexander',
        },
        postalAddress: {
          streetName: 'Fifth Avenue',
          buildingNumber: '350',
          postalCode: '10118',
          city: 'New York',
          subdivision: 'NY',
          district: 'Manhattan',
          country: 'US',
        },
        externalReferenceId: 'person_ref_7f3e2d1c4b8a5e9f',
        dateOfBirth: '1985-03-15',
      },
    },
    customerInternalReferenceId: '32e423',
  },
  idempotencyKey: `order-${Date.now()}`,
});

const order = response.data;

Step 2: Handle Payment Instructions

Since the settlement type is DVP and the source type is EXTERNAL, the funds will move from outside Fireblocks. The order creation response will include payment instructions for the rail which you select (e.g., ACH, SEPA, Wire):

{
  "paymentInstructions": {
    "referenceId": "REF-ACH-001",
    "type": "ACH",
    "address": {
      "accountHolder": {
        "name": "Bridge Provider Ltd.",
        "address": "123 Main Street",
        "city": "New York",
        "country": "US",
        "subdivision": "NY",
        "postalCode": "10001"
      },
      "bankName": "JPMorgan Chase Bank N.A.",
      "bankAccountNumber": "9876543210",
      "routingNumber": "021000021",
      "accountType": "CHECKING"
    }
  }
}

Considerations

  • For third-party payouts, like the one in the example, the provider must receive the beneficiary details (PII as required by the provider). In this case, you are the originator — acting as a first party, already KYC'd with the provider. Therefore, the only data needed is to set the beneficiary.participantRelationshipType = "FirstParty".
  • Since the settlement type is DVP and the source type is EXTERNAL, the funds will move from outside Fireblocks. As a result, the order creation response will include payment instructions for the rail which you select (e.g., ACH, SEPA, Wire).

Use Case 2: On-Ramp - Prefunded settlement with quote execution

Use Case: Convert fiat to crypto at a locked rate from a committed quote. Funds are already held in the account; no payment instructions are returned in the response, and the provider will execute and settle the request immediately.

Settlement Type: Prefunded
Execution Type: Quote

Step 1: Create a Quote

// Create a quote for on-ramp
const quoteResponse = await fireblocks.trading.createQuote({
  createQuote: {
    scope: [
      {
        providerId: 'SCRYPT',
        accountId: 'scrypt_acc_5e9a2d1c4b7f3e8a',
      },
    ],
    side: 'BUY',
    baseAssetId: 'USDT_ERC20',
    quoteAssetId: 'TRX_USDT_S2UZ',
    baseAmount: '15000',
  },
  idempotencyKey: `quote-${Date.now()}`,
});

const quote = quoteResponse.data.quotes[0];

In the quote response, you will receive all related metadata, including the quote ID (id) and expiration time (expiresAt). The quote ID is then used in the next step to create an order and execute the trade.

Step 2: Execute order with quote

// Execute order using the quote from Step 1
const response = await fireblocks.trading.createOrder({
  createOrderRequest: {
    via: {
      type: 'PROVIDER_ACCOUNT',
      accountId: 'acc_5e9a2d1c4b7f3e8a',
      providerId: 'ALFREDPAY',
    },
    executionRequestDetails: {
      type: 'QUOTE',
      reQuote: {
        type: 'MARKET',
      },
      quoteId: 'quote_8f2e4d1a9c5b7e3f',
    },
    settlement: {
      type: 'PREFUNDED',
      destinationAccount: {
        type: 'VAULT_ACCOUNT',
        accountId: 'vault_acc_9f3e2d1c4b8a7e5f',
      },
    },
    participantsIdentification: {
      originator: {
        entityType: 'INDIVIDUAL',
        participantRelationshipType: 'FirstParty',
      },
      beneficiary: {
        entityType: 'INDIVIDUAL',
        participantRelationshipType: 'FirstParty',
      },
    },
  },
  idempotencyKey: `order-${Date.now()}`,
});

const order = response.data;

Considerations

  • In this example, you perform an on-ramp for yourself, not for a third-party recipient. Therefore, both the originator and beneficiary use "participantRelationshipType": "FirstParty", and no additional PII data is required.
  • The quoteId used in the order request is the same ID returned in the response from the POST /trading/quotes call.

Use Case 3: Off-Ramp - DVP settlement with market execution

Use Case: Convert crypto to fiat at the market rate. The provider waits for confirmation of the crypto deposits before finalizing the settlement, ensuring delivery versus payment (DVP).

Settlement Type: DVP
Execution Type: Market

📖

Note: The baseAssetId, baseAssetRail, quoteAssetId, and quoteAssetRail values used in the order must match a supported trading pair returned from the getTradingPairs endpoint (see Step 2 in Getting Started). This ensures the provider supports the specific asset and rail combination you're requesting.

Step 1: Create the Order

// Create off-ramp order with DVP settlement and market execution
const response = await fireblocks.trading.createOrder({
  createOrderRequest: {
    via: {
      type: 'PROVIDER_ACCOUNT',
      providerId: 'YELLOWCARD',
      accountId: 'acc_9f4e2d8b1c6a5e73',
    },
    executionRequestDetails: {
      type: 'MARKET',
      side: 'BUY',
      baseAmount: '250',
      baseAssetId: 'XOF',
      baseAssetRail: 'LOCAL_BANK_TRANSFER_AFRICA',
      quoteAssetId: 'USDT_ERC20',
      quoteAssetRail: 'BLOCKCHAIN',
    },
    settlement: {
      type: 'DVP',
      sourceAccount: {
        type: 'VAULT_ACCOUNT',
        accountId: 'vault_acc_5e9a2d1c4b7f3e8a',
      },
      destinationAccount: {
        type: 'EXTERNAL_WALLET',
        accountId: 'fiat_whitelisted_acc_9f3e2d1c4b8a7e5f',
      },
    },
    participantsIdentification: {
      originator: {
        entityType: 'INDIVIDUAL',
        participantRelationshipType: 'FirstParty',
      },
      beneficiary: {
        entityType: 'INDIVIDUAL',
        participantRelationshipType: 'FirstParty',
      },
    },
  },
  idempotencyKey: `order-${Date.now()}`,
});

const order = response.data;

Considerations

  • In this example, you perform an off-ramp for yourself, not for a third-party recipient. Therefore, both the originator and beneficiary use "participantRelationshipType": "FirstParty", and no additional PII data is required.
  • In this example, you chose to use a whitelisted account as the destination fiat account type. This allows you to register and manage external bank accounts (and also crypto addresses) directly within Fireblocks, ensuring secure, verified destinations that can be safely reused for future orders and transactions.
  • Since the source of this order is a Vault Account, once the provider receives the order request, a transfer request will be automatically created from your vault to the provider. You will then need to approve and sign this transfer in accordance with the workspace's policy rules before the order can be executed.

Use Case 4: Bridging (Crypto-to-Crypto) - DVP settlement with quote execution

Use Case: Execute a cross-chain bridging operation through the provider's connected accounts. In this flow, the user swaps USDT on Ethereum for USDT on Tron using their Scrypt provider account.

Settlement Type: DVP
Execution Type: Quote

Step 1: Create a Quote

// Create a quote for crypto-to-crypto bridging
const quoteResponse = await fireblocks.trading.createQuote({
  createQuote: {
    scope: [
      {
        providerId: 'SCRYPT',
        accountId: 'scrypt_acc_5e9a2d1c4b7f3e8a',
      },
    ],
    side: 'BUY',
    baseAssetId: 'USDT_ERC20',
    quoteAssetId: 'TRX_USDT_S2UZ',
    baseAmount: '15000',
  },
  idempotencyKey: `quote-${Date.now()}`,
});

const quote = quoteResponse.data.quotes[0];

In the quote response, you will receive all related metadata, including the quote ID (id) and expiration time (expiresAt). The quote ID is then used in the next step to create an order and execute the trade.

Step 2: Execute Order with Quote

// Execute bridging order using the quote from Step 1
const response = await fireblocks.trading.createOrder({
  createOrderRequest: {
    via: {
      type: 'PROVIDER_ACCOUNT',
      providerId: 'SCRYPT',
      accountId: 'scrypt_acc_5e9a2d1c4b7f3e8a',
    },
    executionRequestDetails: {
      type: 'QUOTE',
      reQuote: {
        type: 'MARKET',
      },
      quoteId: 'quote_khge4d1a9c5bmvby',
    },
    settlement: {
      type: 'DVP',
      sourceAccount: {
        type: 'VAULT_ACCOUNT',
        accountId: 'vault_acc_5e9a2d1c4b7f3e8a',
      },
      destinationAccount: {
        type: 'VAULT_ACCOUNT',
        accountId: 'vault_acc_5e9a2d1c4b7f3e8a',
      },
    },
    participantsIdentification: {
      originator: {
        entityType: 'INDIVIDUAL',
        participantRelationshipType: 'FirstParty',
      },
      beneficiary: {
        entityType: 'INDIVIDUAL',
        participantRelationshipType: 'FirstParty',
      },
    },
  },
  idempotencyKey: `order-${Date.now()}`,
});

const order = response.data;

Considerations

  • In this example, you perform bridging via a connected account for yourself, not for a third-party recipient. Therefore, both the originator and beneficiary use "participantRelationshipType": "FirstParty", and no additional PII data is required.
  • The quoteId used in the order request is the same ID returned in the response from the POST /trading/quotescall.
  • Since the source of this order is a Vault Account, once the provider receives the order request, a transfer will automatically be initiated from your Vault Account to the provider's connected account on Scrypt. This transfer is governed by the workspace's Transfer Policy, just like any other transfer in Fireblocks.
  • ReQuote Option: Instead of "type": "MARKET", you can use "type": "RETRY" with count (1-10 retry attempts) and optional slippageBps (slippage tolerance in basis points) to automatically retry quote generation if the original quote expires, giving you more control over price execution.

Order Tracking & Monitoring

After creating an order, monitor its execution status to track completion.

Polling for order status

// Get order status and details
const response = await fireblocks.trading.getOrder({
  orderId: 'order_id_here',
});

const order = response.data;
// Check: order.status, order.executionSteps, order.createdAt, etc.

Order Status Values

StatusDescription
CREATEDOrder has been created and is being processed
AWAITING_PAYMENTWaiting for payment to be received
PENDING_USER_ACTIONRequires user action (e.g., approval in console)
PROCESSINGOrder is being executed
COMPLETEDOrder has been successfully completed
FAILEDOrder execution failed
CANCELEDOrder was canceled

📖

Note: FAILED, COMPLETED and CANCELED are terminal states.

Webhooks

Subscribe to order-update events through the Fireblocks Webhooks API. See the Fireblocks Webhooks API documentation for more details.


Additional Resources


Support

For questions or issues with the Trading API: