Skip to content
Sandbox open: magic test cards, hosted checkout and signed webhooks · 55 payment methods in the catalogue, cards to crypto · Built and hosted in the EEA · Read the API reference at /developers

Sandbox open: magic test cards, hosted checkout and signed webhooks

Documentation

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.

RuleValue
Base URLhttps://cleared-pay.com/api/v1
Content typeapplication/json in and out; charset=utf-8 on every response
AuthAuthorization: Bearer cp_sk_…
VersionCleared-Version: 2026-08-19, or the version pinned on the key
IdempotencyIdempotency-Key: <uuid> on every POST
Request idX-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:

ExponentCount of currenciesExample
232{"amount": 2000, "currency": "eur"} — €20.00
016{"amount": 2000, "currency": "jpy"} — ¥2,000
37{"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.

PrefixResource
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.

ParameterMeaning
limit1 to 100, default 25
starting_aftera cursor or a bare resource id — the page after that item
ending_beforea 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, the Cleared-Account platform 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.