API reference
Conventions
Amounts, currencies, ids, timestamps, versioning with Cleared-Version, cursor pagination, and what is not supported yet.
Updated
One base URL, one content type, one set of rules for every resource.
| Rule | Value |
|---|---|
| Base URL | https://cleared-pay.com/api/v1 |
| Content type | application/json in and out; charset=utf-8 on every response |
| Auth | Authorization: Bearer cp_sk_… |
| Version | Cleared-Version: 2026-08-19, or the version pinned on the key |
| Idempotency | Idempotency-Key: <uuid> on every POST |
| Request id | X-Request-Id on every response, error or not |
There is no separate API hostname in v1. api.cleared-pay.com may later be added as an alias that rewrites to these same routes; nothing is deployed as a second service, so treat the origin above as stable.
Amounts
Amounts are integers in minor units. 2000 with currency: "eur" is €20.00. There are no decimal amounts anywhere in the API, in or out, because a float cannot hold money.
The exponent comes from the currency, not from your input:
| Exponent | Count of currencies | Example |
|---|---|---|
| 2 | 32 | {"amount": 2000, "currency": "eur"} — €20.00 |
| 0 | 16 | {"amount": 2000, "currency": "jpy"} — ¥2,000 |
| 3 | 7 | {"amount": 2000, "currency": "kwd"} — 2.000 KWD |
Internally amounts are arbitrary-precision integers; on the wire they are JSON numbers with a guard that refuses anything at or above 2^53, so no amount can ever lose precision in a JavaScript client.
Currencies
Lowercase ISO 4217, always: eur, usd, pln. An uppercase code in a request is accepted and normalised; every response is lowercase. An unsupported code is a 400 validation_error naming the parameter.
Identifiers
Ids are prefixed ULIDs — 26 uppercase base32 characters after the prefix, lexicographically sortable by creation time, which is what makes cursor pagination possible without an offset.
| Prefix | Resource |
|---|---|
mch_ | merchant |
pay_ | payment |
ref_ | refund |
cus_ | customer |
pm_ | payment method |
tok_ | single-use token |
cs_ | checkout session |
whe_ | webhook endpoint |
evt_ | event |
key_ | API key |
dp_ | dispute |
po_ | payout |
set_ | settlement |
pl_ | payment link |
Treat an id as an opaque string up to 64 characters. Do not parse the ULID body, and do not assume a prefix length.
Timestamps
RFC 3339 in UTC with second precision and a literal Z: 2026-08-19T10:15:00Z. Fields that may not have happened yet are null, never an empty string or a zero date — captured_at is null on an authorized payment.
Query parameters that take a time accept either an RFC 3339 string or a unix time in seconds.
Versioning
The API is versioned by date. A version is pinned on each key; Cleared-Version overrides it per request. One version is live today:
curl https://cleared-pay.com/api/v1/payments/pay_01JBXQ2M4K7T9V3ZC8HR5N6PWD \
-H "Authorization: Bearer $CLEARED_SECRET_KEY" \
-H "Cleared-Version: 2026-08-19"Additive changes — a new field, a new event type, a new enum member — ship without a version bump, so parse defensively: ignore fields you do not know and do not fail on an unrecognised status or event type. A breaking change gets a new dated version, and the old shape keeps being served to keys pinned to it.
An unsupported value in Cleared-Version falls back to the version on the key rather than failing the request.
Pagination
List endpoints are cursor-paginated. There is no total count: counting a large tenant-scoped table on every page is slow and the number is stale before you read it.
| Parameter | Meaning |
|---|---|
limit | 1 to 100, default 25 |
starting_after | a cursor or a bare resource id — the page after that item |
ending_before | a cursor or a bare resource id — the page before that item |
Sending both starting_after and ending_before is a 400 validation_error.
{
"object": "list",
"data": [{ "id": "pay_01JBXQ2M4K7T9V3ZC8HR5N6PWD", "object": "payment" }],
"has_more": true,
"next_cursor": "MTc1NTYwMDAwMDAwMC5wYXlfMDFKQlhR…"
}Pass next_cursor back as starting_after until has_more is false. Results are newest first. Cursors are opaque; they encode a creation time and an id, and a malformed one is a 400, never a silent first page.
Expansion is not supported yet
expand is in the contract and is not implemented. A request that sends it is not rejected, but nothing is expanded: customer stays an id string, method carries the fields shown on the payment object, and nothing else is inlined. Fetch the related resource with its own GET for now.
Also reserved
The following appear in the contract and are not part of v1. They answer 404; the schema and key format already accommodate them.
- Customers and the vault:
/v1/customers,/v1/payment-methods/:id - Payouts:
/v1/payouts - Disputes:
/v1/disputes - Payment links:
/v1/payment-links - Restricted keys (
cp_rk_…), per-key IP allowlist enforcement, theCleared-Accountplatform header, OAuth client credentials, subscriptions and a reporting API
Dispute, payout, customer and payment-link event types are already defined, so a webhook consumer written today needs no change when those resources arrive.