Skip to main content

Global Payout

Overview

Create a payout transaction to a selected Channel on behalf of a given customer by settling in your chosen CryptoCurrency and providing the Dynamic Form data required to complete the transaction.

Payout transactions, by default, are settled from the reserve balance held in your NOAH crypto account - where cryptocurrencies are network agnostic.

Payout to a Consumer

BRL PIX Example

1. Set Up the Environment

Register Your Interest

  1. Register your interest or book a demo by contacting NOAH at business@noah.com.
  2. Signup for a Sandbox account and contact NOAH to upgrade it to a business account.

Authentication & Request Signing

For guidance on generating and configuring your API keys, see the Authentication page.

  1. Generate your Sandbox API key via the Business Dashboard.
  2. Include your API key in the X-Api-Key header of all requests.
  3. Request Signing is optional in Sandbox and required in Production. It is important to setup Request Signing before migrating to Production, to do this see the Request Signing page.

Webhook Configuration

For guidance on webhook subscriptions and configuration, see the Configuration page.

  1. Optionally whitelist NOAH's Webhook IP addresses, detailed in the Whitelisting section.
  2. Create a webhook subscription for the following event type, the details required to ingest the webhooks are also available on the respective page.
tip

Throughout the execution of a payout, you will receive one type of webhook, in two cases:

  1. Transaction - to notify you when the transaction has been created - this will begin with the Pending status.
  2. Transaction - to notify you when the transaction's status has changed to any of: Pending, Settled or Failed.
2. Create a Customer

Create a customer, if not already created, via the PUT Customers endpoint.

curl -L -X PUT 'https://api.sandbox.noah.com/v1/customers/:CustomerID' \
-H 'Content-Type: application/json' \
-H 'X-Api-Key: <X-Api-Key>' \
--data-raw '{
"Type": "Individual",
"FullName": {
"FirstName": "string",
"LastName": "string",
"MiddleName": "string"
},
"DateOfBirth": "2024-04-16",
"Email": "user@example.com",
"PhoneNumber": "string",
"Identities": [
{
"IssuingCountry": "US",
"IDNumber": "string",
"IssuedDate": "2024-04-16",
"ExpiryDate": "2024-04-16",
"IDType": "Passport"
}
],
"PrimaryResidence": {
"Street": "string",
"Street2": "string",
"City": "string",
"PostCode": "string",
"State": "string",
"Country": "US"
}
}'

For details, see the PUT Customers endpoint.

tip
  • When integrating under the Reliance Model, you must ensure that any customer executing a transaction has a valid and complete KYC Status in your system.
  • Ensure your CustomerID is stored against the compliance profile of the same customer, to reference when creating transactions.
3. Define the BRL Payout Channel
  1. Retrieve the list of available countries by calling the GET Supported Countries endpoint.
curl -L 'https://api.sandbox.noah.com/v1/channels/sell/countries' \
-H 'Accept: application/json' \
-H 'X-Api-Key: <X-Api-Key>'
  1. Once you've determined that your country of interest is supported, retrieve the list of country-specific compatible payout channels by calling the GET Supported Channels endpoint, providing, at least, your country and settlement CryptoCurrency.

    For Brazil-oriented payout channels for the USDC_TEST cryptocurrency, use the below.

curl -L 'https://api.sandbox.noah.com/v1/channels/sell?Country=BR&CryptoCurrency=USDC_TEST&FiatCurrency=BRL' \
-H 'Accept: application/json' \
-H 'X-Api-Key: <X-Api-Key>'
tip

Testnet faucets typically only drip small amounts of tokens to prevent abuse, which severely limits testing scenarios involving larger transactions or complex workflows. By deploying the USDC_TEST cryptocurrency, as used in the example above, NOAH removes these constraints and enables developers to conduct comprehensive testing with realistic transaction volumes, stress test applications properly, and simulate real-world scenarios without constantly waiting for faucet refills.

  1. On success, a payout channel is returned per supported PaymentMethodTypes.

    For Brazilian-oriented payout channels, the response body is such as the below:

