Merchant Payouts

This documentation shows how to perform a merchant payout from the Coinflow in-app wallet.

Setup

Developer Resources

Quick Links:

Authorization Headers:

  • Authorization - Your API Key from the merchant dashboard
  • x-coinflow-auth-user-id - A unique customer ID you use within your systems to identify the user withdrawing funds
  • x-coinflow-auth-blockchain - Should always be solana

Overview of How Merchant Payouts Work


Implementation

1. KYC / KYB for Withdrawers

To complete a payout/withdraw, every Withdrawer must complete verification before they can proceed with a payout through Coinflow. Withdrawers only need to KYC the first time they withdraw and do not need to KYC again for any subsequent withdrawals.

Before you start, determine how you will KYC your users:

Merchants who want to use Coinflow for KYC should call Register User.

Request
1curl --location 'https://api-sandbox.coinflow.cash/api/withdraw/kyc' \
2--header 'Authorization: YOUR_API_KEY' \
3--header 'accept: application/json' \
4--header 'content-type: application/json' \
5--header 'x-coinflow-auth-user-id: usher' \
6--data-raw '{
7 "info": {
8 "email": "usher@atl.com",
9 "firstName": "usher",
10 "surName": "raymond",
11 "physicalAddress": "2800 N Damen Ave",
12 "city": "Chicago",
13 "state": "IL",
14 "zip": "60625",
15 "country": "US",
16 "dob": "19761014",
17 "ssn": "1234"
18 }
19}'
Response
1{
2 "withdrawer": {
3 "_id": "672400ead654e5cdd247b33f",
4 "currency": "USD",
5 "email": "usher@atl.com",
6 "verification": {
7 "status": "approved"
8 }
9 }
10}
Request
1curl --request POST \
2--url https://api-sandbox.coinflow.cash/api/withdraw/kyc \
3--header 'Authorization: YOUR_API_KEY' \
4--header 'accept: application/json' \
5--header 'content-type: application/json' \
6--header 'x-coinflow-auth-user-id: dwaynejohnsongb123' \
7--data '{
8 "merchantId": "testtest",
9 "email": "dwaynejohnsongb@gmail.com",
10 "country": "GB"
11}'
Response
1{
2 "withdrawer": {
3 "_id": "676091072cd3ae949702b0ea",
4 "currency": "GBP",
5 "email": "dwaynejohnsongb@gmail.com",
6 "verification": {
7 "status": "approved"
8 }
9 }
10}

Merchants who want to pass KYC data from their existing KYC provider can call our Register User via Document endpoint.

Request
1curl --request POST \
2--url https://api-sandbox.coinflow.cash/api/withdraw/kyc-doc \
3--header 'accept: application/json' \
4--header 'content-type: multipart/form-data' \
5--header 'x-coinflow-auth-session-key: YOUR_SESSION_KEY' \
6--form email=dwaynejohnsonus@gmail.com \
7--form country=US \
8--form idType=ID_CARD \
9--form idFront='@1128061-ID_front.png' \
10--form idBack='@012e6a1-ID_back.png' \
11--form merchantId=testtest
Response
1{
2 "withdrawer": {
3 "_id": "67449b17d654e5cdd2925f1c",
4 "currency": "USD",
5 "email": "testtasha@coinflowlabs.app",
6 "verification": {
7 "status": "approved"
8 }
9 }
10}

Merchants who want to share Sumsub data with Coinflow will need to enter a tri-party agreement with Sumsub and Coinflow. Reach out to the Coinflow team with your Sumsub client ID to get started.

Once this agreement has been signed, follow these steps:

  1. Call Sumsub’s Generate Share Token endpoint to obtain a token.
  2. Call our Register User Via Share Token endpoint to pass KYC data.
Request
1curl --request POST \
2--url https://api-sandbox.coinflow.cash/api/withdraw/kyc/share-token \
3--header 'Authorization: YOUR_API_KEY' \
4--header 'accept: application/json' \
5--header 'content-type: application/json' \
6--header 'x-coinflow-auth-user-id: user123' \
7--data '{
8 "vendor": "sumsub",
9 "shareToken": "YOUR_SHARE_TOKEN",
10 "country": "US",
11 "merchantId": "testtest",
12 "email": "testuser@test.com"
13}'
Response
1{
2 "withdrawer": {
3 "_id": "67449b17d654e5cdd2925f1c",
4 "currency": "USD",
5 "email": "testtasha@coinflowlabs.app",
6 "verification": {
7 "status": "approved"
8 }
9 }
10}

Merchants must receive approval from the Coinflow Compliance team regarding approval of your kyc reliance program. Once you’ve receive approval, follow these implementation steps.

