Skip to main content

Automated Payout

Overview

Create a Rule with a Trigger that detects the arrival of cryptocurrency into your NOAH Accounts.

If the deposit satisfies your defined Conditions, it will automatically execute the Action, which in this case, is a payout to your chosen Channel.

1. Set Up the Environment

Register Your Interest

  1. Register your interest or book a demo by contacting us on business@noah.com.
  2. Signup for a Sandbox account and contact us 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 on 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.
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"
}
}'
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 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 settlement CryptoCurrency, FiatCurrency, and Country.

    For example, to retrieve US-oriented payout channels, use values such as the below.

curl -L 'https://api.sandbox.noah.com/v1/channels/sell? \
Country=US& \
CryptoCurrency=USDC_TEST& \
FiatCurrency=USD& \
FiatAmount=100' \
-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 examples 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.

  • Determine the FiatCurrency as an ISO 4217 currency code.

  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 (click below to see the payload). You will need the ID value in the next step to be able to generate the dynamic form for the payout channel of interest. For example, in the example below, a4c3f754-094b-5f73-9da9-e0f1cb367f31 is the ID for the BankFedwire channel.

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"
}
]
}
4. Render or Populate the 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, in the example below, a4c3f754-094b-5f73-9da9-e0f1cb367f31 is the ID for the 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 (click below to see the payload).
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 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.

If called without an existing PaymentMethodID, all necessary fields will be requested; otherwise, only the missing fields will be requested.

On a successful call to the Prepare endpoint, you will receive a FormSessionID which can be used to submit the transaction.

  1. Retrieve your ChannelID, PaymentMethodID, CryptoCurrency, CustomerID, FiatAmount and Form fields, from the previous steps.
  2. Call the POST Prepare Sell Transaction endpoint.
  3. 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.

You can fetch a customer's existing payment methods using the Payment Methods endpoint.

tip

Prepare Endpoints and PaymentMethodIDs

The prepare endpoint's primary purposes 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.

6. Create the Rule
  1. Submit a POST request to the /v1/beta1/rule endpoint with the FormSessionID and CryptoAuthorizedAmount obtained from the Prepare step.
  2. Set the Trigger object to use the DepositSourceTriggerInput type. This trigger type specifies conditions based on the customer's on-chain deposit.
    • For DepositSourceTriggerInput, include:
      • SourceAddress: The on-chain address of the customer making the deposit. This will be used to verify the deposit.
      • AmountConditions: Specify the conditions related to the deposit amount.
      • CryptoCurrency: Set the cryptocurrency type (e.g., "USDC_TEST" for sandbox testing).
      • Network: Define the network, such as EthereumTestSepolia or PolygonTestMumbai for test environments.
  3. You can use the Permanent flag to specify that the rule can be executed repeatedly, to allow your customer to perform the flow multiple times. Ommitting this will only execute the flow the first time that the Trigger is satisfied.
  4. The Actions object acepts the FormSessionID from the Prepare step, which provides a reference to the details required to execute the payout.
  5. You will receive an on-chain deposit address to provide to your customer for the deposit.
  6. The rule will expire either at the specified expiry time or when the customer makes a successful deposit, whichever occurs first.

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