Brazil-oriented demo response body (click to expand)
{
"Items": [
{
"Country": "BR",
"FiatCurrency": "USD",
"ID": "e69c4960-5394-5408-ac40-5c413b6c3f00",
"Limits": {
"MaxLimit": "15000",
"MinLimit": "1.2"
},
"PaymentMethodCategory": "Card",
"PaymentMethodType": "TokenizedCard",
"ProcessingSeconds": 86400,
"Rate": "1"
},
{
"Country": "BR",
"FiatCurrency": "USD",
"ID": "03e16604-adc9-5f63-a771-81fd0c0e4693",
"Limits": {
"MaxLimit": "1000000",
"MinLimit": "1.2"
},
"PaymentMethodCategory": "Card",
"PaymentMethodType": "TokenizedCard",
"ProcessingSeconds": 60,
"Rate": "1"
},
{
"Country": "BR",
"FiatCurrency": "INR",
"ID": "5d78078b-cb6e-564e-8ff8-e6cb0e1170aa",
"Limits": {
"MaxLimit": "15000",
"MinLimit": "2.5"
},
"PaymentMethodCategory": "Bank",
"PaymentMethodType": "BankLocal",
"ProcessingSeconds": 600,
"Rate": "82.08"
},
{
"Country": "BR",
"FiatCurrency": "GBP",
"ID": "b5d5c006-8145-585c-9110-b0dcb6b1ce52",
"Limits": {
"MaxLimit": "15000",
"MinLimit": "1.2"
},
"PaymentMethodCategory": "Card",
"PaymentMethodType": "TokenizedCard",
"ProcessingSeconds": 86400,
"Rate": "0.77"
},
{
"Country": "BR",
"FiatCurrency": "GBP",
"ID": "13370583-c253-5ca0-aae6-418f5b129ff9",
"Limits": {
"MaxLimit": "1000000",
"MinLimit": "1.2"
},
"PaymentMethodCategory": "Card",
"PaymentMethodType": "TokenizedCard",
"ProcessingSeconds": 60,
"Rate": "0.77"
},
{
"Country": "BR",
"FiatCurrency": "EUR",
"ID": "8bf48487-aeb2-5e22-84e9-388b53b5f1df",
"Limits": {
"MaxLimit": "15000",
"MinLimit": "1.2"
},
"PaymentMethodCategory": "Card",
"PaymentMethodType": "TokenizedCard",
"ProcessingSeconds": 86400,
"Rate": "0.93"
},
{
"Country": "BR",
"FiatCurrency": "EUR",
"ID": "c960ba3d-c089-573d-b557-dc7701ed64e3",
"Limits": {
"MaxLimit": "1000000",
"MinLimit": "1.2"
},
"PaymentMethodCategory": "Card",
"PaymentMethodType": "TokenizedCard",
"ProcessingSeconds": 60,
"Rate": "0.93"
},
{
"Country": "BR",
"FiatCurrency": "BRL",
"FormSchema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"AccountHolderAddress": {
"properties": {
"Address": {
"maxLength": 70,
"title": "Address",
"type": "string"
},
"City": {
"maxLength": 35,
"title": "Town",
"type": "string"
},
"State": {
"maxLength": 35,
"title": "State/Province",
"type": "string"
}
},
"required": ["State", "City", "Address"],
"title": "Account Holder Address",
"type": "object"
},
"TaxID": {
"pattern": "^\\d{11}$",
"title": "Tax ID (CPF)",
"type": "string"
},
"PhoneNumber": {
"pattern": "^\\+[0-9]{6,15}$",
"title": "Phone Number",
"type": "string"
},
"PaymentPurpose": {
"enum": [
"business insurance",
"family support",
"education",
"gift and donation",
"medical treatment",
"maintenance expenses",
"travel",
"small value remittance",
"construction expenses",
"exported goods",
"service charges",
"loan payment",
"property purchase",
"property rental",
"shares investment",
"fund investment",
"tax payment",
"personal transfer",
"salary payment",
"other fees",
"other",
"own account abroad",
"liberalized remittance",
"hotel accommodation",
"advertising expenses",
"advisory fees",
"insurance claims",
"delivery fees",
"office expenses",
"royalty fees",
"transportation fees",
"utility bills"
],
"title": "Payment Purpose",
"type": "string"
},
"IdentifierDetails": {
"allOf": [
{
"if": {
"properties": {
"IdentifierType": {
"const": "Email"
}
}
},
"then": {
"properties": {
"Identifier": {
"errorMessage": "Email must be a valid email address",
"format": "email",
"title": "Identifier",
"type": "string"
}
},
"required": ["Identifier"]
}
},
{
"if": {
"properties": {
"IdentifierType": {
"const": "UUID"
}
}
},
"then": {
"properties": {
"Identifier": {
"errorMessage": "Alias must be between 3 and 32 characters",
"maxLength": 32,
"minLength": 3,
"title": "Identifier",
"type": "string"
}
},
"required": ["Identifier"]
}
},
{
"if": {
"properties": {
"IdentifierType": {
"enum": ["PhoneNumber", "TaxID"]
}
}
},
"then": {
"properties": {}
}
}
],
"properties": {
"IdentifierType": {
"enum": ["PhoneNumber", "Email", "TaxID", "UUID"],
"title": "Identifier Type",
"type": "string"
}
},
"required": ["IdentifierType"],
"title": "Identifier Details",
"type": "object"
}
},
"required": [
"AccountHolderAddress",
"TaxID",
"PhoneNumber",
"PaymentPurpose",
"IdentifierDetails"
]
},
"ID": "1dfac6f4-04c1-54c4-9cf7-1a4b555a8579",
"Limits": {
"MaxLimit": "3500",
"MinLimit": "3"
},
"PaymentMethodCategory": "Identifier",
"PaymentMethodType": "IdentifierPix",
"PaymentMethods": [
{
"Details": {
"Identifier": "carlos.oliveira@example.com",
"IdentifierType": "Email",
"Type": "FiatPaymentMethodIdentifierDisplay"
},
"ID": "Identifier/Pix/BRL/Email/68280152-775f-55cf-9531-8b6a80698b2f",
"PaymentMethodType": "IdentifierPix"
},
{
"Details": {
"Identifier": "usuario@exemplo.com.br",
"IdentifierType": "Email",
"Type": "FiatPaymentMethodIdentifierDisplay"
},
"ID": "Identifier/Pix/BRL/Email/569d33d0-eac0-5c11-b217-1e8ef1e1637e",
"PaymentMethodType": "IdentifierPix"
}
],
"ProcessingSeconds": 50,
"Rate": "5.76"
}
]
}
tip

