SDigital2SDigital2

Off-Ramp Flow (Crypto to Fiat)

Convert cryptocurrency (USDT/USDC) to Brazilian Real (BRL) and send funds via PIX

Overview

There are two ways to create off-ramp transactions:

  1. With Exchange Rate Quote - Lock in rates for price certainty
  2. Without Quote (Market Rate) - Use market rates at execution time

Method 1: With Exchange Rate Quote

This method provides rate certainty by locking in exchange rates before creating the transaction.

Flow Overview

Step 1: List Your Customers

First, retrieve the list of customers associated with your organization:

curl -X GET "https://api.sdigital2.com/v1/customers" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json"

Response:

{
  "data": [
    {
      "id": "774f7a53-fd45-4634-9548-eb37c9554137",
      "name": "John Doe",
      "type": "INDIVIDUAL",
      "createdAt": "2025-09-08T07:01:04.093Z",
      "updatedAt": "2025-09-08T07:01:04.084Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 10
  }
}

Step 2: Get Customer Details and Balances

Get detailed information about a specific customer, including their wallet addresses and current balances:

curl -X GET "https://api.sdigital2.com/v1/customers/{customer_id}" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json"

Response:

{
  "id": "774f7a53-fd45-4634-9548-eb37c9554137",
  "name": "John Doe",
  "type": "INDIVIDUAL",
  "createdAt": "2025-09-08T07:01:04.093Z",
  "updatedAt": "2025-09-08T07:01:04.084Z",
  "walletAddresses": [
    {
      "chain": "ARBITRUM",
      "walletAddress": "0xebe39cfda54886b2db0d4e7e4c63a4ccbc880f84",
      "balances": [
        {
          "currency": "USDC",
          "amount": "0",
          "updatedAt": "2025-09-12T22:56:00.648Z"
        },
        {
          "currency": "USDT",
          "amount": "0",
          "updatedAt": "2025-09-12T22:56:00.648Z"
        }
      ]
    },
    {
      "chain": "BASE",
      "walletAddress": "0x2fc4512d5859226cf0bf61fbbbdb5444fda60053",
      "balances": [
        {
          "currency": "USDC",
          "amount": "215.462157",
          "updatedAt": "2025-09-12T22:56:00.566Z"
        }
      ]
    },
    {
      "chain": "ETHEREUM",
      "walletAddress": "0xba1c64834804eac723776aa6addf216d307eb336",
      "balances": [
        {
          "currency": "USDC",
          "amount": "398.770344",
          "updatedAt": "2025-09-12T22:56:00.648Z"
        },
        {
          "currency": "USDT",
          "amount": "0",
          "updatedAt": "2025-09-12T22:56:00.648Z"
        }
      ]
    },
    {
      "chain": "POLYGON",
      "walletAddress": "0xbc87a12b0e9570bb5472d6066f220db7e98b709e",
      "balances": [
        {
          "currency": "USDC",
          "amount": "0",
          "updatedAt": "2025-09-12T22:56:00.594Z"
        },
        {
          "currency": "USDT",
          "amount": "0",
          "updatedAt": "2025-09-12T22:56:00.594Z"
        }
      ]
    }
  ]
}

Verify the customer has sufficient balance in the desired cryptocurrency before proceeding.

Step 3: Get Exchange Rate Quote

Request a locked exchange rate for the conversion. This ensures price certainty:

curl -X POST "https://api.sdigital2.com/v1/exchange-rates" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "chain": "ETHEREUM",
    "customerId": "774f7a53-fd45-4634-9548-eb37c9554137",
    "sourceCurrency": "USDC",
    "sourceAmount": "100.00",
    "destinationCurrency": "BRL",
    "lockDuration": "30s"
  }'

Response:

{
  "id": "faa8ee1b-a422-4ef0-bbd8-4e5c597bb218",
  "sourceCurrency": "USDC",
  "sourceAmount": "100.000000",
  "destinationCurrency": "BRL",
  "destinationAmount": "524.19",
  "grossDestinationAmount": "535.16",
  "baseExchangeRate": "5.351638",
  "appliedExchangeRate": "5.241930",
  "fees": {
    "bps": "105",
    "flatAmount": "1",
    "totalAmount": "2.05",
    "currency": "USDC"
  },
  "expiresAt": "2025-09-13T19:50:32.591Z"
}

