API reference
Authentication and keys
Bearer keys, the two key shapes, how the environment is decided, what a key never returns twice, and how to roll one without a failed request.
Updated
Every request carries a key in an Authorization header. There is no session, no OAuth and no signature on the request side — the key is the credential.
curl https://cleared-pay.com/api/v1/payments \
-H "Authorization: Bearer cp_sk_test_…"The two key shapes
| Prefix | Length | Where it belongs |
|---|---|---|
cp_pk_test_… / cp_pk_live_… | 26 chars | The browser. Tokenisation only — every other endpoint answers 403 authorization_error with code publishable_key_not_allowed. |
cp_sk_test_… / cp_sk_live_… | 32 chars | Your server. Never in client code, a mobile binary, a repository or a log line. |
A publishable key is not a secret and is expected to be visible in page source. A secret key is a bearer credential: anyone holding it can move money on your account.
The environment is the key
_test_ routes to the sandbox simulator; _live_ routes to a live connector. There is no mode parameter and no header that switches environment, so a test call can never accidentally become a live one. Objects are scoped to their environment too: a payment created with a test key is invisible to a live key and vice versa, and livemode on every object says which it is.
Sandbox-only endpoints (/v1/sandbox/*) answer 403 authorization_error with code test_mode_required when called with a live key.
Keys are shown once
A secret key is stored as HMAC-SHA256(pepper, key) alongside an indexed prefix and its last four characters. The full value exists only in the response that created it. There is no endpoint that returns it again and no support process that can recover it — if it is lost, roll it.
Cleared-pay never asks for your secret key, and it never appears in an API response, an error message, an email or a log line.
Rolling a key
Key management is portal-authenticated and needs step-up TOTP — it is not part of the public API, because a key that can mint keys is a key that cannot be contained. TODO(portal): the screen ships with the merchant portal at /portal/developers/api-keys.
The roll is designed so nothing fails while you deploy:
- Roll with a grace period between 0 and 168 hours. The new key is shown once.
- Both keys authenticate until the old one's
expires_at. - Deploy the new key.
- The old key expires on schedule. If it saw traffic in its final hour, the account address gets an email naming the prefix, so a forgotten service is visible before it breaks.
A revoke is immediate and has no grace period. Use it when a key may have leaked.
Failure modes
| Status | code | Cause |
|---|---|---|
| 401 | api_key_missing | No Authorization header, or an empty bearer token. |
| 401 | api_key_malformed | Not a cp_pk_/cp_sk_ key of the right length. |
| 401 | api_key_invalid | Well-formed but unknown, or the wrong environment for its hash. |
| 401 | api_key_revoked | The key was revoked. |
| 401 | api_key_expired | A rolled key past its grace window. |
| 403 | publishable_key_not_allowed | A cp_pk_… key on an endpoint that needs a secret key. |
| 403 | test_mode_required | A live key on a /v1/sandbox/* endpoint. |
| 403 | live_mode_disabled | A live key before the account is approved for live payments. |
| 403 | merchant_suspended | The account is suspended. |
None of these say whether a key exists for another merchant, or which merchant a key belongs to.
Rate limits
Limits apply per key and, more loosely, per IP address. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset; a 429 also carries Retry-After in seconds.
| Endpoint class | Default limit |
|---|---|
POST /v1/payments | 100 per minute |
Other /v1 writes | 300 per minute |
/v1 reads | 1000 per minute |
| Key creation | 10 per hour |
Payment creation is deliberately the tightest limit: it is the endpoint card-testing bots target. Limits are overridable per merchant — ask, with the numbers you need and why.