You will need the ID value in the next step to be able to generate the dynamic form for the payout channel of interest. In the example above, 1dfac6f4-04c1-54c4-9cf7-1a4b555a8579 is the ID for the IdentifierPix channel.

4. Render or Populate the BRL Dynamic Form

NOAH's Dynamic UI API gives you full control over your frontend while removing the complexity of handling conditional, ever-evolving payment flows.

  1. Call the GET Dynamic Form endpoint with the value of the ID of the payout channel to retrieve the payout channel's FormSchema.

    For example, 1dfac6f4-04c1-54c4-9cf7-1a4b555a8579 is the ID of the Brazil-oriented IdentifierPix channel, retrieved in the previous step.

curl -L 'https://api.sandbox.noah.com/v1/channels/1dfac6f4-04c1-54c4-9cf7-1a4b555a8579/form' \
-H 'Accept: application/json' \
-H 'X-Api-Key: <X-Api-Key>'
  1. On success, the FormSchema for the specified payout channel is generated and defines the payload that is returned.

For the IdentifierPix payout channel, the response body is such as the below:

Dynamically generated form schema for IdentifierPix payout channel (click to expand)
{
"FormSchema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"AccountHolderAddress": {
"properties": {
"Address": {
"maxLength": 70,
"title": "Address",
"type": "string"
},
"City": {
"maxLength": 35,
"title": "Town",
"type": "string"
},
"State": {
"maxLength": 35,
"title": "State/Province",
"type": "string"
}
},
"required": ["State", "City", "Address"],
"title": "Account Holder Address",
"type": "object"
},
"TaxID": {
"pattern": "^\\d{11}$",
"title": "Tax ID (CPF)",
"type": "string"
},
"PhoneNumber": {
"pattern": "^\\+[0-9]{6,15}$",
"title": "Phone Number",
"type": "string"
},
"PaymentPurpose": {
"enum": [
"business insurance",
"family support",
"education",
"gift and donation",
"medical treatment",
"maintenance expenses",
"travel",
"small value remittance",
"construction expenses",
"exported goods",
"service charges",
"loan payment",
"property purchase",
"property rental",
"shares investment",
"fund investment",
"tax payment",
"personal transfer",
"salary payment",
"other fees",
"other",
"own account abroad",
"liberalized remittance",
"hotel accommodation",
"advertising expenses",
"advisory fees",
"insurance claims",
"delivery fees",
"office expenses",
"royalty fees",
"transportation fees",
"utility bills"
],
"title": "Payment Purpose",
"type": "string"
},
"IdentifierDetails": {
"allOf": [
{
"if": {
"properties": {
"IdentifierType": {
"const": "Email"
}
}
},
"then": {
"properties": {
"Identifier": {
"errorMessage": "Email must be a valid email address",
"format": "email",
"title": "Identifier",
"type": "string"
}
},
"required": ["Identifier"]
}
},
{
"if": {
"properties": {
"IdentifierType": {
"const": "UUID"
}
}
},
"then": {
"properties": {
"Identifier": {
"errorMessage": "Alias must be between 3 and 32 characters",
"maxLength": 32,
"minLength": 3,
"title": "Identifier",
"type": "string"
}
},
"required": ["Identifier"]
}
},
{
"if": {
"properties": {
"IdentifierType": {
"enum": ["PhoneNumber", "TaxID"]
}
}
},
"then": {
"properties": {}
}
}
],
"properties": {
"IdentifierType": {
"enum": ["PhoneNumber", "Email", "TaxID", "UUID"],
"title": "Identifier Type",
"type": "string"
}
},
"required": ["IdentifierType"],
"title": "Identifier Details",
"type": "object"
}
},
"required": [
"AccountHolderAddress",
"TaxID",
"PhoneNumber",
"PaymentPurpose",
"IdentifierDetails"
]
}
}
tip