Exchange rate quotes expire! Use the expiresAt timestamp to ensure you create the transaction before expiration.

Step 3: Create or Get Bank Account

Before creating an off-ramp transaction, you need a verified bank account. Create one if you don't have it:

curl -X POST "https://api.sdigital2.com/v1/bank-accounts" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customerId": "774f7a53-fd45-4634-9548-eb37c9554137",
    "name": "Main Business Account",
    "type": "EXTERNAL",
    "rail": "PIX",
    "currency": "BRL",
    "beneficiary": {
      "relationship": "SELF",
      "legalName": "John Doe",
      "taxId": "12345678900",
      "email": "john.doe@example.com",
      "incorporationDate": "2024-01-01",
      "address": {
        "line1": "123 Main Street",
        "city": "São Paulo",
        "state": "SP",
        "postalCode": "01310-100",
        "country": "BR"
      }
    },
    "accountDetails": {
      "keyType": "EMAIL",
      "keyValue": "john.doe@example.com"
    }
  }'

Response:

{
  "id": "3190d115-987f-402f-b558-0316d3a22ebd",
  "customerId": "774f7a53-fd45-4634-9548-eb37c9554137",
  "name": "Main Business Account",
  "type": "EXTERNAL",
  "rail": "PIX",
  "currency": "BRL",
  "beneficiary": {
    "relationship": "SELF",
    "legalName": "John Doe",
    "taxId": "12345678900",
    "email": "john.doe@example.com",
    "incorporationDate": "2024-01-01",
    "address": {
      "line1": "123 Main Street",
      "city": "São Paulo",
      "state": "SP",
      "postalCode": "01310-100",
      "country": "BR"
    }
  },
  "accountDetails": {
    "keyType": "EMAIL",
    "keyValue": "john.doe@example.com"
  },
  "verification": {
    "status": "APPROVED",
    "level": "BASIC"
  },
  "createdAt": "2025-09-13T10:00:00.000Z",
  "updatedAt": "2025-09-13T10:00:00.000Z"
}

Important: Ensure the bank account is verified (status: APPROVED) before using it in transactions. Monitor the verification status and wait for approval.

Step 4: Create Transaction with Quote

Create the transaction using the exchange rate quote and bank account ID:

curl -X POST "https://api.sdigital2.com/v1/transactions" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customerId": "774f7a53-fd45-4634-9548-eb37c9554137",
    "exchangeRateId": "faa8ee1b-a422-4ef0-bbd8-4e5c597bb218",
    "source": {
      "paymentRail": "ETHEREUM"
    },
    "destination": {
      "bankAccountId": "3190d115-987f-402f-b558-0316d3a22ebd"
    },
    "purpose": "TRADE_TRANSACTIONS"
  }'

Response:

{
  "id": "82c1bfb7-ef1e-42e3-94d3-8c8b7677b14d",
  "type": "OFF_RAMP",
  "customerId": "774f7a53-fd45-4634-9548-eb37c9554137",
  "sourceAmount": "100",
  "source": {
    "currency": "USDC",
    "paymentRail": "ETHEREUM"
  },
  "destination": {
    "currency": "BRL",
    "paymentRail": "PIX"
  },
  "purpose": "TRADE_TRANSACTIONS",
  "createdAt": "2025-09-13T19:50:34.847Z",
  "updatedAt": "2025-09-13T19:50:34.827Z",
  "status": "TRANSFERRING_STABLECOIN"
}

Step 5: Monitor Transaction Status

Check the transaction status periodically:

curl -X GET "https://api.sdigital2.com/v1/transactions/{transaction_id}" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json"

Completed Transaction Response:

{
  "id": "82c1bfb7-ef1e-42e3-94d3-8c8b7677b14d",
  "type": "OFF_RAMP",
  "customerId": "774f7a53-fd45-4634-9548-eb37c9554137",
  "sourceAmount": "100",
  "source": {
    "currency": "USDC",
    "paymentRail": "ETHEREUM"
  },
  "destination": {
    "currency": "BRL",
    "paymentRail": "PIX"
  },
  "purpose": "TRADE_TRANSACTIONS",
  "createdAt": "2025-09-13T19:50:34.847Z",
  "updatedAt": "2025-09-13T19:50:34.827Z",
  "status": "SUCCESS",
  "receipt": {
    "sourceCurrency": "USDC",
    "sourceAmount": "100.000000",
    "destinationCurrency": "BRL",
    "destinationAmount": "524.19",
    "grossDestinationAmount": "535.16",
    "baseExchangeRate": "5.351638",
    "appliedExchangeRate": "5.241930",
    "fees": {
      "bps": "105",
      "flatAmount": "1",
      "totalAmount": "2.05",
      "currency": "USDC"
    }
  }
}

