Skip to main content
POST
Entity Subscriptions
Base URL: https://api.nozle.app/core/api/v1. Entity subscriptions let one customer pay for multiple independently managed subscriptions. Each Entity keeps a stable subscription identity while using the standard Nozle plan, invoice, checkout, renewal, and reporting lifecycle. Auth: secret key only. Publishable keys are rejected.
Nozle validates the live Entity before creating or changing its subscription. Suspended or deleted Entities cannot start checkout. A newly created Entity that reuses an older external ID cannot inherit the older Entity’s subscription.

Ensure a subscription identity

This operation is idempotent. It creates the stable Entity-to-subscription identity but does not select or activate a plan. It returns 201 when it creates the identity and 200 when the identity already exists, with the identity under entity_subscription.

Read subscriptions

Read one Entity subscription:
List all Entity subscriptions for a customer:
Reading one Entity returns the subscription under entity_subscription. The list returns an entity_subscriptions array, ordered by external_entity_id. Each item reports the current and pending plans independently:
Each item also has billing_authority, credit_import_completed_at, subscription_at, started_at, current_period_start, current_period_end, ending_at, canceled_at, created_at and updated_at. status is unsubscribed when the Entity has no current or incomplete subscription. current_plan and pending_plan have the same fields. Their effective_at is the subscription’s scheduled change time when one is set, otherwise its start date. An unknown Entity returns 404 entity_subscription_not_found.

Start checkout

billing_time accepts anniversary or calendar for the initial subscription. Paid plans remain incomplete until verified payment succeeds. Retrying an incomplete checkout for the same plan reuses its subscription and invoice instead of creating another charge. A different plan replaces the abandoned checkout; 409 entity_subscription_checkout_in_progress remains only while the provider is still processing a payment for it. An unpaid checkout expires after 23 hours: the subscription is canceled with cancelation_reason: timeout, the invoice is closed and the credits it used are returned. See Unpaid checkout expiry.

Purchase multiple Entity plans together

Use bulk Entity checkout when a customer purchases a mixed pool of plans in one payment. The merchant backend supplies one item per Entity; Nozle creates one invoice and one Stripe or Razorpay checkout for the complete basket. Use a server SDK from your trusted backend. These SDK methods validate the basket and return the checkout contract. In Node.js:
The Python SDK exposes the same backend operation:
The Go SDK exposes the grouped checkout result with typed Entity items:
On the frontend, pass a Stripe result to the React checkout component:
The endpoint below remains available for backends that call HTTP directly.
The request accepts 1 to 100 unique Entities and requires an Idempotency-Key header of at most 255 bytes. Every plan must require an up-front payment, use the same currency, and use the same interval. A free plan, a plan with a trial, or a plan billed in arrears is refused, even though single Entity checkout accepts it. Each Entity must be active and must not already have an active, pending, or incomplete subscription. Item problems fail the whole basket with 422 and the reason in error_details.items:
Create each Entity first with Customer Entities. An Entity that does not exist currently makes the request fail with a bare 500, not a 404; this is a known defect. An unknown customer or plan returns 404 (customer_not_found, plan_not_found). Without a connected payment provider the request returns 422 payment_provider_required, as a single Entity checkout does; 422 stripe_customer_required means Nozle could not create the customer in Stripe.
Nozle supports these result types:
  • stripe: mount the returned cs_ client secret with Stripe Embedded Checkout;
  • razorpay: the result also has checkout_id, key_id, and order_id; open the Razorpay modal and verify as described in Razorpay. Razorpay needs INR plans (razorpay_requires_inr otherwise);
  • processing: the payment completed, or the Razorpay order is not ready, and the verified payment event is still being processed; and
  • completed: the invoice is already settled, so no provider session is required.
status is the batch status: pending, open, completed, failed, or expired. An unpaid checkout expires after 23 hours (expires_at).
A bulk Razorpay checkout answers first with type: "processing", status: "open", client_secret: null and a checkout_id, because Nozle creates the Razorpay order in the background. Either poll GET /checkout/{checkout_id} until its nested checkout is {"type": "razorpay", "key_id", "order_id", ...}, or replay the same request with the same Idempotency-Key: once the order exists the replay returns type: "razorpay" with checkout_id, key_id and order_id. Then open the modal. After the payment, status goes succeeded with fulfillment_status processing, then succeeded, and every Entity subscription in the basket becomes active.
The idempotency key covers the customer, billing mode, return URL, Entity IDs, and plans. Retry the exact request with the same key: Nozle returns the same batch with replayed: true and reuses the open Stripe session. A different basket with the same key returns 422 with idempotency_key: ["idempotency_key_reused"]. The result also carries clientSecret, a camel-case alias of client_secret.

Shared customer billing anchor

Bulk Entity checkout establishes one stable billing calendar for the paying customer:
  • if the customer has never completed a paid Entity checkout, the first successful payment establishes the anchor;
  • failed or abandoned first checkout does not establish an anchor;
  • concurrent first-paid checkouts are rejected while the anchor is unresolved;
  • later Entity purchases align to the established anchor and prorate their first period; and
  • the anchor remains stable even if the customer temporarily has no paid Entities.
For a fresh seat pool, send billing_time: "anniversary". For example, a successful first payment on August 6 renews on the sixth. A seat added on August 20 is charged only through September 6, then joins the normal renewal invoice.
The anchor is established only after confirmed payment. Creating an invoice or receiving a browser redirect is not enough.

Change plan

The plan transition policy for the current and target plans determines whether the change is immediate, payment-gated, or scheduled for the renewal boundary. Without a policy for the pair the request returns 422 plan_transition_policy_required (a change from a free plan to a paid plan uses the organization’s free-to-paid policy instead). Change-plan takes no billing_time. Changing one Entity never changes another Entity’s subscription. The result is a checkout result with external_entity_id and external_subscription_id added. A scheduled change:
effective_at carries the customer’s timezone offset, for example 2026-11-01T00:00:00.000000+05:30. Changing again while a scheduled change is pending replaces the pending change. A paid upgrade does not replace or withdraw a pending downgrade: it is refused with 409 subscription_change_pending, so withdraw it first, as described in Paid upgrades: current limits. The target being the current plan returns 422 plan_already_active.

Cancel

Cancellation supports the same settlement controls as customer subscriptions. Send credit_action explicitly: for a plan billed in arrears, a request without it returns 422 with credit_action: not_supported_for_pay_in_arrears. timing accepts immediate or end_of_period. Prefer end_of_period unless the merchant explicitly intends to terminate access immediately. The Idempotency-Key header is required (at most 255 characters); without it the request returns 422 with idempotency_key: value_is_mandatory. The response has the updated identity under entity_subscription and the recorded cancellation under subscription_transition, with its id and replayed. An identity with no current subscription returns 404 subscription_not_found.

Remove an unused identity

Removal is allowed only when the identity has no active, pending, or incomplete subscription. It then returns 204 with no body, including for an identity that never had a subscription. An identity with an active, pending, or incomplete subscription returns 403 entity_subscription_has_billing_history. An identity that does not exist returns 404 entity_subscription_not_found. Historical billing records remain immutable.
Payment provider webhooks (Stripe, or Razorpay together with your verify call) are authoritative for paid activation. Do not grant paid access from a browser redirect or checkout callback alone.