Render the FormSchema in your user interface to collect input from your customers, or populate the required fields from pre-existing data.

5. Prepare the BRL Transaction

The POST Prepare Sell Transaction endpoint provides the most accurate and up-to-date pricing estimate and pre-validates any form data supplied, working with an existing PaymentMethodID if available.

Fetch a customer's existing payment methods, if any, by using the Payment Methods endpoint.

note

Prepare Endpoints and PaymentMethodIDs

The primary purposes of the POST Prepare Sell Transaction endpoint are:

  • To get the most accurate and up-to-date pricing estimate.
  • To pre-validate any form data you have supplied, which works in conjunction with an existing PaymentMethodID.

If you call the forms endpoint without an existing PaymentMethodID, it will request the entire set of fields that NOAH needs to collect as new data for instructing a payment to that channel.

If you call the forms endpoint with an existing PaymentMethodID, it will request only the fields that NOAH doesn't yet have data for.

For example, let's say a Customer has already made an offramp on ChannelA. If they later come back to make another one, and you supply a valid PaymentMethodID in the forms response, NOAH will not request data that has already been provided.

For a Customer's first transaction, they won't have any saved Payment Methods, and therefore you won't have a PaymentMethodID - it's an optional field.

  1. Make sure you have available, by means of earlier API calls, at least your ChannelID, CryptoCurrency, FiatAmount and Form fields, from the previous steps.

  2. Call the POST Prepare Sell Transaction endpoint.

    For example, for the 1dfac6f4-04c1-54c4-9cf7-1a4b555a8579 channel, with the Brazilian-oriented form shown above, use something comparable to the below.

curl -L 'https://api.sandbox.noah.com/v1/transactions/sell/prepare' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'X-Api-Key: <X-Api-Key>' \
-d '{
"ChannelID": "1dfac6f4-04c1-54c4-9cf7-1a4b555a8579",
"CryptoCurrency": "USDC_TEST",
"FiatAmount": "1000.0",
"Form": {
"AccountHolderAddress": {
"Address": "123 Main Street",
"City": "Springfield",
"State": "IL"
},
"TaxID": "12345678909",
"PhoneNumber": "+551234567890",
"PaymentPurpose": "education",
"IdentifierDetails": {
"IdentifierType": "Email",
"Identifier": "user@example.com"
}
},
"DelayedSell": true
}'
tip

If you are directly settling with a customer, debit the customer's account in your system - using the FiatAmount as a reference for absorbing or passing on the fees to your customer.

  1. On success, you will receive information such as shown below.
{
"CryptoAmountEstimate": "178.266824",
"CryptoAuthorizedAmount": "180.049492",
"FormSessionID": "59c95c56-878f-4c4e-85b1-511c880f4be2",
"TotalFee": "20"
}
  • The CryptoAmountEstimate is the estimated amount of cryptocurrency that will be used in the transaction.
  • The CryptoAuthorizedAmount is the maximum amount of cryptocurrency that you will be charged for the transaction. This amount can be guided by a price estimate retrieved from the GET Prices endpoint. You can also add an additional percentage, based on your risk appetite, if you'd like to include a slippage for the transaction to settle in the event of a rate fluctuation.
  • The FormSessionID is the unique identifier for the Form Session. You use it when you submit the transaction, which you will do in the next step.
  • The TotalFee is the total fee that will be charged for using the specific channel, always specified in the same currency as the requested fiat segment of the transaction.
