Ask for an invitation
Menu

Endpoint

Request a payout batch

POST/v1/payout_batches

Verified merchants only. Up to 200 rows, each a payout body. The whole batch is validated first: one bad row rejects the batch with errors[] naming each row. One approval covers the batch; how many transactions that is depends on the network (one Safe transaction per row on an EVM network, with at most 50 rows from one EVM network, one transaction with an output per row on Bitcoin, Litecoin and Dogecoin, one per row on TRON) and each row has its own state. Batch states are derived from the rows: awaiting_approval, then processing, then completed (all rows confirmed) | completed_with_failures or needs_attention (a row is unknown); plus cancelled, which is how the API shows a batch that was withdrawn or rejected.

Scope a restricted key needs
payouts:write
Publishable key
Not allowed: secret or restricted key only
Idempotency-Key
Required. A request without it is refused.

Parameters

Parameters of Request a payout batch
ParameterType and placeMeaning
Orrerie-VersionstringheaderAPI version date. Default: the version the key was created with. Example 2026-10-11.
Idempotency-KeyrequiredstringheaderRequired here: a person at your business approves what this request asks for (money out, or a new saved address), and a retry with the same key never asks for it twice. 1 to 255 characters.

Request body

A JSON object. The body is required. A field the call does not list is refused with parameter_unknown.

Request body of Request a payout batch
FieldTypeMeaning
descriptionstringYour description of the batch.
rowsrequiredarray of values1 to 200 payout bodies.
metadataobjectYour own key-value data (rules in Metadata).

Example request

curl

curl https://api.orrerie.com/v1/payout_batches \
  -H "Authorization: Bearer $ORRERIE_SECRET_KEY" \
  -H "Idempotency-Key: 8f14e45f-ceea-467f-a9d2-6f1c2b3d4e5a" \
  -H "Content-Type: application/json" \
  -d '{
  "description": "October contractor payments",
  "rows": [
    {
      "source": {
        "asset": "base:usdc",
        "amount": "900.00"
      },
      "destination": "dst_6rxm2vzjct7x9svh1vr4bsf3",
      "reference": "C-01"
    },
    {
      "source": {
        "asset": "base:usdc",
        "amount": "800.00"
      },
      "destination": "dst_d89z78ck9qnsqf23d63yrhs8",
      "reference": "C-02"
    }
  ]
}'

JavaScript

