Skip to main content
POST
/
api
/
partner
/
v2
/
off-ramp
cURL
curl --request POST \
  --url https://staging-api.capa.fi/api/partner/v2/off-ramp \
  --header 'Content-Type: application/json' \
  --header 'partner-api-key: <partner-api-key>' \
  --data '
{
  "userId": "8374f327-38bd-4b0b-b8a7-2524599eb903",
  "userBankInformation": {
    "accountIdentifier": "<string>",
    "country": "MX",
    "accountType": "SAVINGS",
    "bankName": "<string>",
    "documentIdentifier": "<string>",
    "documentType": "<string>",
    "routingNumber": "<string>",
    "address": {
      "streetLine1": "123 Main St",
      "city": "New York",
      "postalCode": "10001",
      "country": "US",
      "streetLine2": "<string>",
      "state": "<string>"
    },
    "bic": "<string>",
    "iban": "<string>",
    "accountHolder": {
      "type": "INDIVIDUAL",
      "firstName": "<string>",
      "lastName": "<string>",
      "businessName": "<string>"
    }
  },
  "quoteId": "<string>",
  "fiatAmount": 123,
  "cryptoAmount": 123,
  "fiatCurrency": "MXN",
  "blockchainSymbol": "POL",
  "tokenSymbol": "USDC",
  "premiumSpread": 123,
  "receiverId": "<string>"
}
'
{
  "success": true,
  "data": {
    "id": "63f51f11-6869-47b0-a109-ddb50ef20efb",
    "userId": "8374f327-38bd-4b0b-b8a7-2524599eb903",
    "status": "PENDING_PAYMENT",
    "cryptoAmount": 2500,
    "fiatAmount": 50000,
    "exchangeRate": 19.53964594161554,
    "tokenSymbol": "USDC",
    "blockchainSymbol": "POL",
    "fiatCurrency": "MXN",
    "createdAt": "2025-05-14T10:00:00Z",
    "completedAt": "<string>",
    "destinationWalletAddress": "0x7796d4f304bd84171ee6730ad0f69c07a47e786d",
    "bankAccount": {
      "country": "MX",
      "accountIdentifier": "014680260346007120",
      "isVerified": true,
      "bankName": "Santander",
      "accountType": "SAVINGS",
      "documentIdentifier": "123abc",
      "documentType": "RNC",
      "iban": "DE89370400440532013000",
      "bic": "DEUTDEFF",
      "routingNumber": "021000021"
    },
    "crossFiatAmount": 123,
    "premiumSpread": 0.01
  }
}
Creates an off-ramp transaction on behalf of a verified user. This converts crypto to fiat and initiates a withdrawal to the user’s registered bank account.
For staging transaction testing, use the Mock Testing Guide.

Field Relationships

  • Provide quoteId OR (fiatCurrency + blockchainSymbol + tokenSymbol). When a quote is used, currency/token fields are inherited from the quote.
  • Provide either fiatAmount or cryptoAmount, not both.
  • Provide either userBankInformation (inline bank details) or userBankInformationId (reference to a saved bank account).
  • receiverId is optional — when provided, the off-ramp is processed for that receiver instead of the user directly.

Bank Account Requirements by Country

CountryRequired Fields
MXaccountIdentifier (18-digit CLABE)
DOaccountIdentifier, bankName, accountType, documentIdentifier, documentType
USaccountIdentifier, bankName, routingNumber, accountHolder, address
SEPAiban, bic, bankName, accountHolder

Integration Flow

3

Get a quote (optional)

POST /api/partner/v2/quotes — locks the exchange rate
4

Create off-ramp transaction

POST /api/partner/v2/off-ramp (this endpoint) — returns a destinationWalletAddress
5

User sends crypto

The user sends crypto to the destinationWalletAddress from the response.
6

Execute the off-ramp

POST /api/partner/v2/off-ramp/execute — triggers bank payout after crypto is received

Important Notes

  • User must be KYC-verified before creating transactions.
  • Mexico bank verification: First-time bank accounts trigger a penny test to verify the KYC’d user is the account owner. If verification fails, the transaction is cancelled.
  • CLABE validation (Mexico): Must match ^\d{18}$. Consider using clabe-validator.
  • Amount limits: Fiat and crypto amounts must fall within the min/max thresholds defined in your partner agreement.
  • Destination wallet: The response includes a unique destinationWalletAddress — the user must send crypto to this address.