6. Submit the BRL Transaction
  1. Submit a POST request to the Create Sell Transaction endpoint with the FormSessionID retrieved from the previous step and the required transaction details.
curl -L 'https://api.sandbox.noah.com/v1/transactions/sell' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'X-Api-Key: <X-Api-Key>' \
-d '{
"CryptoCurrency": "USDC_TEST",
"FiatAmount": "1000.0",
"CryptoAuthorizedAmount": "175.733014",
"FormSessionID": "18e80701-f293-4ac6-bc76-89ff8c0f693f",
"Nonce": "dc879b38-494b-4de7-98a9-068703144328",
"ExternalID": "string"
}'
tip

When the transaction is created, your account's AvailableBalance will be immediately reduced and NOAH will attempt to complete the transaction at the best available price.

If this is not possible due to price fluctuations, NOAH will cancel the transaction and refund your AvailableBalance.

If the transaction is successful, during settlement NOAH will reduce your TotalBalance by the final amount and adjust your AvailableBalance to account for any difference between the final, settled amount and the initial CryptoAuthorizedAmount.

  1. When the transaction status changes, you will receive a response including the Transaction object, as shown below.
{
"Transaction": {
"Created": "2025-04-08T10:28:05Z",
"CryptoCurrency": "USDC_TEST",
"Direction": "Out",
"ExternalID": "string",
"FiatPayment": {
"Amount": "1000",
"FeeAmount": "20",
"FiatCurrency": "BRL"
},
"FiatPaymentMethod": {
"Country": "BR",
"DisplayDetails": {
"Identifier": "user@example.com",
"IdentifierType": "Email",
"Type": "FiatPaymentMethodIdentifierDisplay"
},
"ID": "",
"PaymentMethodCategory": "Identifier"
},
"ID": "33ab3f32-2ab7-5921-b581-ba43938e31a7",
"Network": "OffNetwork",
"Status": "Pending"
}
}

You will also receive a Transaction event webhook event, sent to your subscription URL.

Payout to a Business

USD Example

1. Set Up the Environment

Register Your Interest

  1. Register your interest or book a demo by contacting NOAH at business@noah.com.
  2. Signup for a Sandbox account and contact NOAH to upgrade it to a business account.

Authentication & Request Signing

For guidance on generating and configuring your API keys, see the Authentication page.

  1. Generate your Sandbox API key via the Business Dashboard.
  2. Include your API key in the X-Api-Key header of all requests.
  3. Request Signing is optional in Sandbox and required in Production. It is important to setup Request Signing before migrating to Production, to do this see the Request Signing page.

Webhook Configuration

For guidance on webhook subscriptions and configuration, see the Configuration page.

  1. Optionally whitelist NOAH's Webhook IP addresses, detailed in the Whitelisting section.
  2. Create a webhook subscription for the following event type, the details required to ingest the webhooks are also available on the respective page.
tip

Throughout the execution of a payout, you will receive one type of webhook, in two cases:

  1. Transaction - to notify you when the transaction has been created - this will begin with the Pending status.
  2. Transaction - to notify you when the transaction's status has changed to any of: Pending, Settled or Failed.
2. Create a Business Customer

Create a business customer, if not already created, via the PUT Customers endpoint.

curl -L -X PUT 'https://api.sandbox.noah.com/v1/customers/:CustomerID' \
-H 'Content-Type: application/json' \
-H 'X-Api-Key: <X-Api-Key>' \
--data-raw '{
"Type": "Business",
"RegisteredName": "Acme Corporation",
"RegistrationNumber": "12-3456789",
"RegistrationCountry": "US",
"RegisteredAddress": {
"Street": "123 Main Street",
"Street2": "Suite 500",
"City": "San Francisco",
"PostCode": "94105",
"State": "CA",
"Country": "US"
},
"IncorporationDate": "2010-03-15"
}'

For details, see the PUT Customers endpoint.