Request
1curl --request POST \
2 --url https://api-sandbox.coinflow.cash/api/withdraw/kyc/attested \
3 --header 'Authorization: YOUR_API_KEY' \
4 --header 'accept: application/json' \
5 --header 'content-type: application/json' \
6 --header 'x-coinflow-auth-user-id: user-id' \
7 --data '
8{
9 "email": "djohnson051972@gmail.com",
10 "firstName": "Dwayne",
11 "surName": "Johnson",
12 "physicalAddress": "201 E Randolph St",
13 "city": "Chicago",
14 "state": "IL",
15 "zip": "60601",
16 "country": "US",
17 "dob": "05021972",
18 "ssn": "1234"
19}
20'
Response
1{
2 "withdrawer": {
3 "_id": "685d8a89e36b426f2df64069",
4 "__v": 0,
5 "availability": {
6 "status": "Functional",
7 "reason": "Initial",
8 "editor": "system",
9 "updatedAt": "2025-06-26T17:59:37.451Z"
10 },
11 "currency": "USD",
12 "email": "djohnson051972@gmail.com",
13 "merchant": "6840bca9c7cb21ee5baaae76",
14 "originalCurrency": "USD",
15 "riskScoreOverride": false,
16 "user": true,
17 "verification": {
18 "hash": "ce749aa83c2efab20c4e1bfeca602f0674e1cf1b",
19 "vendor": "persona",
20 "reference": "ver_R6E6KrEK5rZN36Hd8d7gPaKg1cms",
21 "status": "attested"
22 },
23 "wallets": [
24 {
25 "wallet": "user-id",
26 "blockchain": "user"
27 }
28 ],
29 "watchlistExempt": "Unknown"
30 }
31}

2. Get Withdrawer Details

After calling our withdraw endpoints, you must call Get Withdrawer to get the latest verification status. This endpoint will tell you the user’s verification status. The get withdrawer endpoint can also be queried at any time to get more information about a withdrawer who has already completed KYC/KYB.

Request
1curl --request GET \
2 --url https://api-sandbox.coinflow.cash/api/withdraw \
3 --header 'Authorization: YOUR_API_KEY' \
4 --header 'accept: application/json' \
5 --header 'x-coinflow-auth-user-id: usher'

3. Add A Payout Destination

Withdrawers must link a payout destination so we know where to send the funds.

πŸ“˜ Want to use Coinflow’s UI for KYC and Bank Authentication?

Merchants can choose to use our UI for KYC and bank authentication, saving time by avoiding the need to build these flows and add payout destinations. You’ll only need to create the UI for displaying quotes and initiating payouts.

If you choose to use our UI, you do not need to separately implement Step 3: Add A Payout Destination as the bank auth UI also handles linking payout destinations.

πŸ‡ΊπŸ‡Έ 1. Withdrawers in the U.S.

πŸ‡ΊπŸ‡Έ Withdrawers in the U.S.

If withdrawers are in the U.S. and want to receive USD, they can link their debit card or bank account.

Add a debit card as a payout destination:
Request
1curl --request POST \
2 --url https://api-sandbox.coinflow.cash/api/withdraw/debit-card \
3 --header 'Authorization: <YOUR_API_KEY>' \
4 --header 'accept: application/json' \
5 --header 'content-type: application/json' \
6 --header 'x-coinflow-auth-user-id: usher' \
7 --data '{
8 "cardToken": "411111YJM5TX1111", // Debit Card Tokenization recipe: https://docs.coinflow.cash/recipes/tokenize-debit-cards-for-withdraws
9 "expMonth": "10",
10 "expYear": "29"
11 }'
Add a bank account as a payout destination:
Request
1curl --location 'https://api-sandbox.coinflow.cash/api/withdraw/account' \
2--header 'Authorization: <YOUR_API_KEY>' \
3--header 'accept: application/json' \
4--header 'content-type: application/json' \
5--header 'x-coinflow-auth-user-id: usher' \
6--data '
7{
8 "type": "checking",
9 "alias": "Ushers Savings 1",
10 "accountNumber": "1111222233330000",
11 "routingNumber": "333333334"
12}
13'
Response
{
"withdrawer": {
"_id": "672400ead654e5cdd247b33f",
"__v": 0,
"currency": "USD",
"email": "usher@atl.com",
"isBlocked": false,
"merchant": "6723f186b2f506b29dbee63d",
"originalCurrency": "USD",
"user": true,
"verification": {
"hash": "02154e69e0f38f2b31dec3b658535fa1cc283063",
"vendor": "persona",
"reference": "ver_DisMcS9M3HftnJYPpFSowNEWHyjT",
"status": "approved"
},
"wallets": [
{
"wallet": "usher",
"blockchain": "user"
}
],
"watchlistExempt": false,
"bankAccounts": [
{
"alias": "Ushers Savings 1",
"token": "0dc36240-29fc-4708-81f6-bcab19e6e597",
"routingNumber": "333333334",
"last4": "0000",
"accountHash": "cb20055931a543bcfe3183541f335f031452055a",
"rtpEligible": false,
"reference": "67240124b2f506b29dbef137",
"isDeleted": false
}
],
"cards": [
{
"last4": "1111",
"type": "VISA",
"disbursementStatus": "Immediate",
"token": "411111YJM5TX1111",
"createdAt": "2024-10-31T22:13:56.435Z"
}
],
"ibans": [],
"pixes": [],
"rtpDisabled": false,
"cardDisabled": false
}
}
πŸ‡ͺπŸ‡ΊπŸ‡¬πŸ‡§ 2.Withdrawers in the EU/UK

