zuplo 6.70.26 → 6.70.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,160 @@
1
+ ---
2
+ title: Switching Between Accounts
3
+ sidebar_label: Switching Accounts
4
+ ---
5
+
6
+ If you belong to more than one Zuplo account (for example, your own personal
7
+ account plus an account you were invited to join), you can switch between them
8
+ in the Zuplo Portal without signing out.
9
+
10
+ This guide covers accepting an invitation, finding the account switcher, and
11
+ troubleshooting common issues when you can't see an account you expect to have
12
+ access to.
13
+
14
+ :::caution{title="Sign in with the same identity"}
15
+
16
+ The invitation must be accepted using the same identity (email address and
17
+ sign-in method) it was sent to. Zuplo treats Google sign-in, GitHub sign-in, and
18
+ email-password as separate identities, even when they share an email address. If
19
+ you click an invitation while signed in with a different identity, the
20
+ invitation does not apply.
21
+
22
+ :::
23
+
24
+ ## Accept an account invitation
25
+
26
+ When an account admin invites you, Zuplo sends an invitation email to the
27
+ address they entered.
28
+
29
+ <Stepper>
30
+
31
+ 1. Open the invitation email and click the link to accept the invitation.
32
+ 1. If you are not already signed in to the Zuplo Portal, sign in with the **same
33
+ email address and method** the invitation was sent to. See
34
+ [I accepted the invitation but I don't see the account](#i-accepted-the-invitation-but-i-dont-see-the-account)
35
+ if you hit a mismatch.
36
+ 1. After signing in, the invited account appears in the **Switch Account**
37
+ submenu of your avatar dropdown.
38
+
39
+ </Stepper>
40
+
41
+ ## Switch accounts in the portal
42
+
43
+ Once you belong to multiple accounts, switch between them from the avatar menu.
44
+
45
+ <Stepper>
46
+
47
+ 1. Click your avatar in the **top-right corner** of the portal. The currently
48
+ active account name appears at the top of the dropdown.
49
+ 1. Hover over **Switch Account** to open the submenu, which lists every account
50
+ you belong to.
51
+ 1. Select the account you want to switch to.
52
+ 1. The portal reloads in the context of the selected account, showing that
53
+ account's projects, settings, and resources.
54
+
55
+ </Stepper>
56
+
57
+ After switching, all navigation within the portal (project lists, account
58
+ settings, billing, logs) reflects the selected account.
59
+
60
+ ## How multi-account membership works
61
+
62
+ When you first sign up for Zuplo, an account is automatically created for you.
63
+ An admin on another account can then invite you by opening their avatar menu,
64
+ selecting **Account Settings → Members**, clicking **Invite to account**, and
65
+ entering your email address and role. Once you accept, you become a member of
66
+ that account in addition to your own.
67
+
68
+ Each account is independent. Projects, billing, custom domains, and API keys all
69
+ belong to a specific account. When you switch accounts in the portal, you see
70
+ only the resources that belong to the account you switched into.
71
+
72
+ ## Roles in an invited account
73
+
74
+ Your role in the invited account determines what you can access. The account
75
+ admin assigns your role at invitation and can update it later. For the full
76
+ permission matrix, see [Role Permissions](./roles-and-permissions.mdx).
77
+
78
+ :::note
79
+
80
+ Role-Based Access Control (RBAC) with Developer and Member roles is an
81
+ enterprise feature. On non-enterprise accounts, all invited users are added as
82
+ **Admin** by default.
83
+
84
+ :::
85
+
86
+ ## Troubleshooting
87
+
88
+ ### I accepted the invitation but I don't see the account
89
+
90
+ This is almost always an **identity mismatch**. Zuplo supports signing in with
91
+ Google, GitHub, and email-password. Different sign-in methods are treated as
92
+ separate identities, even when the underlying email address is the same.
93
+
94
+ Work through this checklist:
95
+
96
+ - **Check the email address on the invitation.** Confirm it matches exactly the
97
+ email you use to sign in to Zuplo.
98
+ - **Check your sign-in method.** If the invitation was sent to `you@company.com`
99
+ and you usually sign in with Google OAuth using that same email, that
100
+ combination should work. But if you also created a separate email-password
101
+ account with the same address, Zuplo treats those as different identities.
102
+ Sign in with the method that matches how the invitation was sent.
103
+ - **Click the invitation link again.** Open the original invitation email and
104
+ click the accept link while signed in with the correct identity. If you were
105
+ signed in with a different identity the first time, the invitation may have
106
+ been applied to the wrong account.
107
+ - **Ask the account admin to verify.** The admin can check **Account Settings →
108
+ Members** to confirm the invitation status. If it shows as pending, it hasn't
109
+ been accepted yet. The admin can also remove and re-send the invitation to
110
+ ensure it goes to the right email.
111
+
112
+ ### I see the account but not its projects
113
+
114
+ If you switched into an account but the project list appears empty or you can't
115
+ open a specific project, check the following:
116
+
117
+ - **Your account-level role may not include project visibility.** Users with the
118
+ **Member** role at the account level cannot view projects unless an admin has
119
+ granted them a project-level role. Ask the account admin to assign you a role
120
+ on the specific project you need access to. See
121
+ [Managing Project Members](./managing-project-members.mdx) for details.
122
+ - **The project may require source control access.** Some projects are connected
123
+ to a GitHub, GitLab, Bitbucket, or Azure DevOps repository. You may need
124
+ access to the linked repository in addition to your Zuplo project role.
125
+
126
+ ### Why doesn't my invitation link work?
127
+
128
+ Invitation links can fail for a few reasons:
129
+
130
+ - **The link is expired or already used.** Ask the account admin to resend the
131
+ invitation from **Account Settings → Members**.
132
+ - **The link was opened while signed in as the wrong identity.** Sign out, open
133
+ the invitation in a fresh browser session, and sign in with the address the
134
+ invitation was sent to.
135
+
136
+ ### Why don't I see the account switcher in my avatar menu?
137
+
138
+ The **Switch Account** submenu only lists other accounts when you belong to more
139
+ than one. If hovering **Switch Account** shows only your current account, you
140
+ belong to a single account. Ask the relevant account admin to send (or resend)
141
+ an invitation to your address.
142
+
143
+ ### Do I get the invited account's plan features?
144
+
145
+ Yes. Plan-tier features (Free, Builder, Enterprise) are attached to the
146
+ **account**, not to individual users. While working inside a higher-tier
147
+ account, you have access to all features your role permits on that plan,
148
+ regardless of your personal account's plan.
149
+
150
+ When you switch back to your personal account, you have access only to the
151
+ features available on your personal account's plan.
152
+
153
+ ## Related topics
154
+
155
+ - [Managing Account Members](./managing-account-members.mdx): How to invite
156
+ users and manage their roles (admin perspective).
157
+ - [Managing Project Members](./managing-project-members.mdx): How to grant
158
+ project-level access to account members.
159
+ - [Role Permissions](./roles-and-permissions.mdx): Full permission matrix for
160
+ account and project roles.
@@ -61,136 +61,6 @@ curl \
61
61
  --header "Authorization: Bearer $ZAPI_KEY"
62
62
  ```
63
63
 
64
- ## Bucket monetization configuration
65
-
66
- Each bucket has an optional `MonetizationConfiguration` record that holds
67
- bucket-wide defaults. The configuration is read by the runtime and the Developer
68
- Portal.
69
-
70
- | Method | Path |
71
- | -------- | ---------------------------------------------------- |
72
- | `GET` | `/v3/metering/{bucketId}/monetization-configuration` |
73
- | `PUT` | `/v3/metering/{bucketId}/monetization-configuration` |
74
- | `DELETE` | `/v3/metering/{bucketId}/monetization-configuration` |
75
-
76
- The `PUT` endpoint upserts the record. At least one of the four fields below
77
- must be present. Pass any combination — fields that are omitted retain their
78
- previous value.
79
-
80
- | Field | Type | Description |
81
- | ------------------------------ | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
82
- | `multipleSubscriptionsEnabled` | `boolean` | Stored on the bucket. Reserved for future enforcement of multi-subscription rules; today the Developer Portal create-subscription path enforces a single active subscription per customer regardless of this flag. |
83
- | `planOrder` | `string[]` | Plan keys in display order. Drives the pricing page sort and is used by [plan changes](./subscription-lifecycle.md#plan-changes-upgrades-and-downgrades) to decide upgrade vs downgrade — moving to a plan with a higher (or equal) index is treated as an upgrade with `"immediate"` timing; a lower index is a downgrade with `"next_billing_cycle"` timing. |
84
- | `planSettings` | `object` | Per-plan overrides keyed by plan key. The supported sub-key today is `visiblePhases` — an array of phase keys that should appear on the pricing page. Omitted means no filtering; `[]` hides all phases for that plan. |
85
- | `maxPaymentOverdueDays` | `integer` (`>= 0`) | Bucket-level grace period for overdue payments. Used as the lowest-priority value in the resolution chain: customer metadata → plan metadata → this bucket value → built-in default of `3` days. See [Subscription and payment validation](./monetization-policy.md#subscription-and-payment-validation). |
86
-
87
- ```bash
88
- curl -X PUT "https://dev.zuplo.com/v3/metering/$BUCKET_ID/monetization-configuration" \
89
- --header "Authorization: Bearer $ZAPI_KEY" \
90
- --header "Content-Type: application/json" \
91
- --data '{
92
- "planOrder": ["free", "developer", "pro"],
93
- "planSettings": {
94
- "pro": { "visiblePhases": ["default"] }
95
- },
96
- "maxPaymentOverdueDays": 7
97
- }'
98
- ```
99
-
100
- `DELETE` removes the record entirely; `GET` on a bucket with no record returns
101
- the schema defaults (`multipleSubscriptionsEnabled: false`, `planOrder: []`,
102
- `planSettings: {}`, `maxPaymentOverdueDays: 3`).
103
-
104
- ## Stripe setup and billing readiness
105
-
106
- These endpoints script the Stripe integration that the
107
- [Monetization Service UI](./stripe-integration.md#connecting-your-stripe-account)
108
- runs interactively. They live on the Zuplo developer API, so the request shape
109
- is documented here.
110
-
111
- ### Connect a Stripe app
112
-
113
- ```http
114
- POST /v3/metering/{bucketId}/setup/stripe
115
- ```
116
-
117
- Installs a Stripe app on the bucket and creates the default billing profile
118
- linked to that app.
119
-
120
- | Field | Type | Description |
121
- | ------------- | --------- | ------------------------------------------------------------------------------------------------ |
122
- | `apiKey` | `string` | Stripe secret or restricted key. Required. |
123
- | `name` | `string` | Display name for the app. Required. |
124
- | `taxEnabled` | `boolean` | Initial value for `workflow.tax.enabled` on the billing profile. Optional; defaults to `false`. |
125
- | `taxEnforced` | `boolean` | Initial value for `workflow.tax.enforced` on the billing profile. Optional; defaults to `false`. |
126
- | `country` | `string` | ISO 3166-1 alpha-2 supplier country for the billing profile. Optional; defaults to `"US"`. |
127
-
128
- The request fails if the key prefix does not match the bucket environment:
129
-
130
- - Working-copy or preview buckets accept `sk_test_*` or `rk_test_*`.
131
- - Production buckets accept `sk_live_*` or `rk_live_*`.
132
-
133
- ```bash
134
- curl -X POST "https://dev.zuplo.com/v3/metering/$BUCKET_ID/setup/stripe" \
135
- --header "Authorization: Bearer $ZAPI_KEY" \
136
- --header "Content-Type: application/json" \
137
- --data '{
138
- "apiKey": "rk_test_...",
139
- "name": "Zuplo Monetization (test)",
140
- "taxEnabled": false,
141
- "country": "US"
142
- }'
143
- ```
144
-
145
- ### Read the connected Stripe app
146
-
147
- ```http
148
- GET /v3/metering/{bucketId}/setup/stripe
149
- ```
150
-
151
- Returns a summary of the connected Stripe app, the matched billing profile, and
152
- connection-test status. Use this to confirm the integration is wired up before
153
- continuing.
154
-
155
- ### Add a billing profile to a Stripe app
156
-
157
- ```http
158
- POST /v3/metering/{bucketId}/setup/stripe/{stripeAppId}/billing-profile
159
- ```
160
-
161
- Creates an additional billing profile against an already-installed Stripe app.
162
- This is rarely needed — the default profile is created during initial setup. Use
163
- this endpoint to create per-supplier-country profiles.
164
-
165
- ### Check billing readiness
166
-
167
- ```http
168
- GET /v3/metering/{bucketId}/billing-readiness
169
- ```
170
-
171
- Returns:
172
-
173
- ```json
174
- {
175
- "hasStripeApp": true,
176
- "stripeAppId": "app_01H...",
177
- "hasDefaultBillingProfile": true,
178
- "defaultBillingProfileId": "bp_01H..."
179
- }
180
- ```
181
-
182
- Use this in setup wizards to gate the UI on whether Stripe is connected.
183
-
184
- ### Update an app
185
-
186
- ```http
187
- PUT /v3/metering/{bucketId}/apps/{appId}
188
- ```
189
-
190
- Replaces an app's configuration (name, description, metadata, and — for Stripe
191
- apps — `secretAPIKey`). The same key-prefix validation as `POST /setup/stripe`
192
- applies: a Stripe key must match the bucket environment.
193
-
194
64
  ## API Reference
195
65
 
196
66
  For complete API operations, see the API Reference documentation:
@@ -0,0 +1,516 @@
1
+ ---
2
+ title: Going to Production with Monetization
3
+ sidebar_label: Going to Production
4
+ description:
5
+ Pre-production checklist, Stripe live-mode cutover, billing model readiness,
6
+ and beta limitations for launching Zuplo API monetization.
7
+ ---
8
+
9
+ :::note{title="Beta"}
10
+
11
+ API Monetization is in beta and free to try. The APIs are stable but should be
12
+ evaluated in non-production environments first. To go to production, contact
13
+ [sales@zuplo.com](mailto:sales@zuplo.com). Production pricing has not yet been
14
+ announced.
15
+
16
+ :::
17
+
18
+ You have built out your monetization configuration in Stripe test mode and your
19
+ customers are ready to pay real money. This guide covers:
20
+
21
+ - The **pre-production checklist**: items to verify before enabling real charges
22
+ - **Billing model readiness**: which pricing models are production-ready today
23
+ - **Stripe live-mode cutover**: step-by-step instructions to connect live
24
+ payments
25
+ - **Beta limitations**: constraints to design around before launch
26
+
27
+ ## Before you start
28
+
29
+ Going to production with monetization requires coordination with the Zuplo team.
30
+ The monetization feature is currently in **public beta**. The APIs are stable
31
+ and the core billing flows work end-to-end, but production pricing for the
32
+ monetization feature itself has not yet been announced.
33
+
34
+ :::tip{title="Email sales to go live"}
35
+
36
+ Email [sales@zuplo.com](mailto:sales@zuplo.com) with:
37
+
38
+ - Your account slug and project slug
39
+ - The Stripe account ID you plan to use in production
40
+ - Your target go-live date
41
+ - A summary of your pricing model (flat-fee, overages, pay-as-you-go, etc.)
42
+
43
+ :::
44
+
45
+ The Zuplo team will confirm your configuration, walk you through any
46
+ beta-specific considerations for your use case, and enable your production
47
+ bucket for live billing.
48
+
49
+ ## Pre-production checklist
50
+
51
+ Work through each item before enabling real charges. The summary table lists
52
+ every check; the sections below give the detail and links.
53
+
54
+ | # | Check | Why it matters |
55
+ | --- | ---------------------------------- | --------------------------------------------------------------------- |
56
+ | 1 | Authentication provider verified | Customers cannot sign in, subscribe, or manage keys without it |
57
+ | 2 | Meters, features, plans configured | Configuration is per-bucket and does not promote between environments |
58
+ | 3 | Stripe live-mode connected | Test keys and live keys are completely separate environments |
59
+ | 4 | Billing profile configured | Tax calculations and supplier country depend on it |
60
+ | 5 | Webhook endpoint validated | Payment events (charges, failures, disputes) must reach Zuplo |
61
+ | 6 | Quota behavior chosen and tested | Hard vs. soft limits change customer experience and billing |
62
+ | 7 | Subscription lifecycle tested | Every state transition must work before real money is on the line |
63
+ | 8 | Payment grace period configured | Controls when overdue customers lose API access |
64
+
65
+ ### Authentication provider verified
66
+
67
+ Your Developer Portal must have an authentication provider configured so that
68
+ customers can sign in, subscribe, and manage their API keys. Verify that your
69
+ auth provider (Auth0, Clerk, or a custom OpenID Connect provider) works
70
+ correctly across all environments: working copy, preview, and production.
71
+
72
+ See
73
+ [Developer Portal Setup → Prerequisites](./developer-portal.md#prerequisites)
74
+ for details.
75
+
76
+ ### Meters, features, and plans configured
77
+
78
+ Confirm that your production bucket has the same meters, features, and plans you
79
+ tested in your working-copy or preview bucket. Monetization configuration is
80
+ scoped per-bucket, so you must recreate it in production. It does not
81
+ automatically promote between environments.
82
+
83
+ Key checks:
84
+
85
+ - **Meter keys match policy configuration.** The meter `slug`/`eventType`, the
86
+ feature `key`, and the `meters` map in your `MonetizationInboundPolicy` must
87
+ all use the same key. See
88
+ [Meters → Naming Consistency](./meters.mdx#naming-consistency).
89
+ - **Plans are published.** Draft plans are not visible to customers. Publish
90
+ each plan from the Monetization Service in the Zuplo Portal.
91
+ - **Currency is correct.** Plans support any ISO 4217 currency code (USD, EUR,
92
+ AUD, GBP, etc.). Verify the `currency` field on every plan. It cannot be
93
+ changed after a plan is created.
94
+ - **Plan ordering is set.** The
95
+ [monetization configuration](./api-access.mdx#bucket-monetization-configuration)
96
+ `planOrder` array controls both the pricing page display order and
97
+ upgrade/downgrade logic. Make sure it reflects your intended tier hierarchy.
98
+
99
+ ### Stripe live-mode connected
100
+
101
+ Your production environment must use a Stripe **live** key (`sk_live_*` or
102
+ `rk_live_*`). Test keys and live keys are completely separate environments in
103
+ Stripe. Customers, invoices, and payment methods created in test mode do not
104
+ exist in live mode.
105
+
106
+ :::tip
107
+
108
+ Use a **restricted key** (`rk_live_*`) rather than a secret key. A restricted
109
+ key follows the principle of least privilege. See
110
+ [Using a Stripe restricted key](./stripe-integration.md#using-a-stripe-restricted-key)
111
+ for the exact eight permissions your key needs.
112
+
113
+ :::
114
+
115
+ :::caution
116
+
117
+ Use one Stripe key type per Zuplo environment. Do not replace a test key with a
118
+ live key in the same environment. Zuplo rejects a live key on a non-production
119
+ bucket and a test key on a production bucket.
120
+
121
+ :::
122
+
123
+ ### Billing profile configured
124
+
125
+ Every bucket that processes payments needs a default billing profile. The
126
+ billing profile is created automatically when you connect Stripe, but you should
127
+ verify:
128
+
129
+ | Setting | What to check | Reference |
130
+ | -------------------- | ---------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
131
+ | **Supplier country** | Set correctly. Tax calculations depend on this value | [Enable tax collection](./tax-collection.md#enable-tax-collection) |
132
+ | **Tax collection** | Tax registrations added in Stripe and `workflow.tax.enabled` set | [Add tax registrations in Stripe](./tax-collection.md#add-tax-registrations-in-stripe) |
133
+ | **Tax behavior** | Inclusive vs. exclusive matches your pricing page | [Tax behavior configuration](./tax-collection.md#tax-behavior-configuration) |
134
+
135
+ ### Webhook endpoint validated
136
+
137
+ When you connect Stripe, Zuplo registers a webhook endpoint automatically so it
138
+ can react to payment events (successful charges, failed payments, disputes).
139
+ Verify the webhook is healthy:
140
+
141
+ <Stepper>
142
+
143
+ 1. Open your
144
+ [Stripe Dashboard → Developers → Webhooks](https://dashboard.stripe.com/webhooks).
145
+
146
+ 1. Find the Zuplo-managed endpoint.
147
+
148
+ 1. Confirm recent deliveries show `2xx` responses (typically `201 OK`), not
149
+ failures or timeouts.
150
+
151
+ </Stepper>
152
+
153
+ <ModalScreenshot size="md">
154
+
155
+ ![Stripe Dashboard Webhooks workbench showing event deliveries to the Zuplo metering endpoint with consecutive 201 OK responses for invoice.paid, invoice.payment_succeeded, and invoice.finalized events](../../../public/media/monetization-going-to-production/stripe-webhooks.png)
156
+
157
+ </ModalScreenshot>
158
+
159
+ ### Quota behavior chosen and tested
160
+
161
+ Each metered entitlement can enforce a **hard limit** or a **soft limit**:
162
+
163
+ | Setting | Quota exhausted behavior | Overage billed? |
164
+ | -------------------- | -------------------------------- | ------------------------ |
165
+ | `isSoftLimit: false` | Returns `403 Forbidden` | No |
166
+ | `isSoftLimit: true` | Request allowed, usage continues | Yes, per-unit in arrears |
167
+
168
+ Test both paths in your working-copy environment before going live:
169
+
170
+ <Stepper>
171
+
172
+ 1. Subscribe to a plan with a low quota (e.g., 10 requests).
173
+
174
+ 1. Exceed the quota and verify the correct behavior: either a `403` response
175
+ (hard limit) or continued access with usage counting above the entitlement
176
+ (soft limit).
177
+
178
+ 1. Check the Stripe test dashboard to verify that invoices include the expected
179
+ line items.
180
+
181
+ </Stepper>
182
+
183
+ See [Rate Cards](./rate-cards.mdx) for how `isSoftLimit` and `issueAfterReset`
184
+ interact, and [Monetization Policy Reference](./monetization-policy.md) for how
185
+ the gateway enforces limits.
186
+
187
+ ### Subscription lifecycle tested
188
+
189
+ Before real money is on the line, walk through every lifecycle state:
190
+
191
+ | State | How to test |
192
+ | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
193
+ | Subscribe | Subscribe to a plan via the Developer Portal using [Stripe test cards](https://docs.stripe.com/testing) |
194
+ | Upgrade | Move from a lower plan to a higher plan. Entitlements should change immediately and the proration credit should appear on the next invoice |
195
+ | Downgrade | Move from a higher plan to a lower plan. The change should take effect at the next billing cycle, not immediately |
196
+ | Cancel | Cancel a subscription. Access should continue until the billing period ends, then be revoked |
197
+ | Reactivate | Reactivate a canceled subscription before the period ends. Access should be restored |
198
+ | Failed payment | Use Stripe test card `4000 0000 0000 0341` (attaches to customer but fails on charge) to verify grace period behavior and that access is blocked after the configured `maxPaymentOverdueDays` |
199
+
200
+ See [Subscription Lifecycle](./subscription-lifecycle.md) for the full details
201
+ on each state transition.
202
+
203
+ ### Payment grace period configured
204
+
205
+ The default grace period for overdue payments is 3 days. During this window,
206
+ customers retain API access while Stripe retries the charge. After the grace
207
+ period, access is blocked.
208
+
209
+ You can customize the grace period at three levels. Higher precedence overrides
210
+ lower:
211
+
212
+ | Precedence | Where | Field / Key |
213
+ | ----------- | -------------------------- | ----------------------------------------------------------------------------------------- |
214
+ | 1 (highest) | Stripe customer metadata | `zuplo_max_payment_overdue_days` |
215
+ | 2 | Stripe plan metadata | `zuplo_max_payment_overdue_days` |
216
+ | 3 (lowest) | Bucket monetization config | `maxPaymentOverdueDays` ([reference](./api-access.mdx#bucket-monetization-configuration)) |
217
+
218
+ Set the value to `0` to block access immediately when payment fails.
219
+
220
+ ## Billing models in production
221
+
222
+ Not all billing models have the same level of portal support today. Choose the
223
+ right model for your launch based on current readiness.
224
+
225
+ | Model | Status | Notes |
226
+ | ---------------------------- | ---------------------- | ------------------------------------------------------------- |
227
+ | Fixed monthly quotas | Production-ready | Fully supported end-to-end |
228
+ | Monthly quotas with overages | Production-ready | Modeled as graduated tiered pricing with `isSoftLimit: true` |
229
+ | Pay-as-you-go | Limited portal support | Underlying model works; portal pricing table not fully tested |
230
+ | Credits / tokens (prepaid) | Limited portal support | Underlying model works; portal experience not fully tested |
231
+
232
+ ### Fixed monthly quotas (production-ready)
233
+
234
+ The most common and most stable model. Customers pay a flat monthly price for an
235
+ included number of requests. When the quota is exhausted, the API returns
236
+ `403 Forbidden` (hard limit) or bills overages (soft limit).
237
+
238
+ This model is fully supported in the Developer Portal pricing table, checkout
239
+ flow, usage dashboard, and invoicing.
240
+
241
+ See
242
+ [Billing Models → Fixed monthly quotas](./billing-models.md#fixed-monthly-quotas)
243
+ for configuration examples.
244
+
245
+ ### Monthly quotas with overages (production-ready)
246
+
247
+ A hybrid model where customers pay a base price for an included allowance, with
248
+ per-unit overage billing in arrears for usage above the limit. This is modeled
249
+ using graduated tiered pricing with `isSoftLimit: true`.
250
+
251
+ Fully supported end-to-end. See
252
+ [Billing Models → Monthly quotas with overages](./billing-models.md#monthly-quotas-with-overages)
253
+ for examples.
254
+
255
+ ### Pay-as-you-go (limited portal support)
256
+
257
+ Pure pay-as-you-go billing (no upfront cost, bill entirely in arrears for actual
258
+ usage) is supported by the underlying monetization models, but the **Developer
259
+ Portal pricing table experience has not been fully tested** for this billing
260
+ model yet.
261
+
262
+ If you need pay-as-you-go pricing in production, contact
263
+ [support@zuplo.com](mailto:support@zuplo.com) to discuss your use case and
264
+ current workarounds.
265
+
266
+ See [Billing Models → Pay-as-you-go](./billing-models.md#pay-as-you-go) for the
267
+ data model and configuration.
268
+
269
+ ### Credits / tokens (prepaid, limited portal support)
270
+
271
+ Credit/token-based billing is supported by the underlying data model, but the
272
+ **Developer Portal experience has not been fully tested** for this billing
273
+ model.
274
+
275
+ If you need prepaid credit billing, contact
276
+ [sales@zuplo.com](mailto:sales@zuplo.com) to discuss your use case.
277
+
278
+ See
279
+ [Billing Models → Credits / tokens](./billing-models.md#credits--tokens-prepaid)
280
+ for configuration examples.
281
+
282
+ ## How usage metering works with Stripe
283
+
284
+ A common point of confusion: Zuplo does **not** use Stripe Billing Meters,
285
+ Stripe Subscriptions, Stripe Products, or Stripe Prices. Zuplo manages plans,
286
+ subscriptions, metering, and entitlements internally. Stripe is used only for
287
+ **money**: collecting payments and issuing invoices.
288
+
289
+ ![API requests are metered by Zuplo's MonetizationInboundPolicy, aggregated and priced inside the Zuplo platform, then materialized as a Stripe invoice at the end of the billing period for Stripe to collect from the customer](../../../public/media/monetization-going-to-production/metering-flow.png)
290
+
291
+ The flow works like this:
292
+
293
+ 1. Every API request that hits a monetized route is metered in real time by the
294
+ `MonetizationInboundPolicy`.
295
+ 2. Usage events are aggregated internally by Zuplo. They are not sent to Stripe
296
+ as individual events.
297
+ 3. At the end of each billing period, Zuplo calculates the total usage, applies
298
+ the plan's pricing model (flat fee, tiered, per-unit, etc.), and creates a
299
+ **Stripe Invoice** with the appropriate line items.
300
+ 4. Stripe collects payment from the customer's saved payment method.
301
+
302
+ This means you will **not** see usage events, billing meters, or subscription
303
+ objects in your Stripe dashboard. You will see **Customers** and **Invoices**.
304
+ To check usage and subscription state, use the
305
+ [Zuplo metering API](./api-access.mdx#authentication) or the Developer Portal's
306
+ usage dashboard.
307
+
308
+ <details>
309
+ <summary>Why usage events might not appear in Stripe</summary>
310
+
311
+ If you expected to see per-request usage events in Stripe and they are not
312
+ there, this is by design. Zuplo does not call Stripe's metered billing APIs.
313
+ Usage is materialized as invoice line items at billing time only.
314
+
315
+ To verify that metering is working:
316
+
317
+ 1. Make API requests to a monetized endpoint.
318
+ 2. Check the Developer Portal usage dashboard. It should show real-time usage.
319
+ 3. Query the meter directly via the API:
320
+
321
+ ```bash
322
+ curl -X POST "https://dev.zuplo.com/v3/metering/$BUCKET_ID/meters/api_requests/query" \
323
+ -H "Authorization: Bearer $ZAPI_KEY" \
324
+ -H "Content-Type: application/json" \
325
+ -d '{
326
+ "filterSubscription": ["SUBSCRIPTION_ID"],
327
+ "from": "2026-05-01T00:00:00Z",
328
+ "to": "2026-05-31T23:59:59Z",
329
+ "windowSize": "DAY"
330
+ }'
331
+ ```
332
+
333
+ If the usage dashboard shows zero despite active traffic, see
334
+ [Troubleshooting → Usage dashboard shows zero](./troubleshooting.md#usage-dashboard-shows-zero-despite-active-api-traffic).
335
+
336
+ </details>
337
+
338
+ ## Connecting Stripe live mode
339
+
340
+ When your test configuration is validated and you are ready to accept real
341
+ payments, follow these steps to connect your production environment to Stripe
342
+ live mode.
343
+
344
+ ### Step 1: Create a Stripe restricted key for production
345
+
346
+ <Stepper>
347
+
348
+ 1. In the [Stripe Dashboard](https://dashboard.stripe.com), switch to **live
349
+ mode** (toggle in the top-right corner).
350
+
351
+ 1. Go to **Developers → API keys → Create restricted key**.
352
+
353
+ 1. Name the key `Zuplo Monetization (production)`.
354
+
355
+ 1. Enable the eight permissions listed in
356
+ [Using a Stripe restricted key](./stripe-integration.md#using-a-stripe-restricted-key).
357
+
358
+ 1. Click **Create key** and copy the value (`rk_live_...`).
359
+
360
+ </Stepper>
361
+
362
+ ### Step 2: Connect to your production environment
363
+
364
+ <Stepper>
365
+
366
+ 1. Open your project's
367
+ [**Services**](https://portal.zuplo.com/+/account/project/services) page in
368
+ the Zuplo Portal.
369
+
370
+ 1. Select the **Production** environment.
371
+
372
+ 1. Open the Monetization Service, then go to **Payment Provider**.
373
+
374
+ 1. Paste your live restricted key (`rk_live_...`) and click **Save**.
375
+
376
+ </Stepper>
377
+
378
+ <ModalScreenshot size="md">
379
+
380
+ ![Zuplo Portal Monetization Service with the Prod environment selected and the Payment Provider tab open, showing the disconnected Stripe card, Stripe API Key field with Save button, Billing Profiles, and Payment Overage sections](../../../public/media/monetization-going-to-production/configure-stripe-production.png)
381
+
382
+ </ModalScreenshot>
383
+
384
+ ### Step 3: Recreate your monetization configuration
385
+
386
+ Because monetization configuration is scoped per-bucket, you need to set up
387
+ meters, features, plans, and the monetization configuration in your production
388
+ bucket. You can do this through the Portal UI or via the
389
+ [monetization APIs](./api-access.mdx).
390
+
391
+ ### Step 4: Verify with a real charge
392
+
393
+ <Stepper>
394
+
395
+ 1. Publish at least one plan in your production environment.
396
+
397
+ 1. Open the Developer Portal on your production URL.
398
+
399
+ 1. Subscribe to a plan using a real payment method.
400
+
401
+ 1. Confirm in the Stripe Dashboard (live mode) that a **Customer** was created
402
+ and the **Webhook** endpoint is registered and showing `2xx` responses.
403
+
404
+ 1. Make a few API requests and verify the usage dashboard updates.
405
+
406
+ 1. Cancel the test subscription when done.
407
+
408
+ </Stepper>
409
+
410
+ ### Step 5: Monitor first production transactions
411
+
412
+ After going live, keep a close eye on:
413
+
414
+ | Surface | What to check |
415
+ | ---------------------------------- | ---------------------------------------------------------------------------------------- |
416
+ | Stripe Dashboard → Invoices | Invoices are created at the end of each billing period with the correct line items |
417
+ | Stripe Dashboard → Webhooks | All webhook deliveries are succeeding |
418
+ | Developer Portal → Usage Dashboard | Customer-facing usage numbers match your expectations |
419
+ | API responses | Monetized routes return `200` for subscribed customers and `403` for over-quota requests |
420
+
421
+ ## Known beta limitations
422
+
423
+ The following limitations apply during the beta period. Design around them when
424
+ planning your production launch. As noted in
425
+ [Before you start](#before-you-start), production access requires coordination
426
+ with the Zuplo sales team, and production pricing for the monetization feature
427
+ has not yet been announced.
428
+
429
+ <details>
430
+ <summary>Pay-as-you-go and credits portal experience</summary>
431
+
432
+ Pure pay-as-you-go and credit/token-based billing models are supported by the
433
+ underlying data model and APIs, but the Developer Portal pricing table has not
434
+ been fully tested for these models. If your business requires either model,
435
+ coordinate with Zuplo support for guidance on workarounds.
436
+
437
+ </details>
438
+
439
+ <details>
440
+ <summary>Configuration does not promote between environments</summary>
441
+
442
+ Meters, features, plans, and monetization configuration are scoped to individual
443
+ buckets. There is no built-in promotion mechanism to copy configuration from
444
+ working-copy to production. You must recreate or script the configuration in
445
+ each environment separately using the APIs documented in
446
+ [API Access](./api-access.mdx).
447
+
448
+ </details>
449
+
450
+ <details>
451
+ <summary>Single active subscription per customer (default)</summary>
452
+
453
+ By default, each customer can hold one active subscription at a time.
454
+ Multi-subscription support (e.g., a primary subscription plus a credit pack) is
455
+ available on request. Contact [sales@zuplo.com](mailto:sales@zuplo.com) to
456
+ enable it for your bucket.
457
+
458
+ See
459
+ [Subscription Lifecycle → Subscriptions per customer](./subscription-lifecycle.md#subscriptions-per-customer)
460
+ for details on multi-subscription scenarios.
461
+
462
+ </details>
463
+
464
+ ## Zuplo plan requirements for monetization
465
+
466
+ Monetization is available during beta on all Zuplo plan tiers, including Free
467
+ and Builder. However, production workloads typically need capabilities that
468
+ exceed what the Free and Builder tiers offer.
469
+
470
+ | Plan | Gateway devs | Custom domains | Support | Production fit |
471
+ | ----------------------- | ------------ | -------------- | --------------------------- | ----------------------------------------------------------------- |
472
+ | **Free** | Up to 2 | 0 | Community | Testing monetization only; not suitable for production billing |
473
+ | **Builder** ($25/mo) | Up to 2 | 2 | Community | Small-scale production if you don't need more team members or SLA |
474
+ | **Enterprise** (custom) | Custom | Custom | Priority, SLA up to 99.999% | Required for >2 devs, additional domains, log retention, or SLA |
475
+
476
+ There is currently no self-serve plan between Builder and Enterprise. If you
477
+ need capabilities beyond Builder but are not ready for a full Enterprise
478
+ engagement, email [sales@zuplo.com](mailto:sales@zuplo.com) to discuss your
479
+ options.
480
+
481
+ For current plan details and pricing, see the
482
+ [Zuplo pricing page](https://zuplo.com/pricing).
483
+
484
+ ## Getting help when going live
485
+
486
+ | Situation | Contact |
487
+ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- |
488
+ | Ready to enable production billing for the first time, discussing production pricing, multi-subscription support, or a plan between Builder and Enterprise | [sales@zuplo.com](mailto:sales@zuplo.com) |
489
+ | Something is not working as expected, debugging a billing or metering issue, or questions about a beta limitation or workaround | [support@zuplo.com](mailto:support@zuplo.com) |
490
+
491
+ ### What to include in your request
492
+
493
+ To help the team resolve your issue quickly, include:
494
+
495
+ | Field | Where to find / what to provide |
496
+ | ------------------ | ------------------------------------------------------ |
497
+ | Account slug | Your Zuplo account identifier |
498
+ | Project slug | The project with monetization enabled |
499
+ | Bucket ID | Project Services → Bucket Details |
500
+ | Stripe account ID | Stripe Dashboard → Settings, starts with `acct_` |
501
+ | Plan keys | The plan keys involved in the issue |
502
+ | Subscription ID | If the issue is subscription-specific |
503
+ | Steps to reproduce | What you did, what you expected, what happened instead |
504
+
505
+ ## Next steps
506
+
507
+ Once you have completed the checklist, connected Stripe live mode, and verified
508
+ your first real charge, your monetized API is in production. Monitor your
509
+ [Stripe Dashboard](https://dashboard.stripe.com) webhooks and invoices closely
510
+ during the first billing cycle.
511
+
512
+ - [Troubleshooting](./troubleshooting.md): common issues and debugging tools
513
+ - [Subscription Lifecycle](./subscription-lifecycle.md): managing ongoing
514
+ upgrades, downgrades, and cancellations
515
+ - [Billing Models Guide](./billing-models.md): exploring additional pricing
516
+ strategies as your business grows
@@ -110,5 +110,6 @@ pricing, wire up Stripe, and configure your gateway to enforce quotas.
110
110
  | [Subscription Lifecycle](./monetization/subscription-lifecycle.md) | Managing trials, upgrades, downgrades, and cancellations |
111
111
  | [Private Plans](./monetization/private-plans.md) | Invite-only plans for specific users |
112
112
  | [Tax Collection](./monetization/tax-collection.md) | Enabling VAT, sales tax, or GST via Stripe Tax |
113
+ | [Going to Production](./monetization/going-to-production.mdx) | Pre-launch checklist, Stripe live mode, and beta considerations |
113
114
  | [Plan Examples](./monetization/plan-examples.mdx) | Real-world plan configurations |
114
115
  | [Troubleshooting](./monetization/troubleshooting.md) | Common issues, debugging, and FAQ |
@@ -154,10 +154,7 @@ below it:
154
154
 
155
155
  1. **Customer metadata** — `zuplo_max_payment_overdue_days` on the customer
156
156
  2. **Plan metadata** — `zuplo_max_payment_overdue_days` on the plan
157
- 3. **Bucket configuration** — `maxPaymentOverdueDays` on the bucket's
158
- monetization configuration (PUT
159
- `/v3/metering/{bucketId}/monetization-configuration`)
160
- 4. **Default** — `3` days
157
+ 3. **Default** — `3` days
161
158
 
162
159
  Set the value to `0` to block requests immediately when payment is overdue.
163
160
 
@@ -190,6 +190,17 @@ curl \
190
190
  "name": "Default",
191
191
  "duration": null,
192
192
  "rateCards": [
193
+ {
194
+ "type": "flat_fee",
195
+ "key": "subscription_fee",
196
+ "name": "Starter Plan Subscription",
197
+ "billingCadence": "P1M",
198
+ "price": {
199
+ "type": "flat",
200
+ "amount": "9.99",
201
+ "paymentTerm": "in_advance"
202
+ }
203
+ },
193
204
  {
194
205
  "type": "usage_based",
195
206
  "key": "api_requests",
@@ -208,7 +219,7 @@ curl \
208
219
  "tiers": [
209
220
  {
210
221
  "upToAmount": "1000",
211
- "flatPrice": { "type": "flat", "amount": "9.99" },
222
+ "flatPrice": { "type": "flat", "amount": "0.00" },
212
223
  "unitPrice": null
213
224
  },
214
225
  {
@@ -227,10 +238,16 @@ EOF
227
238
 
228
239
  **What changed:**
229
240
 
230
- - Changed the rate card from `flat_fee` to `usage_based`
231
- - Changed `isSoftLimit` to `true` - requests continue after 1,000
232
- - Combined base fee and overage into tiered pricing: first 1,000 cost $9.99
233
- flat, then $0.01 per additional request
241
+ - Split the default phase into two rate cards: a billing-only `flat_fee` rate
242
+ card (no `featureKey`, `paymentTerm: "in_advance"`) that collects the $9.99
243
+ subscription fee at the start of each billing period, and a `usage_based` rate
244
+ card that grants the entitlement and prices overage
245
+ - Tier 1's `flatPrice` is set to $0 because the $9.99 is now collected by the
246
+ separate `flat_fee` rate card — leaving it at $9.99 here would double-charge
247
+ the customer in arrears at the end of the period
248
+ - Changed `isSoftLimit` to `true` so requests continue past 1,000 and tier 2
249
+ applies — overage is billed at $0.01 per request above the allowance, in
250
+ arrears at the end of the period
234
251
 
235
252
  **Example billing:**
236
253
 
@@ -305,6 +322,17 @@ curl \
305
322
  "name": "Default",
306
323
  "duration": null,
307
324
  "rateCards": [
325
+ {
326
+ "type": "flat_fee",
327
+ "key": "subscription_fee",
328
+ "name": "Starter Plan Subscription",
329
+ "billingCadence": "P1M",
330
+ "price": {
331
+ "type": "flat",
332
+ "amount": "9.99",
333
+ "paymentTerm": "in_advance"
334
+ }
335
+ },
308
336
  {
309
337
  "type": "usage_based",
310
338
  "key": "api_requests",
@@ -323,7 +351,7 @@ curl \
323
351
  "tiers": [
324
352
  {
325
353
  "upToAmount": "1000",
326
- "flatPrice": { "type": "flat", "amount": "9.99" },
354
+ "flatPrice": { "type": "flat", "amount": "0.00" },
327
355
  "unitPrice": null
328
356
  },
329
357
  {
@@ -49,15 +49,6 @@ in Stripe at the moment a charge is needed (as invoice line items).
49
49
  3. Paste your **Stripe API Key**
50
50
  4. Click **Save**
51
51
 
52
- :::tip
53
-
54
- To script the same flow (CI, infrastructure-as-code, etc.) use the
55
- [Stripe setup API](./api-access.mdx#stripe-setup-and-billing-readiness) — it
56
- exposes `POST /setup/stripe`, `GET /billing-readiness`, and key-rotation
57
- endpoints with the same prefix validation as the UI.
58
-
59
- :::
60
-
61
52
  The connection authorizes Zuplo to manage Stripe objects on your behalf —
62
53
  specifically Customers, Checkout Sessions, Customer Portal Sessions, Invoices,
63
54
  and Tax Calculations. See
@@ -255,9 +246,8 @@ Stripe retries the payment.
255
246
  | `failed` | Access blocked after grace period (configurable) |
256
247
  | `uncollectible` | Access blocked |
257
248
 
258
- The grace period is configurable, with customer metadata overriding plan
259
- metadata, which overrides the bucket-level `maxPaymentOverdueDays`. Default is 3
260
- days. See
249
+ The grace period is configurable via customer or plan metadata, with customer
250
+ metadata overriding plan metadata. Default is 3 days. See
261
251
  [Subscription and payment validation](./monetization-policy.md#subscription-and-payment-validation)
262
252
  for the full resolution order.
263
253
 
@@ -26,13 +26,12 @@ announced.
26
26
  access is blocked.
27
27
 
28
28
  ```bash
