Using the API
Conventions
The rules every endpoint follows, and the list of every resource. Read this once and the reference pages hold no surprises.
Base address and transport
https://api.orrerie.com/v1. HTTPS only, TLS 1.2 or later. A plain-HTTP connection is refused, not redirected, so that a key sent by mistake over HTTP is not followed by a working request. The host serves no cookies and reads none.
Browser calls from another origin are answered only for the few operations a publishable key may use, which each say "Publishable key: Allowed". A request that carries a secret key and an Origin header is refused with secret_key_in_browser, the key is flagged in the dashboard and the Owners are emailed: a secret key in a web page is a leak.
Versions
- The path major,
v1, changes only if authentication or the shape of every response changes. - A dated version is sent as
Orrerie-Version: 2026-10-11. Each API key is pinned to the newest version at the moment it is created; the header overrides it for one request; every response echoes the version used. Each webhook endpoint has its ownapi_version, and events are rendered in it. - Not breaking, so write code that tolerates them: a new endpoint; a new optional request parameter; a new field in a response or event; a new event type; a new error
codeorfailure_code; a new asset or network id; a longer or differently wordedmessage. - Breaking, only in a new dated version: removing or renaming a field, parameter, endpoint or event type; changing a type; changing a default; adding a state to an existing state list; making an optional parameter required.
- A dated version is served for at least 12 months after the next one is published. Retirement is announced by email to every Owner and Developer 90 days ahead and by a
Sunsetresponse header on requests that use the old version. A request naming a retired version fails withversion_unsupported.
The changelog lists every dated version.
Requests and responses
- Request bodies are JSON objects,
Content-Type: application/json, UTF-8, at most 256 KB. The one exception is the file upload, which ismultipart/form-dataof at most 5 MB. A larger body is refused withrequest_too_largebefore it reaches the API. - Unknown parameters are refused with
parameter_unknown, not ignored. A misspeltsucess_urlmust fail loudly, not create a session with no return address. - Updates use
PATCHand change only the fields sent; sendingnullclears an optional field. Actions usePOSTon a path that ends in a verb. Deletes useDELETEand return the object or{id, object, deleted: true}. - Success codes:
200(read, update, action),201(created),202(a money-out or background request was accepted and is waiting, for approval or to be built),302(file download). - Every response has
Orrerie-Request-Id: req_…, a label for one request that you can quote to support; every answer of the API itself also hasOrrerie-Version. The id isreq_and then 24 characters, or 32 when Orrerie's edge server answers by itself, so never check its length. The edge answers by itself for a body too large, too many requests or a brief outage; store the id as it comes. - A
GETis safe to repeat. Nothing in the API is changed by aGET.
Money
- Amounts are decimal strings, never JSON numbers:
"21.99","0.00018700". A float cannot hold 18 decimal places. A request amount with more decimal places than the currency or asset has is refused withamount_invalid; it is never rounded for you. - Fiat currency is an upper-case ISO 4217 code with its usual decimals:
USDhas 2,JPYhas 0. An order priced in a fiat currency other than USD freezes the day's reference rate on the order when it is created, inorder.fx. Pricing in USD or in a crypto asset has no such gap. - Assets are written
<network>:<symbol>in lower case:base:usdc,ethereum:eth,bitcoin:btc,tron:usdt. The network ids are fixed:ethereum,bsc,polygon,arbitrum,optimism,base,solana,tron,bitcoin,litecoin,dogecoin.GET /v1/assetsis the full list with decimals and contract addresses. - A value shown in fiat is
{ "amount": "21.99", "currency": "USD" }. - An order's
amountis in its pricing currency; what moved on chain is always given in the asset's own units beside it.
Time and ids
- Timestamps are RFC 3339 in UTC with milliseconds:
"2026-10-11T14:03:22.418Z". A date without a time, such as an invoice due date, is"2026-10-22". - An id is a prefix, an underscore, and 24 lower-case characters:
ord_for an order,cs_for a checkout session,evt_for an event. Treat an id as an opaque string of up to 64 characters. - Example addresses and transaction hashes in these docs are illustrative and are not real payment destinations.
Metadata
Most objects take metadata: up to 50 keys, each key up to 40 characters, each value a string up to 500 characters. Orrerie stores it and returns it and never acts on it. It is copied from a checkout session to its order, and from a payment link, invoice or subscription to the orders they create. Do not put secrets, card numbers or anything about a person that you would not put in a log.
Pagination
List endpoints return a list object, newest first by created, ties broken by id:
JSON
{
"object": "list",
"url": "/v1/orders",
"has_more": true,
"data": [
{
"id": "ord_0zymngngvf12b8b2cra7tb8e",
"object": "order",
"…": "…"
},
{
"id": "ord_18ayqdcy8s50t2dp5r257ep3",
"object": "order",
"…": "…"
}
]
}| Parameter | Meaning |
|---|---|
limit | 1 to 100. Default 20. |
starting_after | An object id. Returns the items that come after it in the list (older). Use the last id of the page you have. |
ending_before | An object id. Returns the items before it (newer). Use the first id of the page you have. |
has_more says whether another page exists in the direction you are going. Send only one of starting_after and ending_before, or the call fails with parameters_exclusive. The cursor is an id, so a new object created while you page does not shift your place or repeat an item. There is no total count; use an export when you need totals.
Five lists are short and fixed and are not paginated: /v1/wallets (always eleven), /v1/balances, /v1/assets, /v1/rates, and the per-order /payment_options and /conversions/offers.
Filtering, sorting and expanding
- Equality:
?status=paid. Several values for one field are comma-separated and mean "any of":?status=paid,overpaid. - Ranges on timestamps, dates and amounts use bracket operators:
?created[gte]=2026-10-01T00:. The operators are00: 00Z&created[lt]=2026-11-01T00: 00: 00Z gte,gt,lteandlt. Every list endpoint acceptscreated[...]; other range fields are named on each endpoint. - Booleans are
trueorfalse. - Filters combine with "and". A filter the endpoint does not list is refused with
parameter_unknown. - Sorting is fixed: newest first. To walk oldest-first, filter by
created[gte]and page withending_before. - Search by your own reference: orders, payouts and sessions filter on
referenceorclient_reference_idby exact match. There is no free-text search in v1; the dashboard has it. - Expanding: where a field holds an id,
?expand=customer,paymentsreturns the object in its place. Up to 5 fields, one level deep. Lists acceptexpandwith the prefixdata., as in?expand=data.customer. Each field needs the read scope of the object it names, as that object's ownGETdoes: a restricted key withoutcustomers:readgets403insufficient_scopeforexpand=customer, and nothing is expanded.
These samples list the paid orders of October, 100 a page, and then ask for the next page. The JavaScript and Python samples walk every page, with the small orrerie function of the quick start:
curl
curl -G https://api.orrerie.com/v1/orders \
-H "Authorization: Bearer $ORRERIE_SECRET_KEY" \
--data-urlencode "status=paid,overpaid" \
--data-urlencode "created[gte]=2026-10-01T00:00:00Z" \
--data-urlencode "created[lt]=2026-11-01T00:00:00Z" \
--data-urlencode "limit=100"
curl -G https://api.orrerie.com/v1/orders \
-H "Authorization: Bearer $ORRERIE_SECRET_KEY" \
--data-urlencode "status=paid,overpaid" \
--data-urlencode "created[gte]=2026-10-01T00:00:00Z" \
--data-urlencode "created[lt]=2026-11-01T00:00:00Z" \
--data-urlencode "limit=100" \
--data-urlencode "starting_after=ord_0zymngngvf12b8b2cra7tb8e"JavaScript
async function* listOrders(filters) {
let startingAfter;
for (;;) {
const qs = new URLSearchParams({ ...filters, limit: '100' });
if (startingAfter) qs.set('starting_after', startingAfter);
const page = await orrerie('GET', `/orders?${qs}`);
yield* page.data;
if (!page.has_more) return;
startingAfter = page.data[page.data.length - 1].id;
}
}
for await (const order of listOrders({
status: 'paid,overpaid',
'created[gte]': '2026-10-01T00:00:00Z',
'created[lt]': '2026-11-01T00:00:00Z',
})) {
console.log(order.id, order.reference, order.amount, order.currency, order.paid_at);
}Python
def list_orders(**filters):
params = dict(filters, limit=100)
while True:
page = orrerie("GET", "/orders", params=params)
yield from page["data"]
if not page["has_more"]:
return
params["starting_after"] = page["data"][-1]["id"]
for order in list_orders(**{
"status": "paid,overpaid",
"created[gte]": "2026-10-01T00:00:00Z",
"created[lt]": "2026-11-01T00:00:00Z",
}):
print(order["id"], order["reference"], order["amount"], order["currency"], order["paid_at"])The states every object uses
An arrow reads "then"; a bar reads "or".
| Object | States |
|---|---|
| Order | open → processing → paid | underpaid | overpaid | expired | cancelled. refunded and partially_refunded are boolean flags. |
| Payment instruction | waiting → detected → delivered | refunded_by_provider | failed | expired |
| Payment | seen → confirmed → settled; or reverted |
| Refund, payout, conversion | draft → awaiting_approval → signed → sent → confirmed | failed | unknown |
| Wallet | address_ready → deploying → active |
| Merchant tier | unverified → verification_pending → verified | verification_failed; plus the flag restricted |
| Rail | orrerie_app, relay, direct. Recorded on every instruction and payment; never changes. |
| Order, transitions | An expired or cancelled order returns to processing when money arrives in the 30-day late window; paid can become overpaid; a reverted payment can take a paid order back. Flags: refunded, partially_refunded, paid_late, accepted_short, screening_hold, provider_refund_received, has_unconfirmed (shown also as finalized). |
| Checkout session | open → completed | expired |
| Refund | The money-out states, with one earlier step: awaiting_address (claim link made, for you to give the payer; lapses to draft after 14 days) |
| Refund, payout, conversion | A request not approved in 7 days lapses back to draft |
| Payout batch | Derived from its rows: draft, awaiting_approval, processing, completed, completed_with_failures, needs_attention, cancelled |
| Subscription | incomplete → active → past_due → unpaid → ended; incomplete_expired; paused; cancelled. ended carries ended_reason: unpaid_30_days or periods_complete |
| Unmatched payment | Reasons no_instruction, ambiguous_amount, app_report_mismatch, wrong_, after_late_window; assigned, ignored or refunded. A transfer that has just landed while a routed payment is on its way is pending_attribution for up to 30 minutes and is not listed as unmatched (and no payment.unmatched is sent) until then |
| Refund, payout, conversion, batch | One more: cancelled (before signing only), with cancellation_reason cancelled_by_api, rejected_by_member or destination_removed |
| Saved address | awaiting_approval → waiting (24 hours) → active; removed |
| Invoice | draft → open → paid | void; flag overdue |
| Webhook endpoint | enabled, disabled (with disabled_reason: by_member or consecutive_failures). failing is not a state: it is the read-only flag failing: true after 24 hours of consecutive failures |
| Split rule | awaiting_approval → active; inactive |
Verification (read-only on GET /v1/merchant) | created, in_progress, awaiting_people, in_review, needs_more, approved, declined, lapsed |
| Delivery attempt | pending, succeeded, failed |
| Export | queued → running → ready | failed; expired after 7 days |
| API key | active, rolling, revoked, expired |
| Member | invited, active, removed |
Fields that explain a state
These fields say why an object is in the state it is in, or what it can do there.
| Object | Field | Values and meaning |
|---|---|---|
wallet | two_of_three_in_force | A boolean, false for a Solana or TRON wallet before activation. When false, address is null, can_receive is false, and the wallet cannot be a settlement or direct-rail wallet (wallet_not_activated). |
wallet | display_state | ready, not_activated, not_enabled (Orrerie has not switched this network on yet; address is null), deploying, active. |
payment_instruction, payment | rail_reason | Why this rail: payer_chose_app, merchant_direct_only, relay_no_route, below_relay_minimum, same_asset_utxo, default, payer_, provider_refund_counted. |
payment_instruction | recipient_kind | wallet (the merchant's wallet) or fee_account. |
payment_instruction | recipient, refund_to | The two addresses the routing provider was given. Both were signed by the merchant's own worker before the provider was called. Compare them with GET /v1/wallets. null on the direct rail. |
payment_instruction | provider_request_id | Relay's request id, so the request can be looked up at Relay. Shown on the order page and in the checkout's details panel too. |
payment_instruction | refund_wallet_protected | A boolean, false when refund_to is an un-activated Solana or TRON wallet. |
order | screening | An object whose status is pending, clear, review, blocked, unchecked or reviewed. screening_hold stays the boolean it was. |
order | settlement.recipient_ | wallet or fee_account. |
order | tip | Decimal string; the point-of-sale tip inside amount. "0.00" otherwise. |
subscription | ended_reason, max_periods | See the state table above. The subscriber's page link is never returned by the API (like the refund claim link). |
refund | destination_kind | claim_link, payer_orrerie_wallet (no wait) or saved_address (24-hour wait; approvable_at is set). |
payout | kind, split_rule, order | kind gains split_leg (a share of a paid order under a split rule) beside payout and withdrawal. |
webhook_endpoint | failing, failing_since, disabled_reason | See the state table above. |
API reference
126 endpoints in 21 resources. Each resource page prints its object in full and lists its endpoints and events; each endpoint has a page of its own.
- Checkout sessions4 endpoints, 3 events
- Orders11 endpoints, 22 events
- Payments4 endpoints, 6 events
- Products and prices8 endpoints, 4 events
- Payment links4 endpoints, 3 events
- Invoices8 endpoints, 6 events
- Customers5 endpoints, 3 events
- Subscriptions7 endpoints, 13 events
- Refunds5 endpoints, 10 events
- Saved addresses4 endpoints, 4 events
- Payouts9 endpoints, 13 events
- Split rules5 endpoints
- Wallets and balances6 endpoints, 4 events
- Conversions6 endpoints, 8 events
- Rates and assets2 endpoints
- Webhook endpoints8 endpoints, 2 events
- Events3 endpoints
- Files and exports8 endpoints, 2 events
- Receipts and POS3 endpoints, 1 events
- Merchant, team and keys12 endpoints, 11 events
- Test mode helpers4 endpoints
The whole API is also one machine-readable file: openapi.yaml, OpenAPI 3.1.
The same in brief, without code: Crypto payments API and webhooks.