If withdrawers reside in the EU or UK, they can link their IBAN to receive EUR or GBP.

Add an IBAN account:
Request
1curl --location 'https://api-sandbox.coinflow.cash/api/withdraw/iban' \
2--header 'Authorization: <YOUR_API_KEY>' \
3--header 'accept: application/json' \
4--header 'content-type: application/json' \
5--header 'x-coinflow-auth-user-id: usher' \
6--data '
7{
8 "alias": "Ushers Savings 1",
9 "number": "<Iban number>",
10 "sortCode": "<UK Sort Code>"
11}
12'
Response
1{
2 "withdrawer": {
3 "_id": "672400ead654e5cdd247b33f",
4 "__v": 0,
5 "currency": "USD",
6 "email": "usher@atl.com",
7 "isBlocked": false,
8 "merchant": "6723f186b2f506b29dbee63d",
9 "originalCurrency": "USD",
10 "user": true,
11 "verification": {
12 "hash": "02154e69e0f38f2b31dec3b658535fa1cc283063",
13 "vendor": "persona",
14 "reference": "ver_DisMcS9M3HftnJYPpFSowNEWHyjT",
15 "status": "approved"
16 },
17 "wallets": [
18 {
19 "wallet": "usher",
20 "blockchain": "user"
21 }
22 ],
23 "watchlistExempt": false,
24 "bankAccounts": [],
25 "cards": [],
26 "ibans": [{
27 "alias": "Ushers Savings 1",
28 "token": "0dc36240-29fc-4708-81f6-bcab19e6e597",
29 "last4": "0000",
30 "accountHash": "cb20055931a543bcfe3183541f335f031452055a",
31 "reference": "67240124b2f506b29dbef137",
32 "isDeleted": false
33 }],
34 "pixes": [],
35 "rtpDisabled": false,
36 "cardDisabled": false
37 }
38}
πŸ‡§πŸ‡· 3. Withdrawers in Brazil

If withdrawers reside in Brazil, they can link their PIX account to receive BRL.

Add a PIX account:
Request
1curl --request POST \
2 --url https://api-sandbox.coinflow.cash/api/withdraw/pix \
3 --header 'accept: application/json' \
4 --header 'content-type: application/json' \
5 --data '{"pixKey":"1234"}'
Response
{
"withdrawer": {
"_id": "672400ead654e5cdd247b33f",
"__v": 0,
"currency": "USD",
"email": "usher@atl.com",
"isBlocked": false,
"merchant": "6723f186b2f506b29dbee63d",
"originalCurrency": "USD",
"user": true,
"verification": {
"hash": "02154e69e0f38f2b31dec3b658535fa1cc283063",
"vendor": "persona",
"reference": "ver_DisMcS9M3HftnJYPpFSowNEWHyjT",
"status": "approved"
},
"wallets": [
{
"wallet": "usher",
"blockchain": "user"
}
],
"watchlistExempt": false,
"bankAccounts": [],
"cards": [],
"ibans": [],
"pixes": [ {
"key" : "1234",
"accountHash" : "ef501efc395b66692b1fe5aa6ad1d3f4a0af48d7",
"token" : "7ab01310-66b1-4b13-8d80-868bd31a6f8e"
}],
"rtpDisabled": false,
"cardDisabled": false
}
}

4. Get a Quote for the Withdraw

Merchants may show an estimated quote of how much the user will receive after fees.

  • On sandbox, pass the following for token:
    4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU
  • On production, pass the following for token:
    EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
