Payouts
Execute a payout
Lock a quote and start the payout in one call.
POST
/
api
/
rfq
/
execute
Execute a payout
curl --request POST \
--url https://api.example.com/api/rfq/execute \
--header 'Authorization: <authorization>' \
--header 'Content-Type: application/json' \
--data '
{
"fromCurrency": "<string>",
"toCurrency": "<string>",
"amount": 123,
"recipientId": "<string>",
"targetAmount": 123,
"recipientPaymentMethodId": "<string>",
"routeProtocol": "<string>",
"paymentPurpose": "<string>",
"reference": "<string>",
"supportingDocumentKey": "<string>"
}
'import requests
url = "https://api.example.com/api/rfq/execute"
payload = {
"fromCurrency": "<string>",
"toCurrency": "<string>",
"amount": 123,
"recipientId": "<string>",
"targetAmount": 123,
"recipientPaymentMethodId": "<string>",
"routeProtocol": "<string>",
"paymentPurpose": "<string>",
"reference": "<string>",
"supportingDocumentKey": "<string>"
}
headers = {
"Authorization": "<authorization>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<authorization>', 'Content-Type': 'application/json'},
body: JSON.stringify({
fromCurrency: '<string>',
toCurrency: '<string>',
amount: 123,
recipientId: '<string>',
targetAmount: 123,
recipientPaymentMethodId: '<string>',
routeProtocol: '<string>',
paymentPurpose: '<string>',
reference: '<string>',
supportingDocumentKey: '<string>'
})
};
fetch('https://api.example.com/api/rfq/execute', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/api/rfq/execute",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'fromCurrency' => '<string>',
'toCurrency' => '<string>',
'amount' => 123,
'recipientId' => '<string>',
'targetAmount' => 123,
'recipientPaymentMethodId' => '<string>',
'routeProtocol' => '<string>',
'paymentPurpose' => '<string>',
'reference' => '<string>',
'supportingDocumentKey' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/api/rfq/execute"
payload := strings.NewReader("{\n \"fromCurrency\": \"<string>\",\n \"toCurrency\": \"<string>\",\n \"amount\": 123,\n \"recipientId\": \"<string>\",\n \"targetAmount\": 123,\n \"recipientPaymentMethodId\": \"<string>\",\n \"routeProtocol\": \"<string>\",\n \"paymentPurpose\": \"<string>\",\n \"reference\": \"<string>\",\n \"supportingDocumentKey\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.example.com/api/rfq/execute")
.header("Authorization", "<authorization>")
.header("Content-Type", "application/json")
.body("{\n \"fromCurrency\": \"<string>\",\n \"toCurrency\": \"<string>\",\n \"amount\": 123,\n \"recipientId\": \"<string>\",\n \"targetAmount\": 123,\n \"recipientPaymentMethodId\": \"<string>\",\n \"routeProtocol\": \"<string>\",\n \"paymentPurpose\": \"<string>\",\n \"reference\": \"<string>\",\n \"supportingDocumentKey\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/api/rfq/execute")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<authorization>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"fromCurrency\": \"<string>\",\n \"toCurrency\": \"<string>\",\n \"amount\": 123,\n \"recipientId\": \"<string>\",\n \"targetAmount\": 123,\n \"recipientPaymentMethodId\": \"<string>\",\n \"routeProtocol\": \"<string>\",\n \"paymentPurpose\": \"<string>\",\n \"reference\": \"<string>\",\n \"supportingDocumentKey\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"payoutId": "8b1a4c1d-7a3c-4a08-94ad-9f1ef1e0c3a2",
"quoteId": "4c2b7e8f-3a3c-4a08-94ad-1234567890ab",
"status": "accepted",
"toAmount": 4425.00,
"partnerFxRate": 4.4250,
"effectiveRate": 4.4070,
"fees": { "teelFee": 4.05, "teelFeeBps": 40, "partnerFee": 0, "gasFee": 0 },
"bankingInstructions": {
"accountNumber": "1234567890",
"bankName": "Maybank",
"swiftCode": "MBBEMYKL"
}
}
}
Lock + start a payout in one call. Pick the matching tab — shapes differ by corridor.
Response:
{ "success": true, "data": { … } }.
Scope
payouts:write · rate-limitedHeaders
string
required
API key.
string
Optional. 8–255 url-safe characters. 24h replay window; mismatched bodies on the same key return
409 IDEMPOTENCY_KEY_REUSED.Common fields
string
required
Source currency. ISO 4217 for fiat, ticker for stablecoin.
string
required
Destination currency.
number
required
Source amount. Must be
> 0.string
required
UUID from
POST /recipients.number
Exact-output target. When set, sizes
amount upstream so the recipient receives exactly this much.string
UUID of the recipient PM, when more than one exists.
string
Opaque route token (
rt_…) from a /quotes/* call. Pins the route.string
Partner-side classification (e.g.
salary).string
Free-text reference echoed back on reads.
string
S3 key for an attached supporting document. Required by some compliance-bearing onramp flows.
Request body
Switch tabs for the type-specific fields and response shape.- Fiat-to-fiat
- Stablecoin-to-fiat
- Onramp (fiat → stablecoin)
Send fiat from your stored payment method; recipient receives fiat.
string
required
UUID of your funding source.
Response
string
UUID of the new payout. Use with
GET /payouts/{id}.string
UUID of the locked quote. Use with
GET /rfq/status/{quoteId}.string
Hard-coded
accepted on success.number
Destination amount the recipient will receive.
number
Quoted FX rate, pre-fees.
number
Effective rate net of fees.
object
Map of
string → string describing where to send the source fiat. Keys vary by rail (accountNumber, bankName, swiftCode, etc.).{
"success": true,
"data": {
"payoutId": "8b1a4c1d-7a3c-4a08-94ad-9f1ef1e0c3a2",
"quoteId": "4c2b7e8f-3a3c-4a08-94ad-1234567890ab",
"status": "accepted",
"toAmount": 4425.00,
"partnerFxRate": 4.4250,
"effectiveRate": 4.4070,
"fees": { "teelFee": 4.05, "teelFeeBps": 40, "partnerFee": 0, "gasFee": 0 },
"bankingInstructions": {
"accountNumber": "1234567890",
"bankName": "Maybank",
"swiftCode": "MBBEMYKL"
}
}
}
Send stablecoin from your wallet; recipient receives fiat. The response carries escrow deposit instructions — your wallet must send the source stablecoin to After the response: your wallet sends
escrowAddress.string
required
Source wallet address.
Response
string
UUID of the new payout.
string
UUID of the locked quote.
string
Always
awaiting_escrow_deposit until the deposit lands.number
Destination fiat amount.
number
Quoted FX rate, pre-fees.
number
Effective rate net of fees.
string
On-chain address your wallet must send the source stablecoin to.
string
Atomic-units integer string — parse with
BigInt. JSON numbers lose precision for 18-decimal tokens. Example: "300000" = 0.3 USDC at 6 decimals.string
ERC-20 contract address of the source stablecoin.
integer
EVM chain id of the deposit address.
{
"success": true,
"data": {
"payoutId": "8b1a4c1d-7a3c-4a08-94ad-9f1ef1e0c3a2",
"quoteId": "4c2b7e8f-3a3c-4a08-94ad-1234567890ab",
"status": "awaiting_escrow_deposit",
"toAmount": 4425.00,
"partnerFxRate": 4.4250,
"effectiveRate": 4.4070,
"fees": { "teelFee": 0.91, "teelFeeBps": 40, "partnerFee": 0, "gasFee": 0.05 },
"escrowAddress": "0xAbcDef0123456789AbcDef0123456789AbcDef01",
"depositAmountAtomic": "227250000",
"tokenAddress": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
"chainId": 137
}
}
depositAmountAtomic of tokenAddress to escrowAddress on chainId. The engine auto-detects the transfer; if it misses, fall back to POST /rfq/confirm-escrow-deposit with the tx hash.Send fiat from your stored PM; recipient receives stablecoin on-chain.
string
required
UUID of your funding source.
Response
string
UUID of the new payout.
string
UUID of the locked quote.
string
Passed through from the onramp route.
number
Destination stablecoin amount.
number
Quoted FX rate, pre-fees.
number
Effective rate net of fees.
string
Hosted payment page — redirect the user here to complete the onramp.
object
When the rail settles by bank transfer instead of hosted page. Mutually exclusive with
paymentUrl.{
"success": true,
"data": {
"payoutId": "8b1a4c1d-7a3c-4a08-94ad-9f1ef1e0c3a2",
"quoteId": "4c2b7e8f-3a3c-4a08-94ad-1234567890ab",
"status": "pending",
"toAmount": 990.50,
"partnerFxRate": 1.0,
"effectiveRate": 0.9905,
"fees": { "teelFee": 9.50, "teelFeeBps": 95, "partnerFee": 0, "gasFee": 0 },
"paymentUrl": "https://pay.example.com/sess_abc123"
}
}
Fees
number
Teel’s fee, in
fromCurrency.integer
Teel’s fee in basis points (1 bp = 0.01%).
number
Your markup.
0 unless configured.number
On-chain gas (S2F / stablecoin flows).
Errors
| Status | Code | When |
|---|---|---|
400 | INVALID_JSON / INVALID_PARAMETER | Body malformed, required field missing, or a UUID doesn’t belong to your account. |
401 | UNAUTHORIZED | Missing or invalid key. |
403 | FORBIDDEN | Key lacks payouts:write. |
409 | IDEMPOTENCY_KEY_REUSED / IDEMPOTENCY_REQUEST_IN_FLIGHT | Idempotency conflict — back off per Retry-After for in-flight. |
429 | RATE_LIMITED | Per-key rate limit. |
Next steps
PollGET /rfq/status/{quoteId} or — preferred — subscribe to payout.status.updated and payout.delivered via Webhooks.⌘I
Execute a payout
curl --request POST \
--url https://api.example.com/api/rfq/execute \
--header 'Authorization: <authorization>' \
--header 'Content-Type: application/json' \
--data '
{
"fromCurrency": "<string>",
"toCurrency": "<string>",
"amount": 123,
"recipientId": "<string>",
"targetAmount": 123,
"recipientPaymentMethodId": "<string>",
"routeProtocol": "<string>",
"paymentPurpose": "<string>",
"reference": "<string>",
"supportingDocumentKey": "<string>"
}
'import requests
url = "https://api.example.com/api/rfq/execute"
payload = {
"fromCurrency": "<string>",
"toCurrency": "<string>",
"amount": 123,
"recipientId": "<string>",
"targetAmount": 123,
"recipientPaymentMethodId": "<string>",
"routeProtocol": "<string>",
"paymentPurpose": "<string>",
"reference": "<string>",
"supportingDocumentKey": "<string>"
}
headers = {
"Authorization": "<authorization>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: '<authorization>', 'Content-Type': 'application/json'},
body: JSON.stringify({
fromCurrency: '<string>',
toCurrency: '<string>',
amount: 123,
recipientId: '<string>',
targetAmount: 123,
recipientPaymentMethodId: '<string>',
routeProtocol: '<string>',
paymentPurpose: '<string>',
reference: '<string>',
supportingDocumentKey: '<string>'
})
};
fetch('https://api.example.com/api/rfq/execute', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/api/rfq/execute",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'fromCurrency' => '<string>',
'toCurrency' => '<string>',
'amount' => 123,
'recipientId' => '<string>',
'targetAmount' => 123,
'recipientPaymentMethodId' => '<string>',
'routeProtocol' => '<string>',
'paymentPurpose' => '<string>',
'reference' => '<string>',
'supportingDocumentKey' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: <authorization>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/api/rfq/execute"
payload := strings.NewReader("{\n \"fromCurrency\": \"<string>\",\n \"toCurrency\": \"<string>\",\n \"amount\": 123,\n \"recipientId\": \"<string>\",\n \"targetAmount\": 123,\n \"recipientPaymentMethodId\": \"<string>\",\n \"routeProtocol\": \"<string>\",\n \"paymentPurpose\": \"<string>\",\n \"reference\": \"<string>\",\n \"supportingDocumentKey\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "<authorization>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.example.com/api/rfq/execute")
.header("Authorization", "<authorization>")
.header("Content-Type", "application/json")
.body("{\n \"fromCurrency\": \"<string>\",\n \"toCurrency\": \"<string>\",\n \"amount\": 123,\n \"recipientId\": \"<string>\",\n \"targetAmount\": 123,\n \"recipientPaymentMethodId\": \"<string>\",\n \"routeProtocol\": \"<string>\",\n \"paymentPurpose\": \"<string>\",\n \"reference\": \"<string>\",\n \"supportingDocumentKey\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/api/rfq/execute")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = '<authorization>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"fromCurrency\": \"<string>\",\n \"toCurrency\": \"<string>\",\n \"amount\": 123,\n \"recipientId\": \"<string>\",\n \"targetAmount\": 123,\n \"recipientPaymentMethodId\": \"<string>\",\n \"routeProtocol\": \"<string>\",\n \"paymentPurpose\": \"<string>\",\n \"reference\": \"<string>\",\n \"supportingDocumentKey\": \"<string>\"\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"payoutId": "8b1a4c1d-7a3c-4a08-94ad-9f1ef1e0c3a2",
"quoteId": "4c2b7e8f-3a3c-4a08-94ad-1234567890ab",
"status": "accepted",
"toAmount": 4425.00,
"partnerFxRate": 4.4250,
"effectiveRate": 4.4070,
"fees": { "teelFee": 4.05, "teelFeeBps": 40, "partnerFee": 0, "gasFee": 0 },
"bankingInstructions": {
"accountNumber": "1234567890",
"bankName": "Maybank",
"swiftCode": "MBBEMYKL"
}
}
}