tip
  • When integrating under the Reliance Model, you must ensure that any customer executing a transaction has a valid and complete KYC Status in your system.
  • Ensure your CustomerID is stored against the compliance profile of the same customer, to reference when creating transactions.
3. Define the USD Payout Channel
  1. Retrieve the list of available countries by calling the GET Supported Countries endpoint.
curl -L 'https://api.sandbox.noah.com/v1/channels/sell/countries' \
-H 'Accept: application/json' \
-H 'X-Api-Key: <X-Api-Key>'
  1. Once you've determined that your country of interest is supported, retrieve the list of country-specific compatible payout channels by calling the GET Supported Channels endpoint, providing, at least, your country and settlement CryptoCurrency.

    For example, to retrieve US-oriented payout channels for the USDC_TEST cryptocurrency, use the below.

curl -L 'https://api.sandbox.noah.com/v1/channels/sell?Country=US&CryptoCurrency=USDC_TEST&FiatCurrency=USD' \
-H 'Accept: application/json' \
-H 'X-Api-Key: <X-Api-Key>'
tip

Testnet faucets typically only drip small amounts of tokens to prevent abuse, which severely limits testing scenarios involving larger transactions or complex workflows. By deploying the USDC_TEST cryptocurrency, as used in the example above, NOAH removes these constraints and enables developers to conduct comprehensive testing with realistic transaction volumes, stress test applications properly, and simulate real-world scenarios without constantly waiting for faucet refills.

  1. On success, a payout channel is returned per supported PaymentMethodTypes, such as BankAch, BankFedwire, and TokenizedCard.

    For example, when you retrieve US-oriented payout channels, the response body is such as the below:

US-oriented demo response body (click to expand)
{
"Items": [
{
"Calculated": {
"TotalFee": "4.5"
},
"Country": "US",
"FiatCurrency": "USD",
"ID": "ad52bb7c-2cd4-55a0-9547-518bb1b248f8",
"Limits": {
"MaxLimit": "1000000",
"MinLimit": "1.2"
},
"PaymentMethodCategory": "Card",
"PaymentMethodType": "TokenizedCard",
"ProcessingSeconds": 60,
"Rate": "1"
},
{
"Calculated": {
"TotalFee": "0.03"
},
"Country": "US",
"FiatCurrency": "USD",
"FormSchema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"AccountHolderAddress": {
"properties": {
"Address": {
"maxLength": 70,
"title": "Address",
"type": "string"
},
"City": {
"maxLength": 35,
"title": "Town",
"type": "string"
},
"PostalCode": {
"maxLength": 9,
"minLength": 5,
"title": "Postal Code",
"type": "string"
},
"State": {
"maxLength": 35,
"title": "State/Province",
"type": "string"
}
},
"required": ["State", "City", "Address", "PostalCode"],
"title": "Account Holder Address",
"type": "object"
},
"BankDetails": {
"properties": {
"AccountNumber": {
"maxLength": 17,
"minLength": 4,
"title": "Account Number",
"type": "string"
},
"BankCode": {
"maxLength": 9,
"minLength": 9,
"title": "Routing Number",
"type": "string"
}
},
"required": ["AccountNumber", "BankCode"],
"title": "Bank Details",
"type": "object"
},
"PaymentPurpose": {
"title": "Payment Purpose",
"type": "string"
}
},
"required": ["AccountHolderAddress", "BankDetails", "PaymentPurpose"]
},
"ID": "a4c3f754-094b-5f73-9da9-e0f1cb367f31",
"Limits": {
"MaxLimit": "15000",
"MinLimit": "0"
},
"PaymentMethodCategory": "Bank",
"PaymentMethodType": "BankFedwire",
"ProcessingSeconds": 86400,
"Rate": "1"
},
{
"Calculated": {
"TotalFee": "0.03"
},
"Country": "US",
"FiatCurrency": "USD",
"FormSchema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"AccountHolderAddress": {
"properties": {
"Address": {
"maxLength": 70,
"title": "Address",
"type": "string"
},
"City": {
"maxLength": 35,
"title": "Town",
"type": "string"
},
"PostalCode": {
"maxLength": 9,
"minLength": 5,
"title": "Postal Code",
"type": "string"
},
"State": {
"maxLength": 35,
"title": "State/Province",
"type": "string"
}
},
"required": ["State", "City", "Address", "PostalCode"],
"title": "Account Holder Address",
"type": "object"
},
"BankDetails": {
"properties": {
"AccountNumber": {
"maxLength": 12,
"minLength": 8,
"title": "Account Number",
"type": "string"
},
"BankCode": {
"maxLength": 9,
"minLength": 9,
"title": "Routing Number",
"type": "string"
},
"AccountType": {
"enum": ["Checking", "Savings"],
"title": "Account Type",
"type": "string"
}
},
"required": ["AccountNumber", "BankCode", "AccountType"],
"title": "Bank Details",
"type": "object"
},
"PaymentPurpose": {
"title": "Payment Purpose",
"type": "string"
}
},
"required": ["AccountHolderAddress", "BankDetails", "PaymentPurpose"]
},
"ID": "214eab50-e22b-5e0f-b487-37dc1addee90",
"Limits": {
"MaxLimit": "15000",
"MinLimit": "0"
},
"PaymentMethodCategory": "Bank",
"PaymentMethodType": "BankAch",
"ProcessingSeconds": 86400,
"Rate": "1"
}
]
}
tip

