curl --request POST \
--url https://api.grainfinance.co/v1/payouts \
--header 'Authorization: Basic <encoded-value>' \
--header 'Content-Type: application/json' \
--data '
{
"externalVendorId": "vendor_001",
"externalPayoutId": "inv_2026_001",
"currency": "USD",
"amount": 5000,
"reason": 2,
"description": "Payment for services"
}
'import requests
url = "https://api.grainfinance.co/v1/payouts"
payload = {
"externalVendorId": "vendor_001",
"externalPayoutId": "inv_2026_001",
"currency": "USD",
"amount": 5000,
"reason": 2,
"description": "Payment for services"
}
headers = {
"Authorization": "Basic <encoded-value>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Basic <encoded-value>', 'Content-Type': 'application/json'},
body: JSON.stringify({
externalVendorId: 'vendor_001',
externalPayoutId: 'inv_2026_001',
currency: 'USD',
amount: 5000,
reason: 2,
description: 'Payment for services'
})
};
fetch('https://api.grainfinance.co/v1/payouts', 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.grainfinance.co/v1/payouts",
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([
'externalVendorId' => 'vendor_001',
'externalPayoutId' => 'inv_2026_001',
'currency' => 'USD',
'amount' => 5000,
'reason' => 2,
'description' => 'Payment for services'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Basic <encoded-value>",
"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.grainfinance.co/v1/payouts"
payload := strings.NewReader("{\n \"externalVendorId\": \"vendor_001\",\n \"externalPayoutId\": \"inv_2026_001\",\n \"currency\": \"USD\",\n \"amount\": 5000,\n \"reason\": 2,\n \"description\": \"Payment for services\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Basic <encoded-value>")
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.grainfinance.co/v1/payouts")
.header("Authorization", "Basic <encoded-value>")
.header("Content-Type", "application/json")
.body("{\n \"externalVendorId\": \"vendor_001\",\n \"externalPayoutId\": \"inv_2026_001\",\n \"currency\": \"USD\",\n \"amount\": 5000,\n \"reason\": 2,\n \"description\": \"Payment for services\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.grainfinance.co/v1/payouts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Basic <encoded-value>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"externalVendorId\": \"vendor_001\",\n \"externalPayoutId\": \"inv_2026_001\",\n \"currency\": \"USD\",\n \"amount\": 5000,\n \"reason\": 2,\n \"description\": \"Payment for services\"\n}"
response = http.request(request)
puts response.read_body{
"id": "8173b9a7-ee61-413e-b9e3-7c04b2a067c5",
"externalPayoutId": "inv_2026_001",
"currency": "USD",
"amount": 5000,
"fee": 1.5,
"totalAmount": 5001.5,
"reason": 2,
"description": "<string>",
"validUntilTs": 1779805920,
"payoutMethod": "SEPA",
"vendorDetails": {
"vendorId": "1b0ada1e-fa1b-4cfe-a553-01b340c5650e",
"vendorName": "Acme Supplies Ltd",
"externalVendorId": "vendor_001",
"bankDetails": {
"accountHolderName": "Acme Payments Ltd",
"accountNumber": "8290041523",
"bankName": "JPMorgan Chase Bank, N.A.",
"bankAddress": "383 Madison Avenue, New York, NY 10017",
"bankCountry": "US",
"bankState": "NY",
"iban": "<string>",
"bicSwift": "CHASUS33XXX",
"routingCode": "<string>",
"bankCode": "<string>",
"branchCode": "<string>",
"sortCode": "560036",
"bsbCode": "082902"
}
}
}{
"message": "Can't perform this action",
"reason": "Supplied object is in *Cancelled* state, which prevents performing this action"
}{
"message": "Can't perform this action",
"reason": "Supplied object is in *Cancelled* state, which prevents performing this action"
}{
"message": "Can't perform this action",
"reason": "Supplied object is in *Cancelled* state, which prevents performing this action"
}{
"message": "Can't perform this action",
"reason": "Supplied object is in *Cancelled* state, which prevents performing this action"
}Review Payout
Reviews a payout by creating a pending, time-limited proposal. The proposal must be submitted
via POST /v1/payouts/{id}/submit to execute the payment. No funds are moved
at this step.
curl --request POST \
--url https://api.grainfinance.co/v1/payouts \
--header 'Authorization: Basic <encoded-value>' \
--header 'Content-Type: application/json' \
--data '
{
"externalVendorId": "vendor_001",
"externalPayoutId": "inv_2026_001",
"currency": "USD",
"amount": 5000,
"reason": 2,
"description": "Payment for services"
}
'import requests
url = "https://api.grainfinance.co/v1/payouts"
payload = {
"externalVendorId": "vendor_001",
"externalPayoutId": "inv_2026_001",
"currency": "USD",
"amount": 5000,
"reason": 2,
"description": "Payment for services"
}
headers = {
"Authorization": "Basic <encoded-value>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Basic <encoded-value>', 'Content-Type': 'application/json'},
body: JSON.stringify({
externalVendorId: 'vendor_001',
externalPayoutId: 'inv_2026_001',
currency: 'USD',
amount: 5000,
reason: 2,
description: 'Payment for services'
})
};
fetch('https://api.grainfinance.co/v1/payouts', 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.grainfinance.co/v1/payouts",
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([
'externalVendorId' => 'vendor_001',
'externalPayoutId' => 'inv_2026_001',
'currency' => 'USD',
'amount' => 5000,
'reason' => 2,
'description' => 'Payment for services'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Basic <encoded-value>",
"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.grainfinance.co/v1/payouts"
payload := strings.NewReader("{\n \"externalVendorId\": \"vendor_001\",\n \"externalPayoutId\": \"inv_2026_001\",\n \"currency\": \"USD\",\n \"amount\": 5000,\n \"reason\": 2,\n \"description\": \"Payment for services\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Basic <encoded-value>")
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.grainfinance.co/v1/payouts")
.header("Authorization", "Basic <encoded-value>")
.header("Content-Type", "application/json")
.body("{\n \"externalVendorId\": \"vendor_001\",\n \"externalPayoutId\": \"inv_2026_001\",\n \"currency\": \"USD\",\n \"amount\": 5000,\n \"reason\": 2,\n \"description\": \"Payment for services\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.grainfinance.co/v1/payouts")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Basic <encoded-value>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"externalVendorId\": \"vendor_001\",\n \"externalPayoutId\": \"inv_2026_001\",\n \"currency\": \"USD\",\n \"amount\": 5000,\n \"reason\": 2,\n \"description\": \"Payment for services\"\n}"
response = http.request(request)
puts response.read_body{
"id": "8173b9a7-ee61-413e-b9e3-7c04b2a067c5",
"externalPayoutId": "inv_2026_001",
"currency": "USD",
"amount": 5000,
"fee": 1.5,
"totalAmount": 5001.5,
"reason": 2,
"description": "<string>",
"validUntilTs": 1779805920,
"payoutMethod": "SEPA",
"vendorDetails": {
"vendorId": "1b0ada1e-fa1b-4cfe-a553-01b340c5650e",
"vendorName": "Acme Supplies Ltd",
"externalVendorId": "vendor_001",
"bankDetails": {
"accountHolderName": "Acme Payments Ltd",
"accountNumber": "8290041523",
"bankName": "JPMorgan Chase Bank, N.A.",
"bankAddress": "383 Madison Avenue, New York, NY 10017",
"bankCountry": "US",
"bankState": "NY",
"iban": "<string>",
"bicSwift": "CHASUS33XXX",
"routingCode": "<string>",
"bankCode": "<string>",
"branchCode": "<string>",
"sortCode": "560036",
"bsbCode": "082902"
}
}
}{
"message": "Can't perform this action",
"reason": "Supplied object is in *Cancelled* state, which prevents performing this action"
}{
"message": "Can't perform this action",
"reason": "Supplied object is in *Cancelled* state, which prevents performing this action"
}{
"message": "Can't perform this action",
"reason": "Supplied object is in *Cancelled* state, which prevents performing this action"
}{
"message": "Can't perform this action",
"reason": "Supplied object is in *Cancelled* state, which prevents performing this action"
}Authorizations
Basic authentication using the partner API keys from https://console.grainfinance.co/keys
Body
Request body for POST /v1/payouts (review). Creates a pending, time-limited payout proposal
that must be submitted via POST /v1/payouts/{id}/submit before it expires.
Your own reference id for the vendor receiving the payout.
^[A-Za-z0-9_.\-]{1,64}$"vendor_001"
Your own reference number for this payout.
^[A-Za-z0-9_.\-]{1,64}$"inv_2026_001"
The alpha-3 ISO 4217 currency code of the payout.
^[A-Z]{3}$"USD"
The payout amount, denoted in currency.
5000
The compliance reason categorising this payout, as a numeric code:
1 - Intercompany Payment 2 - Purchase of Good(s) 3 - Purchase of Service(s) 4 - Transferring to Own Accounts 5 - Valid Vendor Purchases
1, 2, 3, 4, 5 2
Optional free-form description for the payout.
"Payment for services"
Response
Payout created
A pending payout proposal, returned by POST /v1/payouts (review). Submit it via
POST /v1/payouts/{id}/submit to execute the payout.
The id of the payout proposal within the Grain platform.
[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-4[0-9A-Fa-f]{3}-[89ABab][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}"8173b9a7-ee61-413e-b9e3-7c04b2a067c5"
The payout id, as identified within your system.
^[A-Za-z0-9_.\-]{1,64}$"inv_2026_001"
The alpha-3 ISO 4217 currency code of the payout.
^[A-Z]{3}$"USD"
The payout amount, denoted in currency.
5000
The fee applied to this payout, denoted in currency.
1.5
Total amount that will leave the wallet (amount + fee), denoted in currency.
5001.5
The compliance reason categorising this payout, as a numeric code:
1 - Intercompany Payment 2 - Purchase of Good(s) 3 - Purchase of Service(s) 4 - Transferring to Own Accounts 5 - Valid Vendor Purchases
1, 2, 3, 4, 5 2
Free-form description supplied when initiating the payout.
When the proposal expires if not submitted, as a Unix timestamp in seconds.
1779805920
The payment method that will be used to send the payout.
SWIFT, SEPA, WIRE, ACH, FPS, CA ACSS, NPP, IBG, BECS, CCASS, TEF, UAEFTS, SIC, CERTIS, DKSMC, NICS, SORBNET, RIX, SAMOS, ZAHAV, SARIE, QPS "SEPA"
The vendor receiving the payout, including their bank account details.
Show child attributes
Show child attributes