Request
1curl --location 'https://api-sandbox.coinflow.cash/api/withdraw/quote?token=4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU&amount=3&merchantId=YOUR_MERCHANT_ID&usePermit=true' \
2--header 'Authorization: <YOUR_API_KEY>' \
3--header 'accept: application/json' \
4--header 'x-coinflow-auth-blockchain: solana' \
5--header 'x-coinflow-auth-user-id: usher'
Response
1//Response
2{
3 "quote": {
4 "cents": 300,
5 "currency": "USD"
6 },
7 "gasFees": {
8 "gasFees": {
9 "cents": 0
10 },
11 "gasFeesWei": "0"
12 },
13 "same_day": {
14 "fee": {
15 "cents": 50,
16 "currency": "USD"
17 },
18 "finalSettlement": {
19 "cents": 250,
20 "currency": "USD"
21 },
22 "limit": {
23 "cents": 10000000,
24 "currency": "USD"
25 }
26 },
27 "standard": {
28 "fee": {
29 "cents": 100,
30 "currency": "USD"
31 },
32 "finalSettlement": {
33 "cents": 200,
34 "currency": "USD"
35 },
36 "limit": {
37 "cents": 10000000,
38 "currency": "USD"
39 }
40 },
41 "card": {
42 "fee": {
43 "cents": 200,
44 "currency": "USD"
45 },
46 "finalSettlement": {
47 "cents": 100,
48 "currency": "USD"
49 },
50 "limit": {
51 "cents": 5000000,
52 "currency": "USD"
53 }
54 }
55}

5. Initiate the Payout

This endpoint enables a user to submit a payout. Upon sending the request, funds from your Coinflow in-app balance will decrement, and the end-user will receive their funds in their selected payout destination.

  • Note: To get the tokenized bank account or debit card, call get withdrawer and reference the token param. For example, for bank account: bankAccounts[0]['token']
  • Pass any of the following as the speed depending on the payout method the withdrawer selects:
    speed = card for instant debit card payouts
    speed = asap for instant, RTP payout
    speed = same_day for same day ACH payout
    speed = standard for standard ACH payout
    speed = iban for SEPA payout
    speed = pix for PIX payout\
    Request
    1curl --request POST \
    2 --url https://api-sandbox.coinflow.cash/api/merchant/withdraws/payout/delegated \
    3 --header 'Authorization: YOUR_API_KEY' \
    4 --header 'accept: application/json' \
    5 --header 'content-type: application/json' \
    6 --data '
    7{
    8 "amount": {
    9 "cents": 300
    10 },
    11 "speed": "asap",
    12 "account": "0dc36240-29fc-4708-81f6-bcab19e6e597", // Replace this with the bank account token or debit card token
    13 "userId": "usher"
    14}
    15'
    Response
    //Response
    {
    "signature": "4Cv4nbe6fkGpdSYcqhPHXkdndeiyTa8mhFoWW5x3vroxRibAUssrbXZ5VW4vxkPedcX3xTRKu7ZpkJXWKdJBCGuq"
    }

6. Get In-App Wallet Balance

Optional: Call Get Balance to get the fund balance of your in-app wallet.

7. See Withdrawals

Go to Merchant Dashboard > Withdrawals > See Withdraw status.


FAQ / Troubleshooting

A 451 response indicates that we need additional information to verify the withdrawer.

When you receive a 451 response, you must redirect the withdrawer to the verificationLink provided in the response body (i.e., response.verificationLink). This link takes them to our provider’s verification page, where they typically need to upload a photo ID and take a selfie.

Once the withdrawer completes the verification, send a subsequent request to the get withdrawer endpoint to confirm the verification.status == approved.

Bank authentication verifies that a user owns the bank account they’re connecting. It’s essential for preventing fraud, ensuring regulatory compliance, reducing payment failures, and protecting against chargebacks. Per our AML policies, we require it before allowing withdrawals.

You can obtain the cardToken by using our secure frontend components, which capture and tokenize card details. This token replaces sensitive card information and is then passed to your backend for further processing.

Tokenizing the card is essential for PCI compliance. It ensures that raw card data never touches your servers, helping keep your system out of PCI scope. As a PCI-compliant provider, we handle the sensitive data securely, significantly reducing your compliance burden.

If you do not have an AOC for PCI DSS, follow our Tokenize Debit Cards for Withdraws guide. If you do have an AOC, you may instead follow our Tokenize Card Data via API for Debit Card Payouts recipe.

Merchants with PCI DSS certification should be able to provide a valid Attestation of Compliance (AOC) as proof. Here is an example of an acceptable AOC for merchants.

If you’re a service provider implementing on behalf of a merchant, you must provide a valid AOC specific to service providers. This is an example of an acceptable AOC for service providers. Additionally, your AOC must be reviewed and approved by a Qualified Security Assessor (QSA).

Merchants who are ready to go live have the option to:

  1. Send USDC on Solana directly. (Ask Coinflow for your production wallet address).
  2. Wire funds. (Ask Coinflow for wiring instructions).

At this time, Coinflow does not provide a UI for the merchant payout flow. Merchants have the option of using our UI for bank authentication only.