29
- curl https://dev.zuplo.com/v3/metering/{bucketId}/customers/{CUSTOMER_ID}/subscriptions \
30
- -H "Authorization: Bearer {API_KEY}"
29
+ curl https://dev.zuplo.com/v3/metering/${BUCKET_ID}/customers/${CUSTOMER_ID}/subscriptions \
30
+ -H "Authorization: Bearer ${API_KEY}"
31
31
  ```
32
32
 
33
33
  Fix: Either resolve the payment issue in Stripe, or adjust the grace period.
34
- The window resolves customer metadata → plan metadata → bucket configuration
35
- → 3-day default. See
34
+ The window resolves customer metadata → plan metadata → 3-day default. See
36
35
  [Subscription and payment validation](./monetization-policy.md#subscription-and-payment-validation)
37
36
  for details.
38
37
 
@@ -142,27 +141,29 @@ entitlements don't change.
142
141
 
143
142
  ```bash
144
143
  # List subscriptions for a customer
145
- curl https://dev.zuplo.com/v3/metering/{bucketId}/customers/{CUSTOMER_ID}/subscriptions \
146
- -H "Authorization: Bearer {API_KEY}"
144
+ curl https://dev.zuplo.com/v3/metering/${BUCKET_ID}/customers/${CUSTOMER_ID}/subscriptions \
145
+ -H "Authorization: Bearer ${API_KEY}"
147
146
 