You will need the ID value in the next step to be able to generate the dynamic form for the payout channel of interest. In the example above, a4c3f754-094b-5f73-9da9-e0f1cb367f31 is the ID for the BankFedwire channel.

4. Render or Populate the USD Dynamic Form

NOAH's Dynamic UI API gives you full control over your frontend while removing the complexity of handling conditional, ever-evolving payment flows.

  1. Call the GET Dynamic Form endpoint with the value of the ID of the payout channel to retrieve the payout channel's FormSchema.

    For example, a4c3f754-094b-5f73-9da9-e0f1cb367f31 is the ID for the US-oriented BankFedwire channel, retrieved in the previous step.

curl -L 'https://api.sandbox.noah.com/v1/channels/a4c3f754-094b-5f73-9da9-e0f1cb367f31/form' \
-H 'Accept: application/json' \
-H 'X-Api-Key: <X-Api-Key>'
  1. On success, the FormSchema for the specified payout channel is generated and defines the payload that is returned.

For example, for a BankFedwire payout channel, the response body is such as the below:

Dynamically generated form schema for BankFedwire payout channel (click to expand)
{
"FormSchema": {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"AccountHolderAddress": {
"properties": {
"Address": {
"maxLength": 70,
"title": "Address",
"type": "string"
},
"City": {
"maxLength": 35,
"title": "Town",
"type": "string"
},
"PostalCode": {
"maxLength": 9,
"minLength": 5,
"title": "Postal Code",
"type": "string"
},
"State": {
"maxLength": 35,
"title": "State/Province",
"type": "string"
}
},
"required": ["State", "City", "Address", "PostalCode"],
"title": "Account Holder Address",
"type": "object"
},
"BankDetails": {
"properties": {
"AccountNumber": {
"maxLength": 17,
"minLength": 4,
"title": "Account Number",
"type": "string"
},
"BankCode": {
"maxLength": 9,
"minLength": 9,
"title": "Routing Number",
"type": "string"
}
},
"required": ["AccountNumber", "BankCode"],
"title": "Bank Details",
"type": "object"
},
"PaymentPurpose": {
"title": "Payment Purpose",
"type": "string"
}
},
"required": ["AccountHolderAddress", "BankDetails", "PaymentPurpose"]
}
}
tip

Render the FormSchema in your user interface to collect input from your customers, or populate the required fields from pre-existing data.

5. Prepare the USD Transaction

The POST Prepare Sell Transaction endpoint provides the most accurate and up-to-date pricing estimate and pre-validates any form data supplied, working with an existing PaymentMethodID if available.

Fetch a customer's existing payment methods, if any, by using the Payment Methods endpoint.

note

Prepare Endpoints and PaymentMethodIDs

The primary purposes of the POST Prepare Sell Transaction endpoint are:

  • To get the most accurate and up-to-date pricing estimate.
  • To pre-validate any form data you have supplied, which works in conjunction with an existing PaymentMethodID.

If you call the forms endpoint without an existing PaymentMethodID, it will request the entire set of fields that NOAH needs to collect as new data for instructing a payment to that channel.

If you call the forms endpoint with an existing PaymentMethodID, it will request only the fields that NOAH doesn't yet have data for.

