Skip to main content
Base URL: https://api.nozle.app/engine/api/v1. All routes below require a secret key and an authenticated merchant backend. The backend derives customer_id; the selected external subscription_id must belong to that customer and organization. These hosted endpoints are available. Their new SDK wrappers and React controls are merged but await registry releases; see the billing portal integration.

Endpoint map

These paths are for Engine. Its customer/subscription fields map to Core’s external identifiers; Core uses /subscription_transitions/options and /subscription_transitions/withdraw under its own /api/v1 base. Do not mix the two path sets.

Read state and eligible plans

GET /subscriptions/options returns: A plan contains code, name, amount_cents, currency, and interval. Keep monetary minor-unit values exact. An empty eligible list can reflect missing transition policies, scheduled cancellation, a pending change, external billing authority, or unresolved checkout. Price comparisons and public catalog membership do not establish permission to change plans.

Preview and confirm a plan change

Send customer_id, subscription_id, and plan_code to POST /subscriptions/preview:
The response includes quote_id, transition_direction, timing, currency, credit_amount_cents, debit_amount_cents, net_amount_cents, amount_due_now_cents, amount_due_at_effective_cents, effective_at, and nullable renewal_at. Quotes expire after five minutes. Show the returned amounts and dates for confirmation, and forward the opaque quote_id unchanged. State, policy or amount changes invalidate the quote with HTTP 409. Refresh and obtain a new confirmation instead of silently applying a different price. For an upgrade, send that quote with an Idempotency-Key header to Create checkout:
Use explicit subscription selection. A customer with multiple active subscriptions cannot use an ambiguous unselected preview/checkout. Replays of an already accepted quoted request reuse the original result even after quote expiry; preserve the same request and idempotency key when recovering an uncertain response.

Schedule a downgrade

After confirming a downgrade quote, send POST /subscriptions/transitions with an Idempotency-Key and:
The existing transition result is nested under subscription_transition. Reload options to display the current plan separately from the pending plan and its effective date. The self-service flow uses end-of-period timing and preserves the billing anchor.

Cancel and Keep

Preview operation: "cancel" with timing: "end_of_period" through /subscriptions/transitions/preview. Send the same parameters to /subscriptions/transitions with expected_effective_at set to the exact preview’s subscription_transition.effective_at, plus an Idempotency-Key header. A stale date returns HTTP 409 before cancellation. Keep uses operation: "uncancel", the customer/subscription identifiers and an Idempotency-Key. It accepts no settlement overrides. Keep is available only before the ending is due; it does not restart a terminated subscription. Cancellation removes a pending downgrade, and Keep does not recreate it.

Withdraw a pending downgrade

Read and retain pending_change.id from options when the customer opens the confirmation. Send POST /subscriptions/transitions/withdraw with an Idempotency-Key and:
pending_subscription_id is the pending internal UUID, not the active subscription’s external ID. The response contains subscription, withdrawn_pending_subscription_id and replayed. A stale owned pending ID returns 409; an unknown or unowned resource returns 404. The active subscription remains in place.

Payment status and recovery

Use both customer_id and subscription_id when recovering or verifying a customer checkout. Razorpay verification forwards the genuine browser fields razorpay_order_id, razorpay_payment_id and razorpay_signature. Provider secrets stay on the server; only the scoped checkout’s public key reaches Razorpay’s browser UI. Status returns checkout_id, provider, status, fulfillment_status, amount_cents, currency, optional invoice_id, and optional resumable checkout. Payment status is awaiting_payment, processing, succeeded, failed, expired, or needs_review; fulfillment is pending, processing, or succeeded. A successful browser callback is not proof of fulfillment. Wait for authoritative activation and reload the selected subscription. Razorpay upgrades require a captured payment. Retry a recoverable open checkout using its existing ID; treat expired checkouts and needs_review as distinct outcomes. Unknown payment outcomes require your support/reconciliation route before another collection. Idempotency keys must be nonempty and at most 255 UTF-8 bytes. Keep one key and the same payload for retries of one confirmed action; use a new key for a newly confirmed action. Persist merchant replay context across process restarts. Map ownership failures, stale confirmations and payment-review states without exposing raw upstream error bodies.