Docs
Idempotency, modes and errors
The rules that make retries safe, keep test and live apart, and tell you what went wrong.
Idempotency
Creating money-moving objects needs an Idempotency-Key header: 8 to 255 printable characters, unique per intent (an order id works well). Cortana stores the key with the invoice before any provider is called, so a retry after a timeout returns the original object instead of creating a second one.
# First call: 201 Created
curl -i https://cortanapay.com/api/v1/invoices -H "Idempotency-Key: order-1042" …
# Same key, same body: 200 OK, Idempotent-Replayed: true, same invoice
# Same key, different body: 409 idempotency_conflictReusing a key with a different body is an error on purpose. The Stripe-compatible /v1 routes treat the header as optional because Stripe SDKs always send one.
Test and live
| Aspect | Test | Live |
|---|---|---|
| Key prefix | ck_test_… | ck_live_… |
| Sees | Test invoices and payments only | Live invoices and payments only |
| Providers | Your Stripe or Whop test keys, or the built-in test provider | Your live provider accounts |
| Availability | Always | Needs an active plan |
The mode comes from the key, never from a parameter. A test key cannot read, change or void a live invoice, and the dashboard shows one mode at a time.
Errors
Errors use the HTTP status first and a stable code second. Match on the code; messages can change.
HTTP/1.1 422 Unprocessable Entity
{
"error": {
"code": "invalid_amount",
"message": "An invoice needs at least one item",
"details": null
}
}- 400
- Bad request:
idempotency_key_required,invalid_idempotency_key, malformed input. - 401
invalid_api_key: missing, wrong or revoked.- 403
insufficient_scope,live_access_required,workspace_suspended.- 404
- No such object in this mode.
- 409
idempotency_conflict,request_in_progress,invoice_payment_in_progresswhen voiding mid-settlement.- 422
- Valid JSON, rejected values: amounts, currency, unregistered callback_url.
- 429
rate_limited. 600 requests a minute per key; waitRetry-Afterseconds.- 5xx
- Our side. Retrying with the same Idempotency-Key is safe.
The Stripe-compatible routes answer in Stripe's shape instead (type, code, message, param), with 422 reported as 400.