The browser never receives a Nozle secret key and never chooses the authoritative Nozle customer. Your backend derives that customer from the authenticated application user or team.
Example merchant flow
Backend route
React
Mixed Entity seat purchase
For a seat-pool UI, accept merchant intent such as{ proSeats: 2, maxSeats: 1 }, not Nozle customer or subscription identifiers. Your backend:
- resolves the customer from the authenticated workspace;
- creates or reuses active unassigned Entity placeholders;
- assigns one allowed plan code to each placeholder;
- sends the complete basket to
POST /customers/{customer_id}/entity-subscriptions/checkoutwith one idempotency key; and - returns the embedded Stripe client secret or completed result.
cancelSubscription(customerId, subscriptionId) from the Node.js SDK or cancel_subscription(customer_id, subscription_id) from Python. Both default to end_of_period; the raw API keeps its historical immediate default. Do not accept either Nozle identifier directly from the browser—resolve both from merchant-owned records.
Payment authority
Stripe webhook processing is authoritative. A redirect back to the merchant application never activates a paid plan. Nozle activates payment-gated changes only after verified, idempotently processed Stripe success; failed payments and 3DS remain in the payment flow. Billable and credit-consuming events also stay server-side through@nozle-js/node with the restricted sk_. CORS and Origin headers are browser controls, not authentication.