Use Cases

  • User Payout: Convert crypto to fiat and withdraw to a verified bank account.
  • B2C Integration: Let individual users off-ramp from within your app.
  • Third-Party Payments: Use receiverId to send fiat payouts to receivers on behalf of a user.

Error Codes

Common Errors

HTTP StatusCodeMessage
401UNAUTHORIZED”API Key is missing”
401UNAUTHORIZED”Invalid API Key format”
401UNAUTHORIZED”Invalid API Key”
403INVALID_PARTNER_FLOW”The partner has an invalid flow.”

Verified User Errors

HTTP StatusCodeMessage
400REQUIRED_USER_ID_ERROR”This endpoint requires a user id to be provided”
403USER_NOT_VERIFIED_ERROR”User is not allowed to perform the operations because has not completed the KYC verification.”

Endpoint-Specific Errors

HTTP StatusCodeMessage
400INVALID_USER_INPUT_ERROR”Invalid User Input”
400BAD_REQUEST”Either quoteId or fiatCurrency, blockchainSymbol, tokenSymbol, and at least one of fiatAmount or cryptoAmount must be provided”
400BAD_REQUEST”Fiat currency is disabled or does not exist”
400BAD_REQUEST”This blockchain and token combination is disabled or does not exist”
400QUOTE_EXPIRED”Quote has expired”
400BAD_REQUEST”Quote is not valid for OFF_RAMP”
400INVALID_TOKEN_OPERATION_ERROR”Token cannot be operated as OTC, only stable coins are allowed”
400BAD_REQUEST”Either userBankInformationId or userBankInformation must be provided”
400BAD_REQUEST”The receiver user account is not verified”
400INVALID_FIAT_AMOUNT_ERROR”Fiat amount is outside of the allowed range for OFF_RAMP. Should be between and
400INVALID_TOKEN_AMOUNT_ERROR”Token amount is outside of the allowed range for OFF_RAMP. Should be between and
403RECEIVER_CANNOT_INITIATE_TRANSACTION”Receiver accounts cannot initiate transactions directly.”
403RECEIVER_USER_MISMATCH”The receiver does not belong to the specified user.”
403USER_BANK_INFO_ACCESS_DENIED”The bank information does not belong to the specified user.”
403USER_BANK_INFO_ACCESS_DENIED”The bank information belongs to a different partner.”
404RECEIVER_NOT_FOUND”Receiver not found.”
404USER_BANK_INFO_NOT_FOUND”User bank information not found.”

Headers

partner-api-key
string
required

Api key for the affiliated partner that is performing the request

Body

application/json
userId
string
required

Identifier for the user who's submitting the off-ramp order

Example:

"8374f327-38bd-4b0b-b8a7-2524599eb903"

userBankInformation
object

Inline bank account details for the off-ramp.

quoteId
string

Identifier for the quote to be used for the transaction.

fiatAmount
number

Amount of fiat currency to be received in conversion.

cryptoAmount
number

Amount of crypto currency to be converted to fiat currency.

fiatCurrency
enum<string>

Identifier for the fiat currency which the user will rec. Required when quoteId is not provided.

Available options:
MXN,
DOP,
USD,
EUR
blockchainSymbol
enum<string>

Identifier for the blockchain to token from which the conversion will be made. Required when quoteId is not provided.

Available options:
POL,
SOL,
BASE,
ARB,
BSC,
OP,
WLD,
STK,
ETH,
MTN,
CORE
tokenSymbol
enum<string>

Identifier for the token from which the conversion will be made. Required when quoteId is not provided.

Available options:
USDC,
USDT,
MXNe,
SOL,
ETH,
wBTC,
cbBTC,
PYSUD,
POL,
BNB,
WLD,
STK,
USDY,
CORE,
USDC.e,
wUSDL,
CoreBTC,
MATIC,
USDbC
premiumSpread
number

Spread percentage to be applied to the exchange rate

receiverId
string

Identifier for a previously created receiver (via /api/partner/v2/receivers).

Response

201 - application/json
success
boolean
Example:

true

data
object