148
147
  # Check subscription access and entitlements
149
- curl https://dev.zuplo.com/v3/metering/{bucketId}/subscriptions/{subscriptionId}/access \
150
- -H "Authorization: Bearer {API_KEY}"
148
+ curl https://dev.zuplo.com/v3/metering/${BUCKET_ID}/subscriptions/${SUBSCRIPTION_ID}/access \
149
+ -H "Authorization: Bearer ${API_KEY}"
151
150
  ```
152
151
 
153
152
  ### Check meter usage
154
153
 
155
154
  ```bash
156
155
  # Query meter usage for the current month
157
- curl -X POST https://dev.zuplo.com/v3/metering/{bucketId}/meters/{meterIdOrSlug}/query \
158
- -H "Authorization: Bearer {API_KEY}" \
156
+ curl -X POST https://dev.zuplo.com/v3/metering/${BUCKET_ID}/meters/${METER_ID_OR_SLUG}/query \
157
+ -H "Authorization: Bearer ${API_KEY}" \
159
158
  -H "Content-Type: application/json" \
160
- -d '{
161
- "filterSubscription": ["{SUBSCRIPTION_ID}"],
162
- "from": "2026-03-01T00:00:00Z",
163
- "to": "2026-03-31T23:59:59Z",
164
- "windowSize": "DAY"
165
- }'
159
+ -d @- <<EOF
160
+ {
161
+ "filterSubscription": ["${SUBSCRIPTION_ID}"],
162
+ "from": "2026-03-01T00:00:00Z",
163
+ "to": "2026-12-31T23:59:59Z",
164
+ "windowSize": "DAY"
165
+ }
166
+ EOF
166
167
  ```
167
168
 
168
169
  ### Test with cURL
@@ -171,7 +172,7 @@ Verify the full flow manually:
171
172
 
172
173
  ```bash