The transaction will automatically process once the customer's USDC balance is sufficient and the conversion will be sent to their PIX account.


Method 2: Without Exchange Rate Quote (Market Rate)

This method uses market rates at execution time, requiring fewer API calls but without rate guarantees.

Flow Overview

Steps 1-3: List Customers, Check Balances, and Get Bank Account

Follow the same steps as Method 1 to list customers, check their balances, and ensure you have a verified bank account.

Step 4: Create Transaction with Market Rate

Create the transaction directly without an exchange rate quote. You'll need to specify additional fields:

curl -X POST "https://api.sdigital2.com/v1/transactions" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customerId": "774f7a53-fd45-4634-9548-eb37c9554137",
    "sourceAmount": "100",
    "source": {
      "paymentRail": "ETHEREUM",
      "currency": "USDC"
    },
    "destination": {
      "bankAccountId": "3190d115-987f-402f-b558-0316d3a22ebd"
    },
    "purpose": "TRADE_TRANSACTIONS"
  }'

Key Differences from Method 1:

  • No exchangeRateId field
  • Must specify sourceAmount field
  • Must specify currency in the source object
  • Destination uses bankAccountId instead of payment rail details
  • Exchange rate is determined at execution time

Response:

{
  "id": "82c1bfb7-ef1e-42e3-94d3-8c8b7677b14d",
  "type": "OFF_RAMP",
  "customerId": "774f7a53-fd45-4634-9548-eb37c9554137",
  "sourceAmount": "100",
  "source": {
    "currency": "USDC",
    "paymentRail": "ETHEREUM"
  },
  "destination": {
    "currency": "BRL",
    "paymentRail": "PIX"
  },
  "purpose": "TRADE_TRANSACTIONS",
  "createdAt": "2025-09-13T19:50:34.847Z",
  "updatedAt": "2025-09-13T19:50:34.827Z",
  "status": "TRANSFERRING_STABLECOIN"
}

Step 4: Monitor Transaction Status

Monitor the transaction status the same way as Method 1. The final receipt will include the actual exchange rate that was applied:

{
  "id": "82c1bfb7-ef1e-42e3-94d3-8c8b7677b14d",
  "type": "OFF_RAMP",
  "customerId": "774f7a53-fd45-4634-9548-eb37c9554137",
  "sourceAmount": "100",
  "source": {
    "currency": "USDC",
    "paymentRail": "ETHEREUM"
  },
  "destination": {
    "currency": "BRL",
    "paymentRail": "PIX"
  },
  "purpose": "TRADE_TRANSACTIONS",
  "createdAt": "2025-09-13T19:50:34.847Z",
  "updatedAt": "2025-09-13T19:50:34.827Z",
  "status": "SUCCESS",
  "receipt": {
    "sourceCurrency": "USDC",
    "sourceAmount": "100.000000",
    "destinationCurrency": "BRL",
    "destinationAmount": "524.19",
    "grossDestinationAmount": "535.16",
    "baseExchangeRate": "5.351638",
    "appliedExchangeRate": "5.241930",
    "fees": {
      "bps": "105",
      "flatAmount": "1",
      "totalAmount": "2.05",
      "currency": "USDC"
    }
  }
}

Market Rate Considerations:

  • Exchange rates fluctuate in real-time
  • Final amounts may differ from estimates
  • Best for users who prioritize simplicity over rate certainty
  • Consider showing rate estimates in your UI before confirmation

Choosing the Right Method

Use Method 1 (With Quote) when:

  • Users need price certainty before confirming
  • Building trading or financial applications
  • Handling large transaction amounts
  • Users are price-sensitive

Use Method 2 (Market Rate) when:

  • Building simple conversion tools
  • Users prioritize speed over precision
  • Handling smaller transaction amounts
  • Rate fluctuations are acceptable

Next Steps