Appearance
Payouts
A payout records a USDC transfer you sent to a user. You report it, and Zay confirms it — see How payouts work.
New to the API? Start with Get started.
| Endpoint | Operation |
|---|---|
POST /merchants/v1/payouts/report | Report a payout |
GET /merchants/v1/payouts/{id} | Retrieve payout |
GET /merchants/v1/payouts | List payouts |
Events: payout.received, payout.expired
The Payout object
json
{
"payout": {
"id": "60eb2155-c3b4-43d7-9451-0fe1cc009d85",
"user_id": "e0554f00-e094-4fba-ba22-bc1ffbb7c7be",
"amount": "12.3",
"currency_code": "USDC",
"status": "pending",
"transaction_hash": "0xabc123",
"reason": null,
"custom_metadata": {},
"created_at": "2025-12-05T12:05:49Z"
}
}| Field | Type | Description |
|---|---|---|
id | string (uuid) | |
user_id | string (uuid) | |
amount | string | Decimal amount serialised as a string. Must be greater than 0 and at most 10 000. |
status | string | Payout status One of pending, received, expired. |
created_at | string (date-time) | Creation date and time (ISO-8601) |
currency_code | string | Currency code One of USDC. |
transaction_hash | string | On-chain transaction hash of the transfer the merchant broadcast, supplied when reporting the payout. |
reason | string | null | Free-text reason set by Zay when a payout is expired. Null otherwise. |
custom_metadata | object | Free-form JSON bag the merchant attaches to the payout at creation. |
Payout statuses
| Status | Meaning | Terminal |
|---|---|---|
pending | Reported, not yet confirmed by Zay | no |
received | Zay confirmed the transfer; payout.received was sent | yes |
expired | Zay could not confirm the transfer; payout.expired was sent with a reason | yes |
A payout never leaves received or expired.
Report a payout
Report a USDC transfer you have already sent on-chain, passing its transaction_hash. The payout is created in pending; Zay then confirms it and moves it to received or expired.
POST /merchants/v1/payouts/report
Request body
| Field | Type | Description |
|---|---|---|
user_idrequired | string (uuid) | |
payoutrequired | object | |
payout.amountrequired | string | Decimal amount serialised as a string. Must be greater than 0 and at most 10 000. |
payout.transaction_hashrequired | string | On-chain transaction hash of the transfer the merchant broadcast, supplied when reporting the payout. |
payout.currency_code | string | Currency code One of USDC. |
payout.custom_metadata | object | Free-form JSON bag the merchant attaches to the payout at creation. |
user_id is the user's id as returned by Create user — not your own external_id.
Always send a unique Idempotency-Key here, so a network blip never records the same payout twice — see Idempotency.
Returns 201 Created with a wrapped payout object in pending.
Errors:
401— invalid signature.404—user_iddoes not match a user under this merchant.422— missing or blanktransaction_hash, invalid amount (≤ 0 or > 10,000), or unsupported currency.429— rate limit exceeded.
Examples
cURL
bash
curl -X POST "https://api-connect.zay.me/merchants/v1/payouts/report" \
-H "Content-Type: application/json" \
-H "zay-api-key: $ZAY_API_KEY" \
-H "zay-timestamp: $TS" \
-H "zay-signature: $SIG" \
-H 'Idempotency-Key: "60eb2155-c3b4-43d7-9451-0fe1cc009d85"' \
-d '{
"user_id": "2c9596e6-7c48-4fe9-b957-738bd2e04adc",
"payout": {
"amount": "1.23",
"currency_code": "USDC",
"transaction_hash": "0xabc123",
"custom_metadata": {"invoice_ref": "INV-42"}
}
}'Python
python
import json
import uuid
import requests
body = json.dumps({
"user_id": user_id,
"payout": {"amount": "1.23", "currency_code": "USDC", "transaction_hash": "0xabc123", "custom_metadata": {"invoice_ref": "INV-42"}},
})
url = "https://api-connect.zay.me/merchants/v1/payouts/report"
r = requests.post(url, data=body, headers={
"Content-Type": "application/json",
"zay-api-key": ZAY_API_KEY,
"Idempotency-Key": f'"{uuid.uuid4()}"',
**merchant_headers("POST", url, ZAY_PRIVATE_KEY, body),
})
payout = r.json()["payout"]Node.js
javascript
const url = "https://api-connect.zay.me/merchants/v1/payouts/report";
const body = JSON.stringify({
user_id: userId,
payout: { amount: "1.23", currency_code: "USDC", transaction_hash: "0xabc123", custom_metadata: { invoice_ref: "INV-42" } },
});
const res = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"zay-api-key": ZAY_API_KEY,
"Idempotency-Key": `"${crypto.randomUUID()}"`,
...merchantHeaders("POST", url, ZAY_PRIVATE_KEY, body),
},
body,
});
const { payout } = await res.json();Response
json
{
"payout": {
"id": "eac4cfcc-97ee-4509-bb10-c6953bf30f9d",
"user_id": "9ee859c4-844a-49bf-9e68-965ea01004fe",
"amount": "1.23",
"currency_code": "USDC",
"status": "pending",
"transaction_hash": "0xabc123",
"reason": null,
"custom_metadata": {
"invoice_ref": "INV-42"
},
"created_at": "2025-12-05T12:05:49Z"
}
}Retrieve payout
Retrieve a payout by its id, including its current status.
GET /merchants/v1/payouts/{id}
Parameters
| Parameter | Type | Description |
|---|---|---|
idrequired | string (uuid), path | Payout ID |
Errors:
401— invalid signature.404— payout does not exist under this merchant.429— rate limit exceeded.
Example
bash
curl "https://api-connect.zay.me/merchants/v1/payouts/$PAYOUT_ID" \
-H "zay-api-key: $ZAY_API_KEY" \
-H "zay-timestamp: $TS" \
-H "zay-signature: $SIG"Response
json
{
"payout": {
"id": "60eb2155-c3b4-43d7-9451-0fe1cc009d85",
"user_id": "e0554f00-e094-4fba-ba22-bc1ffbb7c7be",
"amount": "12.3",
"currency_code": "USDC",
"status": "pending",
"transaction_hash": "0xabc123",
"reason": null,
"custom_metadata": {},
"created_at": "2025-12-05T12:05:49Z"
}
}List payouts
List payouts under your merchant with cursor pagination and optional filters. Results are ordered by descending creation time.
GET /merchants/v1/payouts
Parameters
| Parameter | Type | Description |
|---|---|---|
user_id | string (uuid), query | Filter by user ID |
page_size | integer, query | Page size |
page_cursor | string, query | Page identifier |
status | string, query | Payout status to search by |
created_from | string (date), query | Return payouts created on or after this date (inclusive) |
created_to | string (date), query | Return payouts created on or before this date (inclusive) |
Pass the previous response's next_cursor as page_cursor to get the next page. When next_cursor is null or absent, the list is exhausted. page_size ranges 1..1000 and defaults to 1000.
Errors:
401— invalid signature.429— rate limit exceeded.
Examples
cURL
bash
curl "https://api-connect.zay.me/merchants/v1/payouts?user_id=$USER_ID&status=pending&page_size=20" \
-H "zay-api-key: $ZAY_API_KEY" \
-H "zay-timestamp: $TS" \
-H "zay-signature: $SIG"Python
python
url = "https://api-connect.zay.me/merchants/v1/payouts"
params = {"user_id": user_id, "status": "pending", "page_size": 20}
r = requests.get(url, params=params, headers={
"zay-api-key": ZAY_API_KEY,
**merchant_headers("GET", url, ZAY_PRIVATE_KEY),
})
data = r.json()
payouts = data["payouts"]
next_cursor = data.get("next_cursor")Node.js
javascript
const url = new URL("https://api-connect.zay.me/merchants/v1/payouts");
url.searchParams.set("user_id", userId);
url.searchParams.set("status", "pending");
url.searchParams.set("page_size", "20");
const res = await fetch(url, {
headers: {
"zay-api-key": ZAY_API_KEY,
...merchantHeaders("GET", url.toString(), ZAY_PRIVATE_KEY),
},
});
const { payouts, next_cursor: nextCursor } = await res.json();Response
json
{
"payouts": [
{
"id": "bc3d4323-5b62-4bda-ac62-79f76e7942cd",
"user_id": "48434864-8c35-4a04-abcd-476e5a89457f",
"amount": "100.0",
"currency_code": "USDC",
"status": "pending",
"transaction_hash": "0xabc123",
"reason": null,
"custom_metadata": {},
"created_at": "2025-12-05T12:05:49Z"
},
{
"id": "abb8ba5a-ba29-4500-8da1-844225707bb5",
"user_id": "48434864-8c35-4a04-abcd-476e5a89457f",
"amount": "200.0",
"currency_code": "USDC",
"status": "pending",
"transaction_hash": "0xabc123",
"reason": null,
"custom_metadata": {},
"created_at": "2025-12-05T12:05:49Z"
},
{
"id": "9a0aef2d-8730-48dd-ae80-7c616370bc07",
"user_id": "48434864-8c35-4a04-abcd-476e5a89457f",
"amount": "200.0",
"currency_code": "USDC",
"status": "pending",
"transaction_hash": "0xabc123",
"reason": null,
"custom_metadata": {},
"created_at": "2025-12-05T12:05:49Z"
}
],
"next_cursor": "Ijk0ZjZhYjA1LTIxZTEtNDMxYS1hMzY4LWU2MmY1NDc1M2RmZiI=--f0aba3c8e2f7b8970dff0121c4626caaaa1985ba"
}