Quickstart
This guide walks through two end-to-end flows against the sandbox environment — a card checkout and a payout to a bank account.
Before you start: Make sure you’ve completed Account Setup and have your sandbox API key and merchant ID ready.
Prefer Postman? Import the Card Checkout + Merchant Payouts collection to run every request in this guide with pre-wired variables. Set apiKey, merchantId, and userId in the collection variables and you’re ready to go.
Part 1 — Card Checkout
Three steps to take your first card payment:
- Get a session key — authorize the payer to your server (server-side)
- Get a checkout JWT — sign the cart details (server-side)
- Render the
CoinflowPurchasecomponent — display the card form (client-side)
Coinflow’s pre-built UI handles card capture, PCI-compliant tokenization, 3DS challenges, and fraud signals automatically. You don’t tokenize cards yourself unless you have your own PCI DSS AOC.
Step 1 — Get a session key
A session key is a short-lived JWT that ties the checkout to a specific payer. Generate it on your server using your internal user ID, then pass it to the front-end.
Request
GET https://api-sandbox.coinflow.cash/api/auth/session-key
curl https://api-sandbox.coinflow.cash/api/auth/session-key \
-H "x-coinflow-auth-user-id: <apiKey>" \
-H "Content-Type: application/json"
import requests
url = "https://api-sandbox.coinflow.cash/api/auth/session-key"
payload = {}
headers = {
"x-coinflow-auth-user-id": "<apiKey>",
"Content-Type": "application/json"
}
response = requests.get(url, json=payload, headers=headers)
print(response.json())
const url = 'https://api-sandbox.coinflow.cash/api/auth/session-key';
const options = {
method: 'GET',
headers: {'x-coinflow-auth-user-id': '<apiKey>', 'Content-Type': 'application/json'},
body: '{}'
};
try {
const response = await fetch(url, options);
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api-sandbox.coinflow.cash/api/auth/session-key"
payload := strings.NewReader("{}")
req, _ := http.NewRequest("GET", url, payload)
req.Header.Add("x-coinflow-auth-user-id", "<apiKey>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
require 'uri'
require 'net/http'
url = URI("https://api-sandbox.coinflow.cash/api/auth/session-key")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["x-coinflow-auth-user-id"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{}"
response = http.request(request)
puts response.read_body
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;
HttpResponse<String> response = Unirest.get("https://api-sandbox.coinflow.cash/api/auth/session-key")
.header("x-coinflow-auth-user-id", "<apiKey>")
.header("Content-Type", "application/json")
.body("{}")
.asString();
<?php
require_once('vendor/autoload.php');
$client = new \GuzzleHttp\Client();
$response = $client->request('GET', 'https://api-sandbox.coinflow.cash/api/auth/session-key', [
'body' => '{}',
'headers' => [
'Content-Type' => 'application/json',
'x-coinflow-auth-user-id' => '<apiKey>',
],
]);
echo $response->getBody();
using RestSharp;
var client = new RestClient("https://api-sandbox.coinflow.cash/api/auth/session-key");
var request = new RestRequest(Method.GET);
request.AddHeader("x-coinflow-auth-user-id", "<apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
import Foundation
let headers = [
"x-coinflow-auth-user-id": "<apiKey>",
"Content-Type": "application/json"
]
let parameters = [] as [String : Any]
let postData = JSONSerialization.data(withJSONObject: parameters, options: [])
let request = NSMutableURLRequest(url: NSURL(string: "https://api-sandbox.coinflow.cash/api/auth/session-key")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error as Any)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse)
}
})
dataTask.resume()
Response (200)
{
"key": "a1b2c3d4e5f67890abcdef1234567890"
}
Session keys are valid for 24 hours. Refresh when expired.
Step 2 — Get a checkout JWT
The checkout JWT signs the cart payload (amount, customer email, chargeback-protection data) so the front-end can’t tamper with it. Generate it on your server right before rendering the checkout component.
Request
POST https://api-sandbox.coinflow.cash/api/checkout/jwt-token
curl -X POST https://api-sandbox.coinflow.cash/api/checkout/jwt-token \
-H "Authorization: <apiKey>" \
-H "Content-Type: application/json" \
-d '{}'
import requests
url = "https://api-sandbox.coinflow.cash/api/checkout/jwt-token"
payload = {}
headers = {
"Authorization": "<apiKey>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.json())
const url = 'https://api-sandbox.coinflow.cash/api/checkout/jwt-token';
const options = {
method: 'POST',
headers: {Authorization: '<apiKey>', 'Content-Type': 'application/json'},
body: '{}'
};
try {
const response = await fetch(url, options);
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api-sandbox.coinflow.cash/api/checkout/jwt-token"
payload := strings.NewReader("{}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<apiKey>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
require 'uri'
require 'net/http'
url = URI("https://api-sandbox.coinflow.cash/api/checkout/jwt-token")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{}"
response = http.request(request)
puts response.read_body
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;
HttpResponse<String> response = Unirest.post("https://api-sandbox.coinflow.cash/api/checkout/jwt-token")
.header("Authorization", "<apiKey>")
.header("Content-Type", "application/json")
.body("{}")
.asString();
<?php
require_once('vendor/autoload.php');
$client = new \GuzzleHttp\Client();
$response = $client->request('POST', 'https://api-sandbox.coinflow.cash/api/checkout/jwt-token', [
'body' => '{}',
'headers' => [
'Authorization' => '<apiKey>',
'Content-Type' => 'application/json',
],
]);
echo $response->getBody();
using RestSharp;
var client = new RestClient("https://api-sandbox.coinflow.cash/api/checkout/jwt-token");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "<apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
import Foundation
let headers = [
"Authorization": "<apiKey>",
"Content-Type": "application/json"
]
let parameters = [] as [String : Any]
let postData = JSONSerialization.data(withJSONObject: parameters, options: [])
let request = NSMutableURLRequest(url: NSURL(string: "https://api-sandbox.coinflow.cash/api/checkout/jwt-token")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error as Any)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse)
}
})
dataTask.resume()
{
"checkoutJwtToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Send key (from Step 1) and checkoutJwtToken (from Step 2) to your front-end.
Step 3 — Render the checkout component
Install the React SDK and render CoinflowPurchase with the two tokens. Coinflow handles the rest — card entry UI, validation, PCI-compliant tokenization, 3DS, fraud scoring, and settlement to your Coinflow Wallet.
npm install @coinflowlabs/react
import { CoinflowPurchase, Currency } from '@coinflowlabs/react';
export function Checkout({ sessionKey, jwtToken }: { sessionKey: string; jwtToken: string }) {
return (
<CoinflowPurchase
merchantId="YOUR_MERCHANT_ID"
env="sandbox" // switch to "prod" when going live
sessionKey={sessionKey}
jwtToken={jwtToken}
subtotal={{ cents: 100, currency: Currency.USD }}
email="payer@example.com"
onSuccess={(paymentId) => {
console.log('Payment successful:', paymentId);
// Redirect to your success page
}}
/>
);
}
That’s a payment. When onSuccess fires, funds have settled to your Coinflow Wallet. Coinflow’s chargeback protection and 3DS are layered in by default — see Adding Chargeback Protection and About 3D Secure to tune them.
Sandbox test cards: 5204247750001471 (Mastercard) or any card from the testing guide.
Alternative: Direct API integration (PCI-compliant merchants only)
If you hold your own PCI DSS AOC and want a fully custom UI, you can tokenize and charge cards via the API directly without rendering the CoinflowPurchase component. Contact support@pay.plasma.to to provision the tokenization credentials your requests need.
Tokenize the card:
Request
POST https://api-sandbox.coinflow.cash/api/tokenize
curl -X POST https://api-sandbox.coinflow.cash/api/tokenize \
-H "Authorization: <apiKey>" \
-H "Content-Type: application/json" \
-d '{}'
import requests
url = "https://api-sandbox.coinflow.cash/api/tokenize"
payload = {}
headers = {
"Authorization": "<apiKey>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.json())
const url = 'https://api-sandbox.coinflow.cash/api/tokenize';
const options = {
method: 'POST',
headers: {Authorization: '<apiKey>', 'Content-Type': 'application/json'},
body: '{}'
};
try {
const response = await fetch(url, options);
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api-sandbox.coinflow.cash/api/tokenize"
payload := strings.NewReader("{}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<apiKey>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
require 'uri'
require 'net/http'
url = URI("https://api-sandbox.coinflow.cash/api/tokenize")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{}"
response = http.request(request)
puts response.read_body
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;
HttpResponse<String> response = Unirest.post("https://api-sandbox.coinflow.cash/api/tokenize")
.header("Authorization", "<apiKey>")
.header("Content-Type", "application/json")
.body("{}")
.asString();
<?php
require_once('vendor/autoload.php');
$client = new \GuzzleHttp\Client();
$response = $client->request('POST', 'https://api-sandbox.coinflow.cash/api/tokenize', [
'body' => '{}',
'headers' => [
'Authorization' => '<apiKey>',
'Content-Type' => 'application/json',
],
]);
echo $response->getBody();
using RestSharp;
var client = new RestClient("https://api-sandbox.coinflow.cash/api/tokenize");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "<apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
import Foundation
let headers = [
"Authorization": "<apiKey>",
"Content-Type": "application/json"
]
let parameters = [] as [String : Any]
let postData = JSONSerialization.data(withJSONObject: parameters, options: [])
let request = NSMutableURLRequest(url: NSURL(string: "https://api-sandbox.coinflow.cash/api/tokenize")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error as Any)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse)
}
})
dataTask.resume()
Response (200)
{
"token": "tok_1A2b3C4d5E6f7G8h9I0j",
"firstSix": "411111",
"lastFour": "1111",
"referenceNumber": "REF123456789",
"success": true,
"error": "",
"message": "Tokenization successful"
}
Submit the payment with the returned token as card.cardToken:
Request
POST https://api-sandbox.coinflow.cash/api/checkout/card/{merchantId}
curl -X POST https://api-sandbox.coinflow.cash/api/checkout/card/merchantId \
-H "x-coinflow-auth-session-key: <apiKey>" \
-H "Content-Type: application/json" \
-d '{
"subtotal": {
"cents": 1,
"currency": "USD"
},
"card": {
"cardToken": "string",
"expYear": "string",
"expMonth": "string",
"email": "string",
"firstName": "string",
"lastName": "string",
"address1": "string",
"city": "string",
"country": "string"
}
}'
import requests
url = "https://api-sandbox.coinflow.cash/api/checkout/card/merchantId"
payload = {
"subtotal": {
"cents": 1,
"currency": "USD"
},
"card": {
"cardToken": "string",
"expYear": "string",
"expMonth": "string",
"email": "string",
"firstName": "string",
"lastName": "string",
"address1": "string",
"city": "string",
"country": "string"
}
}
headers = {
"x-coinflow-auth-session-key": "<apiKey>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.json())
const url = 'https://api-sandbox.coinflow.cash/api/checkout/card/merchantId';
const options = {
method: 'POST',
headers: {'x-coinflow-auth-session-key': '<apiKey>', 'Content-Type': 'application/json'},
body: '{"subtotal":{"cents":1,"currency":"USD"},"card":{"cardToken":"string","expYear":"string","expMonth":"string","email":"string","firstName":"string","lastName":"string","address1":"string","city":"string","country":"string"}}'
};
try {
const response = await fetch(url, options);
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api-sandbox.coinflow.cash/api/checkout/card/merchantId"
payload := strings.NewReader("{\n \"subtotal\": {\n \"cents\": 1,\n \"currency\": \"USD\"\n },\n \"card\": {\n \"cardToken\": \"string\",\n \"expYear\": \"string\",\n \"expMonth\": \"string\",\n \"email\": \"string\",\n \"firstName\": \"string\",\n \"lastName\": \"string\",\n \"address1\": \"string\",\n \"city\": \"string\",\n \"country\": \"string\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("x-coinflow-auth-session-key", "<apiKey>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
require 'uri'
require 'net/http'
url = URI("https://api-sandbox.coinflow.cash/api/checkout/card/merchantId")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["x-coinflow-auth-session-key"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"subtotal\": {\n \"cents\": 1,\n \"currency\": \"USD\"\n },\n \"card\": {\n \"cardToken\": \"string\",\n \"expYear\": \"string\",\n \"expMonth\": \"string\",\n \"email\": \"string\",\n \"firstName\": \"string\",\n \"lastName\": \"string\",\n \"address1\": \"string\",\n \"city\": \"string\",\n \"country\": \"string\"\n }\n}"
response = http.request(request)
puts response.read_body
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;
HttpResponse<String> response = Unirest.post("https://api-sandbox.coinflow.cash/api/checkout/card/merchantId")
.header("x-coinflow-auth-session-key", "<apiKey>")
.header("Content-Type", "application/json")
.body("{\n \"subtotal\": {\n \"cents\": 1,\n \"currency\": \"USD\"\n },\n \"card\": {\n \"cardToken\": \"string\",\n \"expYear\": \"string\",\n \"expMonth\": \"string\",\n \"email\": \"string\",\n \"firstName\": \"string\",\n \"lastName\": \"string\",\n \"address1\": \"string\",\n \"city\": \"string\",\n \"country\": \"string\"\n }\n}")
.asString();
<?php
require_once('vendor/autoload.php');
$client = new \GuzzleHttp\Client();
$response = $client->request('POST', 'https://api-sandbox.coinflow.cash/api/checkout/card/merchantId', [
'body' => '{
"subtotal": {
"cents": 1,
"currency": "USD"
},
"card": {
"cardToken": "string",
"expYear": "string",
"expMonth": "string",
"email": "string",
"firstName": "string",
"lastName": "string",
"address1": "string",
"city": "string",
"country": "string"
}
}',
'headers' => [
'Content-Type' => 'application/json',
'x-coinflow-auth-session-key' => '<apiKey>',
],
]);
echo $response->getBody();
using RestSharp;
var client = new RestClient("https://api-sandbox.coinflow.cash/api/checkout/card/merchantId");
var request = new RestRequest(Method.POST);
request.AddHeader("x-coinflow-auth-session-key", "<apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n \"subtotal\": {\n \"cents\": 1,\n \"currency\": \"USD\"\n },\n \"card\": {\n \"cardToken\": \"string\",\n \"expYear\": \"string\",\n \"expMonth\": \"string\",\n \"email\": \"string\",\n \"firstName\": \"string\",\n \"lastName\": \"string\",\n \"address1\": \"string\",\n \"city\": \"string\",\n \"country\": \"string\"\n }\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
import Foundation
let headers = [
"x-coinflow-auth-session-key": "<apiKey>",
"Content-Type": "application/json"
]
let parameters = [
"subtotal": [
"cents": 1,
"currency": "USD"
],
"card": [
"cardToken": "string",
"expYear": "string",
"expMonth": "string",
"email": "string",
"firstName": "string",
"lastName": "string",
"address1": "string",
"city": "string",
"country": "string"
]
] as [String : Any]
let postData = JSONSerialization.data(withJSONObject: parameters, options: [])
let request = NSMutableURLRequest(url: NSURL(string: "https://api-sandbox.coinflow.cash/api/checkout/card/merchantId")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error as Any)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse)
}
})
dataTask.resume()
Response (200)
{
"paymentId": "string",
"authorizationExpiration": "string"
}
Fetch full payment details any time:
Request
GET https://api-sandbox.coinflow.cash/api/merchant/payments/enhanced/{paymentId}
curl https://api-sandbox.coinflow.cash/api/merchant/payments/enhanced/paymentId \
-H "Authorization: <apiKey>"
import requests
url = "https://api-sandbox.coinflow.cash/api/merchant/payments/enhanced/paymentId"
headers = {"Authorization": "<apiKey>"}
response = requests.get(url, headers=headers)
print(response.json())
const url = 'https://api-sandbox.coinflow.cash/api/merchant/payments/enhanced/paymentId';
const options = {method: 'GET', headers: {Authorization: '<apiKey>'}};
try {
const response = await fetch(url, options);
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api-sandbox.coinflow.cash/api/merchant/payments/enhanced/paymentId"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "<apiKey>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
require 'uri'
require 'net/http'
url = URI("https://api-sandbox.coinflow.cash/api/merchant/payments/enhanced/paymentId")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = '<apiKey>'
response = http.request(request)
puts response.read_body
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;
HttpResponse<String> response = Unirest.get("https://api-sandbox.coinflow.cash/api/merchant/payments/enhanced/paymentId")
.header("Authorization", "<apiKey>")
.asString();
<?php
require_once('vendor/autoload.php');
$client = new \GuzzleHttp\Client();
$response = $client->request('GET', 'https://api-sandbox.coinflow.cash/api/merchant/payments/enhanced/paymentId', [
'headers' => [
'Authorization' => '<apiKey>',
],
]);
echo $response->getBody();
using RestSharp;
var client = new RestClient("https://api-sandbox.coinflow.cash/api/merchant/payments/enhanced/paymentId");
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "<apiKey>");
IRestResponse response = client.Execute(request);
import Foundation
let headers = ["Authorization": "<apiKey>"]
let request = NSMutableURLRequest(url: NSURL(string: "https://api-sandbox.coinflow.cash/api/merchant/payments/enhanced/paymentId")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error as Any)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse)
}
})
dataTask.resume()
Part 2 — Payout to a Bank Account
Four steps to pay a user out from your Coinflow Wallet:
- Get a session key for the withdrawer (server-side)
- Embed the Bank Authentication UI — Coinflow’s hosted UI handles KYC and bank linking in one flow (client-side)
- Get the withdrawer to retrieve the linked bank account token (server-side)
- Initiate the payout (server-side)
Step 1 — Get a session key
Generate a session key tied to your internal user ID. You’ll pass it into the bank-link URL in the next step.
Request
GET https://api-sandbox.coinflow.cash/api/auth/session-key
curl https://api-sandbox.coinflow.cash/api/auth/session-key \
-H "x-coinflow-auth-user-id: <apiKey>" \
-H "Content-Type: application/json"
import requests
url = "https://api-sandbox.coinflow.cash/api/auth/session-key"
payload = {}
headers = {
"x-coinflow-auth-user-id": "<apiKey>",
"Content-Type": "application/json"
}
response = requests.get(url, json=payload, headers=headers)
print(response.json())
const url = 'https://api-sandbox.coinflow.cash/api/auth/session-key';
const options = {
method: 'GET',
headers: {'x-coinflow-auth-user-id': '<apiKey>', 'Content-Type': 'application/json'},
body: '{}'
};
try {
const response = await fetch(url, options);
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api-sandbox.coinflow.cash/api/auth/session-key"
payload := strings.NewReader("{}")
req, _ := http.NewRequest("GET", url, payload)
req.Header.Add("x-coinflow-auth-user-id", "<apiKey>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
require 'uri'
require 'net/http'
url = URI("https://api-sandbox.coinflow.cash/api/auth/session-key")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["x-coinflow-auth-user-id"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{}"
response = http.request(request)
puts response.read_body
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;
HttpResponse<String> response = Unirest.get("https://api-sandbox.coinflow.cash/api/auth/session-key")
.header("x-coinflow-auth-user-id", "<apiKey>")
.header("Content-Type", "application/json")
.body("{}")
.asString();
<?php
require_once('vendor/autoload.php');
$client = new \GuzzleHttp\Client();
$response = $client->request('GET', 'https://api-sandbox.coinflow.cash/api/auth/session-key', [
'body' => '{}',
'headers' => [
'Content-Type' => 'application/json',
'x-coinflow-auth-user-id' => '<apiKey>',
],
]);
echo $response->getBody();
using RestSharp;
var client = new RestClient("https://api-sandbox.coinflow.cash/api/auth/session-key");
var request = new RestRequest(Method.GET);
request.AddHeader("x-coinflow-auth-user-id", "<apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
import Foundation
let headers = [
"x-coinflow-auth-user-id": "<apiKey>",
"Content-Type": "application/json"
]
let parameters = [] as [String : Any]
let postData = JSONSerialization.data(withJSONObject: parameters, options: [])
let request = NSMutableURLRequest(url: NSURL(string: "https://api-sandbox.coinflow.cash/api/auth/session-key")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error as Any)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse)
}
})
dataTask.resume()
Response (200)
{
"key": "a1b2c3d4e5f67890abcdef1234567890"
}
Step 2 — Embed the Bank Authentication UI
Coinflow’s hosted UI handles KYC verification and bank/card linking end-to-end — you don’t have to build any of it. Drop the URL below into an iframe, replacing YOUR_MERCHANT_ID and SESSION_KEY_FROM_STEP_1 with your own values.
<iframe
src="https://sandbox.coinflow.cash/user/withdraw/YOUR_MERCHANT_ID?sessionKey=SESSION_KEY_FROM_STEP_1&bankAccountLinkRedirect=https%3A%2F%2Fyourapp.com%2Fpayout-complete"
allow="payment"
style="width:100%;height:600px;border:none;"
/>
| Parameter | Description |
|---|---|
sessionKey |
The JWT from Step 1. |
bankAccountLinkRedirect |
URL-encoded URL Coinflow redirects to once the user finishes linking. |
When the user completes linking, the iframe emits a postMessage with method: "accountLinked". Listen for it on your page so you know when to advance the flow. See Listen for Successful Account Link Messages for a complete example.
Production URL: swap sandbox.coinflow.cash for coinflow.cash when going live. See the full Bank Authentication UI guide for show-only-cards/show-only-banks options and iframe origin configuration.
Step 3 — Get the withdrawer
After the user finishes linking, fetch the withdrawer record to retrieve the linked bank account’s token. You’ll pass this token into the payout request in Step 4.
Request
GET https://api-sandbox.coinflow.cash/api/withdraw
curl https://api-sandbox.coinflow.cash/api/withdraw \
-H "x-coinflow-auth-wallet: <apiKey>"
import requests
url = "https://api-sandbox.coinflow.cash/api/withdraw"
headers = {"x-coinflow-auth-wallet": "<apiKey>"}
response = requests.get(url, headers=headers)
print(response.json())
const url = 'https://api-sandbox.coinflow.cash/api/withdraw';
const options = {method: 'GET', headers: {'x-coinflow-auth-wallet': '<apiKey>'}};
try {
const response = await fetch(url, options);
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api-sandbox.coinflow.cash/api/withdraw"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("x-coinflow-auth-wallet", "<apiKey>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
require 'uri'
require 'net/http'
url = URI("https://api-sandbox.coinflow.cash/api/withdraw")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["x-coinflow-auth-wallet"] = '<apiKey>'
response = http.request(request)
puts response.read_body
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;
HttpResponse<String> response = Unirest.get("https://api-sandbox.coinflow.cash/api/withdraw")
.header("x-coinflow-auth-wallet", "<apiKey>")
.asString();
<?php
require_once('vendor/autoload.php');
$client = new \GuzzleHttp\Client();
$response = $client->request('GET', 'https://api-sandbox.coinflow.cash/api/withdraw', [
'headers' => [
'x-coinflow-auth-wallet' => '<apiKey>',
],
]);
echo $response->getBody();
using RestSharp;
var client = new RestClient("https://api-sandbox.coinflow.cash/api/withdraw");
var request = new RestRequest(Method.GET);
request.AddHeader("x-coinflow-auth-wallet", "<apiKey>");
IRestResponse response = client.Execute(request);
import Foundation
let headers = ["x-coinflow-auth-wallet": "<apiKey>"]
let request = NSMutableURLRequest(url: NSURL(string: "https://api-sandbox.coinflow.cash/api/withdraw")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error as Any)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse)
}
})
dataTask.resume()
Response (200)
{
"withdrawer": {
"_id": "string",
"wallet": "string",
"blockchain": "solana",
"wallets": [
{
"wallet": "string",
"blockchain": "solana"
}
],
"email": "string",
"availability": {
"status": "Functional",
"reason": "string",
"editor": "string",
"updatedAt": "2024-01-15T09:30:00Z"
},
"currency": "USD",
"merchant": "string",
"verification": {
"reference": "string",
"status": "pending",
"vendor": "middesk",
"name": "string",
"attested": true,
"shareToken": "string",
"shareTokenStatus": "string",
"sessionToken": "string",
"rejectionReasons": [
"string"
]
},
"riskScoreOverride": true,
"country": "string",
"bankAccounts": [
{
"last4": "string",
"accountHash": "string",
"alias": "string",
"token": "string",
"reference": "string",
"accountNumberOnlyHash": "string",
"isDeleted": true,
"isTokenized": true,
"accountNumber": "string"
}
],
"cards": [
{
"last4": "string",
"token": "string",
"type": "VISA",
"disbursementStatus": "Immediate",
"createdAt": "2024-01-15T09:30:00Z",
"isDeleted": true,
"currency": "USD",
"nameOnCard": "string",
"expMonth": "string",
"expYear": "string"
}
],
"ibans": [
{
"last4": "string",
"accountHash": "string",
"alias": "string",
"token": "string",
"reference": "string",
"sortCode": "string",
"bic": "string"
}
],
"swifts": [
{
"alias": "string",
"token": "string",
"reference": "string",
"last4": "string",
"accountHash": "string",
"beneficiaryBankName": "string",
"beneficiaryBankAddress": {
"address1": "string",
"city": "string",
"state": "string",
"zip": "string",
"country": "string",
"address2": "string"
},
"swiftCode": "string",
"intermediaryBank": {
"swiftCode": "string",
"bankAddress": {
"address1": "string",
"city": "string",
"state": "string",
"zip": "string",
"country": "string",
"address2": "string"
},
"bankName": "string"
}
}
],
"pixes": [
{
"key": "string",
"accountHash": "string",
"token": "string"
}
],
"efts": [
{
"accountHash": "string",
"alias": "string",
"token": "string",
"reference": "string",
"mask": "string",
"isDeleted": true,
"accountNumber": "string",
"institutionId": "string",
"institution": "string",
"transit_number": "string"
}
],
"mobiles": [
{
"alias": "string",
"token": "string",
"type": "mobile",
"genus": "applepay",
"disbursementStatus": "Immediate",
"currency": "USD",
"deletedAt": "2024-01-15T09:30:00Z",
"expMonth": "string",
"expYear": "string"
}
],
"p2cAvailable": true,
"applePayAvailable": true,
"bankCurrencyOptions": [
"USD"
],
"freeWithdrawSpeeds": [
"asap"
],
"originalCurrency": "USD",
"geoBlockOverride": {
"reason": "string",
"setBy": "string",
"setAt": "2024-01-15T09:30:00Z",
"expiresAt": "2024-01-15T09:30:00Z"
},
"blockCardReuseExempt": true,
"createdAt": "2024-01-15T09:30:00Z",
"venmo": {
"alias": "string",
"token": "string",
"type": "venmo",
"isDeleted": true
},
"paypal": {
"alias": "string",
"token": "string",
"type": "paypal",
"isDeleted": true
},
"interac": {
"alias": "string",
"token": "string",
"type": "interac",
"isDeleted": true
}
}
}
The response includes bankAccounts[] (and/or cards[], ibans[], pixes[] depending on what the user linked). Grab bankAccounts[0].token — that’s the account identifier you’ll use next.
Step 4 — Initiate the payout
Send the payout from your Coinflow Wallet to the withdrawer’s linked bank account. This example sends $3.00 via the fastest available rail.
Request
POST https://api-sandbox.coinflow.cash/api/merchant/withdraws/payout/delegated
curl -X POST https://api-sandbox.coinflow.cash/api/merchant/withdraws/payout/delegated \
-H "Authorization: <apiKey>" \
-H "Content-Type: application/json" \
-d '{
"speed": "same_day",
"account": "card_4f3a2b1c9d8e7f6a",
"userId": "user_1234567890abcdef",
"idempotencyKey": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"amount": {
"cents": 25000
}
}'
import requests
url = "https://api-sandbox.coinflow.cash/api/merchant/withdraws/payout/delegated"
payload = {
"speed": "same_day",
"account": "card_4f3a2b1c9d8e7f6a",
"userId": "user_1234567890abcdef",
"idempotencyKey": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"amount": { "cents": 25000 }
}
headers = {
"Authorization": "<apiKey>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.json())
const url = 'https://api-sandbox.coinflow.cash/api/merchant/withdraws/payout/delegated';
const options = {
method: 'POST',
headers: {Authorization: '<apiKey>', 'Content-Type': 'application/json'},
body: '{"speed":"same_day","account":"card_4f3a2b1c9d8e7f6a","userId":"user_1234567890abcdef","idempotencyKey":"3fa85f64-5717-4562-b3fc-2c963f66afa6","amount":{"cents":25000}}'
};
try {
const response = await fetch(url, options);
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api-sandbox.coinflow.cash/api/merchant/withdraws/payout/delegated"
payload := strings.NewReader("{\n \"speed\": \"same_day\",\n \"account\": \"card_4f3a2b1c9d8e7f6a\",\n \"userId\": \"user_1234567890abcdef\",\n \"idempotencyKey\": \"3fa85f64-5717-4562-b3fc-2c963f66afa6\",\n \"amount\": {\n \"cents\": 25000\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<apiKey>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
require 'uri'
require 'net/http'
url = URI("https://api-sandbox.coinflow.cash/api/merchant/withdraws/payout/delegated")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"speed\": \"same_day\",\n \"account\": \"card_4f3a2b1c9d8e7f6a\",\n \"userId\": \"user_1234567890abcdef\",\n \"idempotencyKey\": \"3fa85f64-5717-4562-b3fc-2c963f66afa6\",\n \"amount\": {\n \"cents\": 25000\n }\n}"
response = http.request(request)
puts response.read_body
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;
HttpResponse<String> response = Unirest.post("https://api-sandbox.coinflow.cash/api/merchant/withdraws/payout/delegated")
.header("Authorization", "<apiKey>")
.header("Content-Type", "application/json")
.body("{\n \"speed\": \"same_day\",\n \"account\": \"card_4f3a2b1c9d8e7f6a\",\n \"userId\": \"user_1234567890abcdef\",\n \"idempotencyKey\": \"3fa85f64-5717-4562-b3fc-2c963f66afa6\",\n \"amount\": {\n \"cents\": 25000\n }\n}")
.asString();
<?php
require_once('vendor/autoload.php');
$client = new \GuzzleHttp\Client();
$response = $client->request('POST', 'https://api-sandbox.coinflow.cash/api/merchant/withdraws/payout/delegated', [
'body' => '{
"speed": "same_day",
"account": "card_4f3a2b1c9d8e7f6a",
"userId": "user_1234567890abcdef",
"idempotencyKey": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"amount": {
"cents": 25000
}
}',
'headers' => [
'Authorization' => '<apiKey>',
'Content-Type' => 'application/json',
],
]);
echo $response->getBody();
using RestSharp;
var client = new RestClient("https://api-sandbox.coinflow.cash/api/merchant/withdraws/payout/delegated");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "<apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n \"speed\": \"same_day\",\n \"account\": \"card_4f3a2b1c9d8e7f6a\",\n \"userId\": \"user_1234567890abcdef\",\n \"idempotencyKey\": \"3fa85f64-5717-4562-b3fc-2c963f66afa6\",\n \"amount\": {\n \"cents\": 25000\n }\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
import Foundation
let headers = [
"Authorization": "<apiKey>",
"Content-Type": "application/json"
]
let parameters = [
"speed": "same_day",
"account": "card_4f3a2b1c9d8e7f6a",
"userId": "user_1234567890abcdef",
"idempotencyKey": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"amount": ["cents": 25000]
] as [String : Any]
let postData = JSONSerialization.data(withJSONObject: parameters, options: [])
let request = NSMutableURLRequest(url: NSURL(string: "https://api-sandbox.coinflow.cash/api/merchant/withdraws/payout/delegated")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error as Any)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse)
}
})
dataTask.resume()
Response (200)
{
"effectiveSpeed": "same_day",
"signature": "3045022100dff9a1b2c3d4e5f67890123456789abcdef0123456789abcdef0123456789ab02207c9e8f7a6b5c4d3e2f1a0b9c8d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3a2b1c0d9e"
}
Use the withdrawalId to check status at any time:
Request
GET https://api-sandbox.coinflow.cash/api/merchant/withdraws/{withdrawalId}
curl https://api-sandbox.coinflow.cash/api/merchant/withdraws/withdrawalId \
-H "Authorization: <apiKey>"
import requests
url = "https://api-sandbox.coinflow.cash/api/merchant/withdraws/withdrawalId"
headers = {"Authorization": "<apiKey>"}
response = requests.get(url, headers=headers)
print(response.json())
const url = 'https://api-sandbox.coinflow.cash/api/merchant/withdraws/withdrawalId';
const options = {method: 'GET', headers: {Authorization: '<apiKey>'}};
try {
const response = await fetch(url, options);
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api-sandbox.coinflow.cash/api/merchant/withdraws/withdrawalId"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "<apiKey>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
require 'uri'
require 'net/http'
url = URI("https://api-sandbox.coinflow.cash/api/merchant/withdraws/withdrawalId")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = '<apiKey>'
response = http.request(request)
puts response.read_body
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;
HttpResponse<String> response = Unirest.get("https://api-sandbox.coinflow.cash/api/merchant/withdraws/withdrawalId")
.header("Authorization", "<apiKey>")
.asString();
<?php
require_once('vendor/autoload.php');
$client = new \GuzzleHttp\Client();
$response = $client->request('GET', 'https://api-sandbox.coinflow.cash/api/merchant/withdraws/withdrawalId', [
'headers' => [
'Authorization' => '<apiKey>',
],
]);
echo $response->getBody();
using RestSharp;
var client = new RestClient("https://api-sandbox.coinflow.cash/api/merchant/withdraws/withdrawalId");
var request = new RestRequest(Method.GET);
request.AddHeader("Authorization", "<apiKey>");
IRestResponse response = client.Execute(request);
import Foundation
let headers = ["Authorization": "<apiKey>"]
let request = NSMutableURLRequest(url: NSURL(string: "https://api-sandbox.coinflow.cash/api/merchant/withdraws/withdrawalId")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error as Any)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse)
}
})
dataTask.resume()
That’s a payout. Funds are on their way to the withdrawer’s bank account. The asap speed uses RTP for instant delivery where available, falling back to Same-Day ACH. Learn about payout speeds →
What’s next
Testing Guide
Test card numbers, bank accounts, and edge cases for sandbox testing.
Webhooks
Receive real-time notifications when payments and payouts change status.
Chargeback Protection
Add fraud scoring and chargeback coverage to card payments.
Payout Speeds
Understand RTP, Same-Day ACH, and standard ACH options and fees.