For example, let's say a Customer has already made an offramp on ChannelA. If they later come back to make another one, and you supply a valid PaymentMethodID in the forms response, NOAH will not request data that has already been provided.

For a Customer's first transaction, they won't have any saved Payment Methods, and therefore you won't have a PaymentMethodID - it's an optional field.

  1. Make sure you have available, by means of earlier API calls, at least your ChannelID, CryptoCurrency, FiatAmount and Form fields, from the previous steps.

  2. Call the POST Prepare Sell Transaction endpoint.

    For example, for the a4c3f754-094b-5f73-9da9-e0f1cb367f31 channel, with the US-oriented form shown above, use something comparable to the below.

curl -L 'https://api.sandbox.noah.com/v1/transactions/sell/prepare' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'X-Api-Key: <X-Api-Key>' \
-d '{
"ChannelID": "a4c3f754-094b-5f73-9da9-e0f1cb367f31",
"CryptoCurrency": "USDC_TEST",
"FiatAmount": "1000.0",
"Form": {
"AccountHolderAddress": {
"Address": "123 Main Street",
"City": "New York",
"State": "NY",
"PostalCode": "10001"
},
"BankDetails": {
"AccountNumber": "12345678",
"BankCode": "123456789"
},
"PaymentPurpose": "Salary payment"
},
"DelayedSell": true
}'
tip

If you are directly settling with a customer, debit the customer's account in your system - using the FiatAmount as a reference for absorbing or passing on the fees to your customer.

  1. On success, you will receive information such as shown below.
{
"CryptoAmountEstimate": "1000.03",
"CryptoAuthorizedAmount": "1010.03",
"FormSessionID": "59c95c56-878f-4c4e-85b1-511c880f4be2",
"TotalFee": "0.03"
}
  • The CryptoAmountEstimate is the estimated amount of cryptocurrency that will be used in the transaction.
  • The CryptoAuthorizedAmount is the maximum amount of cryptocurrency that you will be charged for the transaction. This amount can be guided by a price estimate retrieved from the GET Prices endpoint. You can also add an additional percentage, based on your risk appetite, if you'd like to include a slippage for the transaction to settle in the event of a rate fluctuation.
  • The FormSessionID is the unique identifier for the Form Session. You use it when you submit the transaction, which you will do in the next step.
  • The TotalFee is the total fee that will be charged for using the specific channel, always specified in the same currency as the requested fiat segment of the transaction.
6. Submit the USD Transaction
  1. Submit a POST request to the Create Sell Transaction endpoint with the FormSessionID retrieved from the previous step and the required transaction details.
curl -L 'https://api.sandbox.noah.com/v1/transactions/sell' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json' \
-H 'X-Api-Key: <X-Api-Key>' \
-d '{
"CryptoCurrency": "USDC_TEST",
"FiatAmount": "1000.0",
"CryptoAuthorizedAmount": "1010.03",
"FormSessionID": "59c95c56-878f-4c4e-85b1-511c880f4be2",
"Nonce": "dc879b38-494b-4de7-98a9-068703144328",
"ExternalID": "string"
}'
tip

When the transaction is created, your account's AvailableBalance will be immediately reduced and NOAH will attempt to complete the transaction at the best available price.

If this is not possible due to price fluctuations, NOAH will cancel the transaction and refund your AvailableBalance.

If the transaction is successful, during settlement NOAH will reduce your TotalBalance by the final amount and adjust your AvailableBalance to account for any difference between the final, settled amount and the initial CryptoAuthorizedAmount.

  1. When the transaction status changes, you will receive a response including the Transaction object, as shown below.
{
"Transaction": {
"Created": "2025-04-08T10:28:05Z",
"CryptoCurrency": "USDC_TEST",
"Direction": "Out",
"ExternalID": "string",
"FiatPayment": {
"Amount": "1000",
"FeeAmount": "0.03",
"FiatCurrency": "USD"
},
"FiatPaymentMethod": {
"Country": "US",
"DisplayDetails": {
"BankAccountNumber": "****5678",
"BankCode": "123456789",
"Type": "FiatPaymentMethodBankDisplay"
},
"ID": "",
"PaymentMethodCategory": "Bank"
},
"ID": "33ab3f32-2ab7-5921-b581-ba43938e31a7",
"Network": "OffNetwork",
"Status": "Pending"
}
}

You will also receive a Transaction event webhook event, sent to your subscription URL.

For more details on this product, see the Global Payouts API Product page.