const response = await fetch("https://api.orrerie.com/v1/payout_batches", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.ORRERIE_SECRET_KEY}`,
    "Idempotency-Key": "8f14e45f-ceea-467f-a9d2-6f1c2b3d4e5a",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "description": "October contractor payments",
    "rows": [
      {
        "source": {
          "asset": "base:usdc",
          "amount": "900.00"
        },
        "destination": "dst_6rxm2vzjct7x9svh1vr4bsf3",
        "reference": "C-01"
      },
      {
        "source": {
          "asset": "base:usdc",
          "amount": "800.00"
        },
        "destination": "dst_d89z78ck9qnsqf23d63yrhs8",
        "reference": "C-02"
      }
    ]
  }),
});
const result = await response.json();
if (!response.ok) throw new Error(`${result.error.code}: ${result.error.message}`);
console.log(result);

Python

import os
import requests

response = requests.post(
    "https://api.orrerie.com/v1/payout_batches",
    headers={"Authorization": f"Bearer {os.environ['ORRERIE_SECRET_KEY']}", "Idempotency-Key": "8f14e45f-ceea-467f-a9d2-6f1c2b3d4e5a"},
    json={
        "description": "October contractor payments",
        "rows": [
            {
                "source": {
                    "asset": "base:usdc",
                    "amount": "900.00",
                },
                "destination": "dst_6rxm2vzjct7x9svh1vr4bsf3",
                "reference": "C-01",
            },
            {
                "source": {
                    "asset": "base:usdc",
                    "amount": "800.00",
                },
                "destination": "dst_d89z78ck9qnsqf23d63yrhs8",
                "reference": "C-02",
            },
        ],
    },
    timeout=30,
)
result = response.json()
if not response.ok:
    raise RuntimeError(f"{result['error']['code']}: {result['error']['message']}")
print(result)

PHP

<?php
$request = curl_init('https://api.orrerie.com/v1/payout_batches');
curl_setopt_array($request, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('ORRERIE_SECRET_KEY'),
        'Idempotency-Key: 8f14e45f-ceea-467f-a9d2-6f1c2b3d4e5a',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
        'description' => 'October contractor payments',
        'rows' => [
            [
                'source' => [
                    'asset' => 'base:usdc',
                    'amount' => '900.00',
                ],
                'destination' => 'dst_6rxm2vzjct7x9svh1vr4bsf3',
                'reference' => 'C-01',
            ],
            [
                'source' => [
                    'asset' => 'base:usdc',
                    'amount' => '800.00',
                ],
                'destination' => 'dst_d89z78ck9qnsqf23d63yrhs8',
                'reference' => 'C-02',
            ],
        ],
    ]),
]);
$body = curl_exec($request);
if ($body === false) {
    throw new RuntimeException(curl_error($request));
}
$result = json_decode($body, true);
if (curl_getinfo($request, CURLINFO_HTTP_CODE) >= 400) {
    throw new RuntimeException($result['error']['code'] . ': ' . $result['error']['message']);
}
print_r($result);

Responses

202 Accepted

The request was accepted and is waiting: for a person to approve it in the dashboard, or to be built in the background. Nothing has been signed or sent. The answer is the payout_batch object. Headers: Orrerie-Request-Id.

Example response, 202

{
  "id": "pob_b1xkj45rqwdaf7j2bc94pef3",
  "object": "payout_batch",
  "livemode": true,
  "created": "2026-10-12T09:40:00.000Z",
  "status": "awaiting_approval",
  "description": "October contractor payments",
  "row_count": 2,
  "total_value": {
    "amount": "1700.00",
    "currency": "USD"
  },
  "counts": {
    "draft": 0,
    "awaiting_approval": 2,
    "signed": 0,
    "sent": 0,
    "confirmed": 0,
    "failed": 0,
    "unknown": 0,
    "cancelled": 0
  },
  "payouts": [
    "po_crs9tp390f2yjremph9m0npb",
    "po_kyrcy0h8t43wvedbxe5v40bp"
  ],
  "requested_by": {
    "type": "api_key",
    "id": "key_6e3gg2c6ea176xxdek2nar6v"
  },
  "approved_by": null,
  "approval_url": "https://merchant.orrerie.com/approve/pob_b1xkj45rqwdaf7j2bc94pef3",
  "approval_expires_at": "2026-10-19T09:40:00.000Z",
  "approved_at": null,
  "completed_at": null,
  "cancellation_reason": null,
  "metadata": {}
}

Any other status: an error

A status of 400 or above answers with the error object. Branch on its code, never on its message.

Errors

Beside the errors every call can answer with (a bad key, a missing scope, the rate limit), this call answers with:

Errors of Request a payout batch
CodeHTTPMeaning
idempotency_key_required400The endpoint moves money on approval (refunds, payouts, batches, conversions, saved addresses) and needs an Idempotency-Key.
insufficient_balance409The available balance (amount minus reserved) of the source asset is too low; error.available gives it.
fee_account_low409The fee account cannot pay the network fee. error.needed says how much of which coin on which network must be held, for example {"asset": "tron:trx", "amount": "30"}.
saved_address_not_usable409The saved address is awaiting_approval, waiting (error.usable_at says until when) or removed.
destination_blocked403The destination address is on a sanctions list. The request is refused and recorded.
screening_unavailable503The sanctions check could not be made. A failed check is never treated as clean; retry later.
merchant_restricted403The merchant is restricted: new orders, payouts to others and conversions are off. Reads and withdrawals to saved addresses work.
payout_batch_not_allowed403Batches need the Verified tier.
payout_batch_too_large400More than 200 rows, or more than 50 rows sent from one EVM network.
payout_daily_limit_reached403Payouts to others in the last 24 hours (payouts, split legs and refunds to a saved address) plus this one would pass US$1,000 (Unverified) or US$100,000 (Verified). Withdrawals to own saved addresses have no tier limit.
merchant_closing403The business is closing: new orders, payment links and invoices are off, and money goes out only to its own saved addresses. Reads, exports and withdrawals to saved addresses work until it closes.
wallet_needs_review409The wallet holds less than Orrerie's records show, because money left it that nothing recorded. Money out from it waits until an Owner has reviewed the wallet in the dashboard.