173
174
  # Make a request to a monetized endpoint
174
- curl -v -H "Authorization: Bearer {CUSTOMER_API_KEY}" \
175
+ curl -v -H "Authorization: Bearer ${CUSTOMER_API_KEY}" \
175
176
  https://your-api.zuplo.dev/api/v1/resource
176
177
 
177
178
  # Check the response status and body for error details
@@ -195,30 +196,46 @@ products.
195
196
 
196
197
  ### What happens if Stripe is down?
197
198
 
198
- Zuplo caches subscription and quota state locally. If Stripe is temporarily
199
- unavailable, existing customers continue to have access based on their cached
200
- subscription state. New subscriptions and plan changes require Stripe to be
201
- available.
199
+ Zuplo's API request path doesn't go through Stripe. The runtime validates each
200
+ request against Zuplo's own subscription store, so existing customers keep
201
+ access while Stripe is unavailable. New paid subscriptions and billing-affecting
202
+ plan changes do need Stripe and have to wait; free plans skip Stripe Checkout
203
+ entirely and still work. If Stripe misses scheduled payments during the outage,
204
+ the [grace period](./monetization-policy.md#subscription-and-payment-validation)
205
+ (default 3 days) absorbs them once Stripe recovers and reports the failures.
202
206
 
203
207
  ### Can I use currencies other than USD?
204
208
 
205
209
  Yes. Plans support any ISO 4217 currency code. Set the `currency` field when
206
210
  creating a plan. You can offer the same plan in multiple currencies by creating
207
- separate plan objects (e.g., `pro-usd` and `pro-eur`).
211
+ separate plan objects (e.g., `pro_usd` and `pro_eur`).
208
212
 
209
213
  ### How are overages calculated?
210
214
 
211
- Overages are modeled using graduated tiered pricing. The first tier covers the
212
- included allowance at a flat price, and subsequent tiers charge per-unit for
213
- usage beyond the allowance. See [Pricing Models](./pricing-models.mdx) for
214
- details.
215
+ Overage uses graduated tiered pricing on a `usage_based` rate card. Every tier
216
+ has two price slots, `flatPrice` and `unitPrice` either or both can be set
217
+ (each may be 0), and a tier charges its flat price plus its per-unit price times
218
+ the units consumed inside the tier. All tier-based charges are billed in arrears
219
+ at the end of the billing cycle. The entitlement must set `isSoftLimit: true`,
220
+ otherwise the policy returns 403 at the quota line and there's no overage to
221
+ bill.
222
+
223
+ For example, tier 1 with `flatPrice: $499` covers the first 1M requests; tier 2
224
+ with `unitPrice: $0.0005` covers each request after. A customer who used 1.2M
225
+ requests invoices $499 + (200,000 × $0.0005) = $599 at the end of the period.
226
+
227
+ If you need a fixed fee billed at the **start** of the cycle instead, add a
228
+ separate billing-only rate card alongside the usage-based one (no `featureKey`,
229
+ `paymentTerm: "in_advance"`) — see
230
+ [Base fee in advance](./billing-models.md#example-base-fee-in-advance). For the
231
+ full rate card shape, see
232
+ [Included Usage with Overage](./pricing-models.mdx#included-usage-with-overage).
215
233
 
216
234
  ### Can I set spending limits for pay-as-you-go customers?
217
235
 
218
236
  You can set a hard entitlement limit that acts as a spending cap (e.g., max
219
237
  100,000 requests regardless of willingness to pay). Alternatively, use a custom
220
- policy to check current usage against a configurable limit, or set up Stripe
221
- billing alerts to notify customers approaching a threshold.
238
+ policy to check current usage against a configurable limit.
222
239
 
223
240
  ### Do meters count retried requests?
224
241
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zuplo",
3
- "version": "6.70.26",
3
+ "version": "6.70.28",
4
4
  "type": "module",
5
5
  "description": "The programmable API Gateway",
6
6
  "author": "Zuplo, Inc.",
@@ -19,9 +19,9 @@
19
19
  "zuplo": "zuplo.js"
20
20
  },
21
21
  "dependencies": {
22
- "@zuplo/cli": "6.70.26",
23
- "@zuplo/core": "6.70.26",
24
- "@zuplo/runtime": "6.70.26",
22
+ "@zuplo/cli": "6.70.28",
23
+ "@zuplo/core": "6.70.28",
24
+ "@zuplo/runtime": "6.70.28",
25
25
  "@zuplo/test": "1.4.0"
26
26
  }
27
27
  }