Base URLs

The kurnl API has two separate surfaces: All partner integration work uses the /partner base. Examples in this reference use that base unless the page is explicitly marked as internal (/api/v1). (The split is organisational rather than physical: the partner routers are also mounted under /api/v1, so /api/v1/external-checkout/* etc. are live duplicates with identical auth — prefer /partner anyway.) All endpoints are HTTPS only. The partner API operates on your live account; there is no separate sandbox API key. For safe exploration, use the in-dashboard sandbox.

Authentication

See Authentication for the full guide. Quick reference:

Rate limits

Limits are fixed per endpoint: Requests are bucketed per API key (key prefix) when an X-API-Key header is present, otherwise per client IP — callers behind a shared NAT share one IP bucket. When the limit is exceeded, kurnl returns 429 Too Many Requests. No X-RateLimit-* headers are emitted — back off and retry with your own timer.

Response format

All responses are JSON. Successful responses vary by endpoint. Error responses always follow this structure:
Validation errors return an array (Pydantic v2 shape):
Exception: the 410 Gone responses for expired/cancelled checkout sessions nest an object inside detail:
The cancelled variant carries no expired_at: {"detail": {"detail": "Session cancelled"}}.

Common HTTP status codes

Idempotency

All provisioning endpoints are idempotent by design:
  • POST /external-checkout/complete — the same session_id returns the existing subscription
  • POST /external-checkout/anonymous-complete — the same external_subscription_id (or an already-completed session_id) returns the existing subscription, flagged idempotent_replay: true
  • POST /provisioning/provider-initiated, POST /provisioning/bulk, and the explicit-fields (no-session) POST /external-checkout/complete — idempotent per (subscriber email, plan version, location): a retry returns the existing subscription with the message "Subscription already exists (idempotent replay)" or "Provisioning already in progress" (job_id may be empty on replay)
Retrying after a 5xx is therefore safe on all of these paths — kurnl also rolls back partial state automatically on server failures.

Pagination

The partner API currently has no paginated list endpoints.