GUIDES

Your own billing (API)

Without Stripe or a supported billing tool, your backend sends subscriptions and payment attempts, and applies what NoChurn asks for through webhooks.

Choose billing by API when your app does not bill with Stripe or one of the supported tools: another processor, or billing you built yourself. NoChurn never charges a card it does not hold. Your backend tells NoChurn what happens; NoChurn decides and asks your backend to act.

Set the account’s billing source once: PATCH /v1/account { "billing_source": "api" } (or pick “API” at onboarding).

What your backend sends

Subscriptions. POST /v1/subscriptions when a subscription is created or changes:

Field
external_id Your subscription id
customer Your user id, or { external_id, email } to create the customer at the same time
plan Plan name
amount, currency Price per interval, in cents
interval month or year
status The subscription status
current_period_end Next renewal, ISO 8601

Later changes go to PATCH /v1/subscriptions/{id}.

Payment attempts. POST /v1/payments for every attempt, failed or succeeded:

Field
external_id Your payment id. Idempotent: the same id is recorded once
customer, subscription Your ids
invoice Your invoice id: groups the attempts of one bill
amount, currency In cents
status failed or succeeded
decline_code The processor’s decline code, e.g. insufficient_funds
invoice_url The page of your app where the customer updates their card. The recovery emails link to it

A failed payment opens the recovery flow. A succeeded one on the same invoice closes it as recovered.

curl -X POST https://api.nochurn.sh/v1/subscriptions -H "Authorization: Bearer $NOCHURN_API_KEY" -H "Content-Type: application/json" -d '{"external_id":"sub_123","customer":{"external_id":"user_123","email":"jane@example.com"},"plan":"Pro","amount":4900,"currency":"usd","interval":"month","status":"active","current_period_end":"2026-12-05T00:00:00Z"}'
curl -X POST https://api.nochurn.sh/v1/payments -H "Authorization: Bearer $NOCHURN_API_KEY" -H "Content-Type: application/json" -d '{"external_id":"pay_123","customer":"user_123","subscription":"sub_123","invoice":"inv_123","amount":4900,"currency":"usd","status":"failed","decline_code":"insufficient_funds","invoice_url":"https://app.example.com/billing"}'

What NoChurn asks your backend to do

NoChurn sends webhooks your backend applies. Each one carries an idempotency_key: pass it on, so the action happens once even when the webhook is delivered twice.

Event Your backend
billing.retry_requested Attempts the payment of the invoice now, then reports the result with POST /v1/payments
billing.cancel_requested Cancels the subscription. reason is customer_cancelled (at_period_end says when) or recovery_lost (cancel at the end of the period)
billing.discount_requested Applies the accepted discount offer to the subscription
billing.pause_requested Pauses the subscription
billing.downgrade_requested Moves the subscription to the offered plan

Payloads: Webhook events › Billing by API. A cancellation the customer asked for must always be carried out (click to cancel).

Check it in test mode

With a test key, POST /v1/sandbox/customers creates a customer billed by API, and POST /v1/sandbox/failed_payment plays a failed payment: see Test mode and sandbox.