Payouts
A payout pushes funds back to the instrument used for a previously settled payment — for example to pay out loyalty cashback, a rebate, or winnings to a customer you have already charged. You identify the original payment and Orchestr sends the money back to the same instrument, so you never handle the customer's account details.
A payout is created as a new payout transaction on the original payment's order and comes back with
status pending; the provider is called asynchronously and the final outcome arrives by webhook.
Payout vs refund
| Use a payout when… | Use a refund when… |
|---|---|
| You want to send money to the original instrument independent of the original amount — a payout may exceed the amount you charged. | You are reversing a payment, in full or in part, and never beyond the original amount. |
| The disbursement is a new event (cashback, rebate, prize), not a reversal. | The customer is getting their money back for the original purchase. |
1. Create a payout
Identify the original settled payment with exactly one of order_id, transaction_id, or
original_merchant_reference. Omit amount to pay out the original payment amount, or set it
explicitly (it may be larger than the original). merchant_reference is required and doubles as the
idempotency key.
This POST /v1/payouts call reuses the buildSignatureHeader helper from the
Signing guide (the signed string for a request is
timestamp + path + body). It assumes you've exported ORCHESTR_SECRET_KEY and
ORCHESTR_SIGNING_SECRET as shown in Getting started.
- cURL
- Node.js
- Python
- PHP
- Java
- C#
REQ_PATH="/v1/payouts"
BODY='{"transaction_id":"txn_9f8e7d6c","amount":"10.00","merchant_reference":"payout-1001","description":"Loyalty cashback"}'
TS=$(date +%s)
SIG=$(printf '%s' "${TS}${REQ_PATH}${BODY}" \
| openssl dgst -sha256 -hmac "$ORCHESTR_SIGNING_SECRET" | sed 's/^.*= //')
curl -sS -X POST "https://api.sandbox.upprove.com${REQ_PATH}" \
-H "Authorization: Bearer $ORCHESTR_SECRET_KEY" \
-H "X-Signature: t=${TS}, v1=${SIG}" \
-H "Content-Type: application/json" \
--data-raw "$BODY"
const path = '/v1/payouts';
const body = JSON.stringify({
transaction_id: 'txn_9f8e7d6c',
amount: '10.00',
merchant_reference: 'payout-1001',
description: 'Loyalty cashback',
});
const res = await fetch(`https://api.sandbox.upprove.com${path}`, {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.ORCHESTR_SECRET_KEY}`,
'X-Signature': buildSignatureHeader(process.env.ORCHESTR_SIGNING_SECRET, path, body),
'Content-Type': 'application/json',
},
body,
});
const payout = await res.json();
console.log(payout.order_id, payout.status);
import json
import requests
path = "/v1/payouts"
body = json.dumps(
{
"transaction_id": "txn_9f8e7d6c",
"amount": "10.00",
"merchant_reference": "payout-1001",
"description": "Loyalty cashback",
},
separators=(",", ":"),
)
resp = requests.post(
f"https://api.sandbox.upprove.com{path}",
data=body,
headers={
"Authorization": f"Bearer {ORCHESTR_SECRET_KEY}",
"X-Signature": build_signature_header(ORCHESTR_SIGNING_SECRET, path, body),
"Content-Type": "application/json",
},
)
payout = resp.json()
print(payout["order_id"], payout["status"])
<?php
$path = '/v1/payouts';
$body = json_encode([
'transaction_id' => 'txn_9f8e7d6c',
'amount' => '10.00',
'merchant_reference' => 'payout-1001',
'description' => 'Loyalty cashback',
], JSON_UNESCAPED_SLASHES);
$ch = curl_init("https://api.sandbox.upprove.com{$path}");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('ORCHESTR_SECRET_KEY'),
'X-Signature: ' . build_signature_header(getenv('ORCHESTR_SIGNING_SECRET'), $path, $body),
'Content-Type: application/json',
],
]);
$payout = json_decode(curl_exec($ch), true);
echo $payout['order_id'], ' ', $payout['status'];
String path = "/v1/payouts";
String body = """
{"transaction_id":"txn_9f8e7d6c","amount":"10.00",\
"merchant_reference":"payout-1001","description":"Loyalty cashback"}""";
HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.sandbox.upprove.com" + path))
.header("Authorization", "Bearer " + System.getenv("ORCHESTR_SECRET_KEY"))
.header("X-Signature", buildSignatureHeader(System.getenv("ORCHESTR_SIGNING_SECRET"), path, body))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body, StandardCharsets.UTF_8))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
var path = "/v1/payouts";
var body = "{\"transaction_id\":\"txn_9f8e7d6c\",\"amount\":\"10.00\"," +
"\"merchant_reference\":\"payout-1001\",\"description\":\"Loyalty cashback\"}";
using var http = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, $"https://api.sandbox.upprove.com{path}")
{
Content = new StringContent(body, Encoding.UTF8, "application/json"),
};
request.Headers.Add("Authorization", $"Bearer {Environment.GetEnvironmentVariable("ORCHESTR_SECRET_KEY")}");
request.Headers.Add("X-Signature",
BuildSignatureHeader(Environment.GetEnvironmentVariable("ORCHESTR_SIGNING_SECRET"), path, body));
var response = await http.SendAsync(request);
Console.WriteLine(await response.Content.ReadAsStringAsync());
Send one of order_id, transaction_id, or original_merchant_reference. Sending more than one
(or none) is rejected with ambiguous_original_transaction_target.
2. Read the response
A 201 Created returns a PayoutView. The X-Signature response header signs the body — verify
it before trusting the response (see Signing).
{
"order_id": "pou_5b4a3c2d",
"transaction_id": "txn_1c2b3a4d",
"original_transaction_id": "txn_9f8e7d6c",
"merchant_reference": "payout-1001",
"amount": 10.00,
"currency": "GBP",
"status": "pending",
"description": "Loyalty cashback",
"provider_transaction_id": null,
"additional_identifiers": [],
"created_at": "2024-06-22T10:00:00Z"
}
| Field | Description |
|---|---|
order_id | The payout order (pou_...) — the public payout identifier. |
transaction_id | The payout transaction attempt. |
original_transaction_id | The original settled payment whose instrument receives the funds. |
merchant_reference | Your reference for this payout. |
amount / currency | The payout amount and currency. |
status | Starts at pending; moves to completed or failed. |
provider_transaction_id | Settlement reference; null before settlement. |
additional_identifiers | Extra provider-side identifiers, each { "identifier": …, "type": "checkout" | "order" }. |
created_at | When the payout was created. |
Retrieving a payout later with GET /v1/payouts/{id} (where {id} is the payout transaction id,
txn_...) returns the fuller TransactionView — the same shape used across the
Transactions API, with processing/settlement amounts and failure details — rather than the slim
PayoutView above.
3. Handle the outcome
The create response tells you the initial state; the final outcome arrives asynchronously via
the payout.* webhooks (payout.paid, payout.failed, payout.canceled,
…). Alternatively, poll GET /v1/payouts/{id} until the transaction reaches a terminal status.
Next steps
- Signing — build and verify the
X-Signatureheader. - Transfers — send funds to an arbitrary bank account instead of the original instrument.
- Webhooks — the
payout.*events that report the final outcome. - The full API Reference for
POST /v1/payoutsandGET /v1/payouts/{id}.