Ask for an invitation
Menu

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 own api_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 code or failure_code; a new asset or network id; a longer or differently worded message.
  • 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 Sunset response header on requests that use the old version. A request naming a retired version fails with version_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 is multipart/form-data of at most 5 MB. A larger body is refused with request_too_large before it reaches the API.
  • Unknown parameters are refused with parameter_unknown, not ignored. A misspelt sucess_url must fail loudly, not create a session with no return address.
  • Updates use PATCH and change only the fields sent; sending null clears an optional field. Actions use POST on a path that ends in a verb. Deletes use DELETE and 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 has Orrerie-Version. The id is req_ 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 GET is safe to repeat. Nothing in the API is changed by a GET.

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 with amount_invalid; it is never rounded for you.
  • Fiat currency is an upper-case ISO 4217 code with its usual decimals: USD has 2, JPY has 0. An order priced in a fiat currency other than USD freezes the day's reference rate on the order when it is created, in order.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/assets is the full list with decimals and contract addresses.
  • A value shown in fiat is { "amount": "21.99", "currency": "USD" }.
  • An order's amount is 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",
      "…": "…"
    }
  ]
}
Table: Pagination
ParameterMeaning
limit1 to 100. Default 20.
starting_afterAn object id. Returns the items that come after it in the list (older). Use the last id of the page you have.
ending_beforeAn 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:00:00Z&created[lt]=2026-11-01T00:00:00Z. The operators are gte, gt, lte and lt. Every list endpoint accepts created[...]; other range fields are named on each endpoint.
  • Booleans are true or false.
  • 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 with ending_before.
  • Search by your own reference: orders, payouts and sessions filter on reference or client_reference_id by exact match. There is no free-text search in v1; the dashboard has it.
  • Expanding: where a field holds an id, ?expand=customer,payments returns the object in its place. Up to 5 fields, one level deep. Lists accept expand with the prefix data., as in ?expand=data.customer. Each field needs the read scope of the object it names, as that object's own GET does: a restricted key without customers:read gets 403 insufficient_scope for expand=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".

The states every object uses
ObjectStates
Orderopen → processing → paid | underpaid | overpaid | expired | cancelled. refunded and partially_refunded are boolean flags.
Payment instructionwaiting → detected → delivered | refunded_by_provider | failed | expired
Paymentseen → confirmed → settled; or reverted
Refund, payout, conversiondraft → awaiting_approval → signed → sent → confirmed | failed | unknown
Walletaddress_ready → deploying → active
Merchant tierunverified → verification_pending → verified | verification_failed; plus the flag restricted
Railorrerie_app, relay, direct. Recorded on every instruction and payment; never changes.
Order, transitionsAn 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 sessionopen → completed | expired
RefundThe 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, conversionA request not approved in 7 days lapses back to draft
Payout batchDerived from its rows: draft, awaiting_approval, processing, completed, completed_with_failures, needs_attention, cancelled
Subscriptionincomplete → active → past_due → unpaid → ended; incomplete_expired; paused; cancelled. ended carries ended_reason: unpaid_30_days or periods_complete
Unmatched paymentReasons no_instruction, ambiguous_amount, app_report_mismatch, wrong_asset_on_order_address, 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, batchOne more: cancelled (before signing only), with cancellation_reason cancelled_by_api, rejected_by_member or destination_removed
Saved addressawaiting_approval → waiting (24 hours) → active; removed
Invoicedraft → open → paid | void; flag overdue
Webhook endpointenabled, 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 ruleawaiting_approval → active; inactive
Verification (read-only on GET /v1/merchant)created, in_progress, awaiting_people, in_review, needs_more, approved, declined, lapsed
Delivery attemptpending, succeeded, failed
Exportqueued → running → ready | failed; expired after 7 days
API keyactive, rolling, revoked, expired
Memberinvited, 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.

Fields that explain a state
ObjectFieldValues and meaning
wallettwo_of_three_in_forceA 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).
walletdisplay_stateready, not_activated, not_enabled (Orrerie has not switched this network on yet; address is null), deploying, active.
payment_instruction, paymentrail_reasonWhy this rail: payer_chose_app, merchant_direct_only, relay_no_route, below_relay_minimum, same_asset_utxo, default, payer_chose_after_relay_refusal, provider_refund_counted.
payment_instructionrecipient_kindwallet (the merchant's wallet) or fee_account.
payment_instructionrecipient, refund_toThe 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_instructionprovider_request_idRelay'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_instructionrefund_wallet_protectedA boolean, false when refund_to is an un-activated Solana or TRON wallet.
orderscreeningAn object whose status is pending, clear, review, blocked, unchecked or reviewed. screening_hold stays the boolean it was.
ordersettlement.recipient_kindwallet or fee_account.
ordertipDecimal string; the point-of-sale tip inside amount. "0.00" otherwise.
subscriptionended_reason, max_periodsSee the state table above. The subscriber's page link is never returned by the API (like the refund claim link).
refunddestination_kindclaim_link, payer_orrerie_wallet (no wait) or saved_address (24-hour wait; approvable_at is set).
payoutkind, split_rule, orderkind gains split_leg (a share of a paid order under a split rule) beside payout and withdrawal.
webhook_endpointfailing, failing_since, disabled_reasonSee 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.

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.