GET STARTED

Concepts

Test and live mode, the three customer ids, events and their journal, idempotency, pagination and the four flows.

Test and live mode

Every account has two separate sets of data: test and live. A secret key belongs to one of them: nc_test_… only sees test data, nc_live_… only live data. Customers, subscriptions, flows, webhook endpoints and custom attributes are all kept per mode, and every object carries livemode.

In test mode NoChurn uses your billing tool’s test data (Stripe test mode, a sandbox site) and never emails a real inbox. Live keys are created by a person in the dashboard; AI apps work in test mode and need a human for anything live (see AI apps and MCP).

Authentication

Caller Header Sees
Your backend Authorization: Bearer nc_test_… Everything the key allows (a key is full, or restricted with none / read / write per resource)
Your front end or app Authorization: Bearer pk_test_… plus X-NoChurn-Customer-Token One customer’s cancel flow and in-app screens, from an allowed origin
An AI app Authorization: Bearer ncat_… (OAuth) Test mode by default; see MCP

Publishable keys (pk_…) are refused on the secret-key endpoints, and secret keys on the few endpoints that only take a publishable key.

Three customer ids

Each customer has a NoChurn id (cst_…, always), a Stripe id (cus_…, when Stripe is connected) and your own user id (external_id, when you send it). Responses and webhooks carry the three, null when absent.

Wherever the API takes a customer (/v1/customers/{id}, customer in payments, usage events, cancel sessions…) you can pass any of them. cst_ is read as a NoChurn id, cus_ as a Stripe id when it exists on a connected Stripe account, anything else as your external_id. An unknown value answers 404 customer_not_found; a value that matches two customers answers 409 ambiguous_customer: use the explicit parameter then. GET /v1/customers/resolve tells which customer a value names.

Always create customers with your user id. With Stripe, also put it in the Stripe customer’s metadata.user_id (the key is configurable): NoChurn then links the two instead of creating a duplicate. NoChurn never merges two customers on its own: a possible duplicate is proposed, and a person merges it or dismisses it.

Other ids

Every NoChurn id has a prefix, never shared with Stripe’s (cus_, sub_, in_, pm_, evt_): cst_ customer, nsub_ subscription, ninv_ invoice, npm_ payment method, npay_ payment attempt, nevt_ event, key_ API key, acc_ account.

Events

Every decision NoChurn takes (a retry scheduled, an email held, an offer accepted, a customer flagged) is written as an event with its reason. GET /v1/events lists them, newest first. The same events reach your systems as webhooks, each with its nevt_… id: see the event types.

Idempotency

  • What you send. Payments are idempotent on their external_id (your payment id): sending the same attempt twice records it once. Usage events take an idempotency_key and are deduplicated on it.
  • What NoChurn sends. Webhook deliveries are retried and can be replayed: the event id stays the same, so deduplicate on it. The billing.*_requested webhooks carry an idempotency_key for the action your billing runs.
  • What NoChurn does in your billing tool. Each action on Stripe carries an Idempotency-Key, so a retried job never charges or cancels twice.

Pagination

Lists return { "object": "list", "data": [...], "has_more": true, "next_cursor": "…" }, newest first. Pass ?limit= (1 to 100, default 20) and ?starting_after=<next_cursor> for the next page.

Errors

Every error is { "error": { "code", "message", "fix", "docs_url" } } with a meaningful HTTP status. The fix says what to do; docs_url points to the code’s section in Errors.

The four flows

Flow id What it does
Prevention prevention Acts on early signals, for example a card removed before the renewal
Recovery recovery A failed payment: retries, recovery emails with a card update link, in-app banner and wall
Cancel cancel The cancel flow: reasons, offers (discount, pause, downgrade, talk to us) and click to cancel at every step
Win-back winback Emails to customers who left

Each flow has a draft and published versions per mode: GET /v1/flows/{id} returns the draft, PATCH edits it and POST /v1/flows/{id}/publish publishes it to test or live. A flow can be paused, and its emails previewed before anything is sent.

Click to cancel

A customer who asks to cancel can always do it: the cancel flow shows Cancel now at every step, and a requested cancellation is always carried out (in Stripe by NoChurn, or in your billing through billing.cancel_requested). Offers come before the cancellation, never instead of it.