zuplo 6.69.6 → 6.69.8
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.
- package/docs/articles/monetization/api-access.mdx +130 -0
- package/docs/articles/monetization/features.mdx +39 -8
- package/docs/articles/monetization/index.mdx +25 -24
- package/docs/articles/monetization/meters.mdx +19 -6
- package/docs/articles/monetization/monetization-policy.md +35 -20
- package/docs/articles/monetization/plans.mdx +25 -8
- package/docs/articles/monetization/private-plans.md +9 -0
- package/docs/articles/monetization/quickstart.md +15 -9
- package/docs/articles/monetization/stripe-integration.md +95 -50
- package/docs/articles/monetization/subscription-lifecycle.md +38 -18
- package/docs/articles/monetization/troubleshooting.md +5 -2
- package/docs/dev-portal/zudoku/configuration/authentication-auth0.md +22 -0
- package/docs/dev-portal/zudoku/configuration/authentication-azure-ad.md +18 -0
- package/docs/dev-portal/zudoku/configuration/authentication-clerk.md +17 -0
- package/docs/dev-portal/zudoku/configuration/authentication-firebase.md +7 -0
- package/docs/dev-portal/zudoku/configuration/authentication-openid.md +27 -0
- package/docs/dev-portal/zudoku/configuration/authentication-supabase.md +35 -0
- package/docs/dev-portal/zudoku/configuration/docs.md +16 -0
- package/docs/dev-portal/zudoku/extending/hooks.md +243 -0
- package/docs/dev-portal/zudoku/markdown/frontmatter.md +19 -0
- package/docs/policies/upstream-gcp-service-auth-inbound/schema.json +6 -0
- package/package.json +4 -4
|
@@ -60,6 +60,136 @@ curl \
|
|
|
60
60
|
--header "Authorization: Bearer $ZAPI_KEY"
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
+
## Bucket monetization configuration
|
|
64
|
+
|
|
65
|
+
Each bucket has an optional `MonetizationConfiguration` record that holds
|
|
66
|
+
bucket-wide defaults. The configuration is read by the runtime and the Developer
|
|
67
|
+
Portal — it is not stored in OpenMeter.
|
|
68
|
+
|
|
69
|
+
| Method | Path |
|
|
70
|
+
| -------- | ---------------------------------------------------- |
|
|
71
|
+
| `GET` | `/v3/metering/{bucketId}/monetization-configuration` |
|
|
72
|
+
| `PUT` | `/v3/metering/{bucketId}/monetization-configuration` |
|
|
73
|
+
| `DELETE` | `/v3/metering/{bucketId}/monetization-configuration` |
|
|
74
|
+
|
|
75
|
+
The `PUT` endpoint upserts the record. At least one of the four fields below
|
|
76
|
+
must be present. Pass any combination — fields that are omitted retain their
|
|
77
|
+
previous value.
|
|
78
|
+
|
|
79
|
+
| Field | Type | Description |
|
|
80
|
+
| ------------------------------ | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
81
|
+
| `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. |
|
|
82
|
+
| `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. |
|
|
83
|
+
| `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. |
|
|
84
|
+
| `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). |
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
curl -X PUT "https://dev.zuplo.com/v3/metering/$BUCKET_ID/monetization-configuration" \
|
|
88
|
+
--header "Authorization: Bearer $ZAPI_KEY" \
|
|
89
|
+
--header "Content-Type: application/json" \
|
|
90
|
+
--data '{
|
|
91
|
+
"planOrder": ["free", "developer", "pro"],
|
|
92
|
+
"planSettings": {
|
|
93
|
+
"pro": { "visiblePhases": ["default"] }
|
|
94
|
+
},
|
|
95
|
+
"maxPaymentOverdueDays": 7
|
|
96
|
+
}'
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`DELETE` removes the record entirely; `GET` on a bucket with no record returns
|
|
100
|
+
the schema defaults (`multipleSubscriptionsEnabled: false`, `planOrder: []`,
|
|
101
|
+
`planSettings: {}`, `maxPaymentOverdueDays: 3`).
|
|
102
|
+
|
|
103
|
+
## Stripe setup and billing readiness
|
|
104
|
+
|
|
105
|
+
These endpoints script the Stripe integration that the
|
|
106
|
+
[Monetization Service UI](./stripe-integration.md#connecting-your-stripe-account)
|
|
107
|
+
runs interactively. They live on the Zuplo developer API (not OpenMeter), so the
|
|
108
|
+
request shape is documented here.
|
|
109
|
+
|
|
110
|
+
### Connect a Stripe app
|
|
111
|
+
|
|
112
|
+
```http
|
|
113
|
+
POST /v3/metering/{bucketId}/setup/stripe
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Installs a Stripe app on the bucket and creates the default billing profile
|
|
117
|
+
linked to that app.
|
|
118
|
+
|
|
119
|
+
| Field | Type | Description |
|
|
120
|
+
| ------------- | --------- | ------------------------------------------------------------------------------------------------ |
|
|
121
|
+
| `apiKey` | `string` | Stripe secret or restricted key. Required. |
|
|
122
|
+
| `name` | `string` | Display name for the app. Required. |
|
|
123
|
+
| `taxEnabled` | `boolean` | Initial value for `workflow.tax.enabled` on the billing profile. Optional; defaults to `false`. |
|
|
124
|
+
| `taxEnforced` | `boolean` | Initial value for `workflow.tax.enforced` on the billing profile. Optional; defaults to `false`. |
|
|
125
|
+
| `country` | `string` | ISO 3166-1 alpha-2 supplier country for the billing profile. Optional; defaults to `"US"`. |
|
|
126
|
+
|
|
127
|
+
The request fails if the key prefix does not match the bucket environment:
|
|
128
|
+
|
|
129
|
+
- Working-copy or preview buckets accept `sk_test_*` or `rk_test_*`.
|
|
130
|
+
- Production buckets accept `sk_live_*` or `rk_live_*`.
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
curl -X POST "https://dev.zuplo.com/v3/metering/$BUCKET_ID/setup/stripe" \
|
|
134
|
+
--header "Authorization: Bearer $ZAPI_KEY" \
|
|
135
|
+
--header "Content-Type: application/json" \
|
|
136
|
+
--data '{
|
|
137
|
+
"apiKey": "rk_test_...",
|
|
138
|
+
"name": "Zuplo Monetization (test)",
|
|
139
|
+
"taxEnabled": false,
|
|
140
|
+
"country": "US"
|
|
141
|
+
}'
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### Read the connected Stripe app
|
|
145
|
+
|
|
146
|
+
```http
|
|
147
|
+
GET /v3/metering/{bucketId}/setup/stripe
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Returns a summary of the connected Stripe app, the matched billing profile, and
|
|
151
|
+
connection-test status. Use this to confirm the integration is wired up before
|
|
152
|
+
continuing.
|
|
153
|
+
|
|
154
|
+
### Add a billing profile to a Stripe app
|
|
155
|
+
|
|
156
|
+
```http
|
|
157
|
+
POST /v3/metering/{bucketId}/setup/stripe/{stripeAppId}/billing-profile
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Creates an additional billing profile against an already-installed Stripe app.
|
|
161
|
+
This is rarely needed — the default profile is created during initial setup. Use
|
|
162
|
+
this endpoint to create per-supplier-country profiles.
|
|
163
|
+
|
|
164
|
+
### Check billing readiness
|
|
165
|
+
|
|
166
|
+
```http
|
|
167
|
+
GET /v3/metering/{bucketId}/billing-readiness
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
|
|
172
|
+
```json
|
|
173
|
+
{
|
|
174
|
+
"hasStripeApp": true,
|
|
175
|
+
"stripeAppId": "app_01H...",
|
|
176
|
+
"hasDefaultBillingProfile": true,
|
|
177
|
+
"defaultBillingProfileId": "bp_01H..."
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Use this in setup wizards to gate the UI on whether Stripe is connected.
|
|
182
|
+
|
|
183
|
+
### Update an app
|
|
184
|
+
|
|
185
|
+
```http
|
|
186
|
+
PUT /v3/metering/{bucketId}/apps/{appId}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Replaces an app's configuration (name, description, metadata, and — for Stripe
|
|
190
|
+
apps — `secretAPIKey`). The same key-prefix validation as `POST /setup/stripe`
|
|
191
|
+
applies: a Stripe key must match the bucket environment.
|
|
192
|
+
|
|
63
193
|
## API Reference
|
|
64
194
|
|
|
65
195
|
For complete API operations, see the API Reference documentation:
|
|
@@ -44,7 +44,8 @@ feature structure carefully before creating them.
|
|
|
44
44
|
|
|
45
45
|
## Creating a Feature
|
|
46
46
|
|
|
47
|
-
Create a metered feature linked to
|
|
47
|
+
Create a metered feature linked to the `api_requests` meter. The feature's `key`
|
|
48
|
+
must match the meter's `slug`:
|
|
48
49
|
|
|
49
50
|
```shell
|
|
50
51
|
curl \
|
|
@@ -54,8 +55,8 @@ curl \
|
|
|
54
55
|
--header "Content-Type: application/json" \
|
|
55
56
|
--data @- << EOF
|
|
56
57
|
{
|
|
57
|
-
"key": "
|
|
58
|
-
"name": "API
|
|
58
|
+
"key": "api_requests",
|
|
59
|
+
"name": "API Requests",
|
|
59
60
|
"meterSlug": "api_requests"
|
|
60
61
|
}
|
|
61
62
|
EOF
|
|
@@ -66,17 +67,19 @@ The response includes the created feature:
|
|
|
66
67
|
```json
|
|
67
68
|
{
|
|
68
69
|
"id": "01ARZ3NDEKTSV4RRFFQ69G5FAV",
|
|
69
|
-
"key": "
|
|
70
|
-
"name": "API
|
|
70
|
+
"key": "api_requests",
|
|
71
|
+
"name": "API Requests",
|
|
71
72
|
"meterSlug": "api_requests",
|
|
72
|
-
"createdAt": "
|
|
73
|
-
"updatedAt": "
|
|
73
|
+
"createdAt": "2026-01-01T01:01:01.001Z",
|
|
74
|
+
"updatedAt": "2026-01-01T01:01:01.001Z"
|
|
74
75
|
}
|
|
75
76
|
```
|
|
76
77
|
|
|
77
78
|
### Creating a Static Feature
|
|
78
79
|
|
|
79
|
-
For features without usage tracking, omit the `meterSlug
|
|
80
|
+
For features without usage tracking, omit the `meterSlug`. A plan's rate card
|
|
81
|
+
attaches a `boolean` entitlement that determines whether the customer has access
|
|
82
|
+
to the capability:
|
|
80
83
|
|
|
81
84
|
```shell
|
|
82
85
|
curl \
|
|
@@ -92,6 +95,34 @@ curl \
|
|
|
92
95
|
EOF
|
|
93
96
|
```
|
|
94
97
|
|
|
98
|
+
### Creating a Billing-Only Feature
|
|
99
|
+
|
|
100
|
+
Some plans charge a flat subscription fee that isn't tied to any entitlement — a
|
|
101
|
+
monthly access fee, a setup fee, or a fixed retainer. Model these the same way
|
|
102
|
+
as a static feature (no `meterSlug`), then attach them to a plan via a
|
|
103
|
+
`flat_fee` rate card with no `entitlementTemplate` and a price whose
|
|
104
|
+
`paymentTerm` is `"in_advance"`. Zuplo issues a Stripe Invoice for the fee at
|
|
105
|
+
the start of each billing period.
|
|
106
|
+
|
|
107
|
+
```shell
|
|
108
|
+
curl \
|
|
109
|
+
https://dev.zuplo.com/v3/metering/$BUCKET_ID/features \
|
|
110
|
+
--request POST \
|
|
111
|
+
--header "Authorization: Bearer $ZAPI_KEY" \
|
|
112
|
+
--header "Content-Type: application/json" \
|
|
113
|
+
--data @- << EOF
|
|
114
|
+
{
|
|
115
|
+
"key": "monthly_fee",
|
|
116
|
+
"name": "Monthly Fee"
|
|
117
|
+
}
|
|
118
|
+
EOF
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Because the rate card carries no entitlement, the developer portal's
|
|
122
|
+
feature-comparison matrix doesn't render a row for the feature — the price rolls
|
|
123
|
+
into the plan's headline cost. See the Pro plan example in [Plans](./plans.mdx)
|
|
124
|
+
for the rate-card shape.
|
|
125
|
+
|
|
95
126
|
## API Reference
|
|
96
127
|
|
|
97
128
|
For complete API operations (list, get, archive), see the
|
|
@@ -28,8 +28,8 @@ and invoices come out wrong.
|
|
|
28
28
|
|
|
29
29
|
Zuplo eliminates this by making the gateway the single source of truth. Every
|
|
30
30
|
request that hits your API is metered, enforced, and billed through one system.
|
|
31
|
-
When a customer's quota runs out, they get a `
|
|
32
|
-
minutes later when a batch job catches up.
|
|
31
|
+
When a customer's quota runs out, they get a `403 Forbidden` immediately — not
|
|
32
|
+
five minutes later when a batch job catches up.
|
|
33
33
|
|
|
34
34
|
## Core concepts
|
|
35
35
|
|
|
@@ -73,10 +73,11 @@ with automatic conversion to paid, and multiple subscriptions per customer.
|
|
|
73
73
|
- **Built-in metering** — Count requests, tokens, bytes, or any custom
|
|
74
74
|
dimension. No external metering service required.
|
|
75
75
|
- **Real-time quota enforcement** — Customers hitting their limit get a
|
|
76
|
-
`
|
|
77
|
-
- **Stripe billing integration** —
|
|
78
|
-
|
|
79
|
-
|
|
76
|
+
`403 Forbidden` response instantly, not after a batch sync.
|
|
77
|
+
- **Stripe billing integration** — Stripe handles checkout, payment, the billing
|
|
78
|
+
portal, and tax. Zuplo issues Stripe Invoices for fixed fees and metered usage
|
|
79
|
+
at the end of each billing period, and the gateway revokes access
|
|
80
|
+
automatically when a payment goes overdue past the configured grace period.
|
|
80
81
|
- **Self-serve Developer Portal** — Pricing page, plan selection, checkout,
|
|
81
82
|
subscription management, usage dashboards, and API key management built into
|
|
82
83
|
the Zudoku-powered portal.
|
|
@@ -93,21 +94,21 @@ pricing, wire up Stripe, and configure your gateway to enforce quotas.
|
|
|
93
94
|
|
|
94
95
|
## Documentation map
|
|
95
96
|
|
|
96
|
-
| Document | What it covers
|
|
97
|
-
| ---------------------------------------------------------------------- |
|
|
98
|
-
| [Quickstart](./monetization/quickstart.md) | End-to-end setup in 30 minutes
|
|
99
|
-
| [Meters](./monetization/meters.mdx) | How meters track usage dimensions
|
|
100
|
-
| [Features](./monetization/features.mdx) | Connecting meters to your product catalog
|
|
101
|
-
| [Plans](./monetization/plans.mdx) | Plan structure, phases, lifecycle, and publishing
|
|
102
|
-
| [Rate Cards](./monetization/rate-cards.mdx) | Pricing and entitlements within plans
|
|
103
|
-
| [Pricing Models](./monetization/pricing-models.mdx) | Flat, per-unit, tiered, volume, and package pricing
|
|
104
|
-
| [Billing Models Guide](./monetization/billing-models.md) | Choosing the right pricing strategy for your business
|
|
105
|
-
| [Stripe Integration](./monetization/stripe-integration.md) | Connecting Stripe and managing payments
|
|
106
|
-
| [Developer Portal Setup](./monetization/developer-portal.md) | Configuring the self-serve portal for your customers
|
|
107
|
-
| [Monetization Policy Reference](./monetization/monetization-policy.md) | Configuring the `MonetizationInboundPolicy` per-route
|
|
108
|
-
| [API Access](./monetization/api-access.mdx) | Authentication, buckets, and
|
|
109
|
-
| [Subscription Lifecycle](./monetization/subscription-lifecycle.md) | Managing trials, upgrades, downgrades, and cancellations
|
|
110
|
-
| [Private Plans](./monetization/private-plans.md) | Invite-only plans for specific users
|
|
111
|
-
| [Tax Collection](./monetization/tax-collection.md) | Enabling VAT, sales tax, or GST via Stripe Tax
|
|
112
|
-
| [Plan Examples](./monetization/plan-examples.mdx) | Real-world plan configurations
|
|
113
|
-
| [Troubleshooting](./monetization/troubleshooting.md) | Common issues, debugging, and FAQ
|
|
97
|
+
| Document | What it covers |
|
|
98
|
+
| ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
|
|
99
|
+
| [Quickstart](./monetization/quickstart.md) | End-to-end setup in 30 minutes |
|
|
100
|
+
| [Meters](./monetization/meters.mdx) | How meters track usage dimensions |
|
|
101
|
+
| [Features](./monetization/features.mdx) | Connecting meters to your product catalog |
|
|
102
|
+
| [Plans](./monetization/plans.mdx) | Plan structure, phases, lifecycle, and publishing |
|
|
103
|
+
| [Rate Cards](./monetization/rate-cards.mdx) | Pricing and entitlements within plans |
|
|
104
|
+
| [Pricing Models](./monetization/pricing-models.mdx) | Flat, per-unit, tiered, volume, and package pricing |
|
|
105
|
+
| [Billing Models Guide](./monetization/billing-models.md) | Choosing the right pricing strategy for your business |
|
|
106
|
+
| [Stripe Integration](./monetization/stripe-integration.md) | Connecting Stripe and managing payments |
|
|
107
|
+
| [Developer Portal Setup](./monetization/developer-portal.md) | Configuring the self-serve portal for your customers |
|
|
108
|
+
| [Monetization Policy Reference](./monetization/monetization-policy.md) | Configuring the `MonetizationInboundPolicy` per-route |
|
|
109
|
+
| [API Access](./monetization/api-access.mdx) | Authentication, buckets, bucket configuration, Stripe setup APIs, and reference links |
|
|
110
|
+
| [Subscription Lifecycle](./monetization/subscription-lifecycle.md) | Managing trials, upgrades, downgrades, and cancellations |
|
|
111
|
+
| [Private Plans](./monetization/private-plans.md) | Invite-only plans for specific users |
|
|
112
|
+
| [Tax Collection](./monetization/tax-collection.md) | Enabling VAT, sales tax, or GST via Stripe Tax |
|
|
113
|
+
| [Plan Examples](./monetization/plan-examples.mdx) | Real-world plan configurations |
|
|
114
|
+
| [Troubleshooting](./monetization/troubleshooting.md) | Common issues, debugging, and FAQ |
|
|
@@ -25,7 +25,7 @@ A meter is configured with:
|
|
|
25
25
|
- **Aggregation** - How to combine values (typically `SUM`)
|
|
26
26
|
|
|
27
27
|
When events are ingested, the meter matches events by type, extracts the
|
|
28
|
-
specified value, and aggregates it per customer over time.
|
|
28
|
+
specified value, and aggregates it per customer subscription over time.
|
|
29
29
|
|
|
30
30
|
## Common Examples
|
|
31
31
|
|
|
@@ -52,7 +52,7 @@ Each event contains the `subscription` ID linking it to a subscription and a
|
|
|
52
52
|
"specversion": "1.0",
|
|
53
53
|
"type": "api_requests",
|
|
54
54
|
"source": "monetization-policy",
|
|
55
|
-
"subject": "
|
|
55
|
+
"subject": "01KNVXHQG356VA7T7W0V9N21GH",
|
|
56
56
|
"subscription": "01KNVXHQG356VA7T7W0V9N21GH",
|
|
57
57
|
"data": {
|
|
58
58
|
"total": 1
|
|
@@ -60,6 +60,14 @@ Each event contains the `subscription` ID linking it to a subscription and a
|
|
|
60
60
|
}
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
+
:::note
|
|
64
|
+
|
|
65
|
+
Events emitted by the `MonetizationInboundPolicy` always set `subject` and
|
|
66
|
+
`subscription` to the same subscription ULID. See
|
|
67
|
+
[Monetization Policy](./monetization-policy.md) for how usage is recorded.
|
|
68
|
+
|
|
69
|
+
:::
|
|
70
|
+
|
|
63
71
|
### Token Usage
|
|
64
72
|
|
|
65
73
|
Track token consumption for AI applications:
|
|
@@ -74,8 +82,13 @@ Track token consumption for AI applications:
|
|
|
74
82
|
}
|
|
75
83
|
```
|
|
76
84
|
|
|
77
|
-
|
|
78
|
-
|
|
85
|
+
The meter aggregates events from the gateway; the per-request quantity comes
|
|
86
|
+
from the `MonetizationInboundPolicy`. Set a fixed cost per request in the
|
|
87
|
+
policy's `meters` option, or call
|
|
88
|
+
[`MonetizationInboundPolicy.setMeters`](./monetization-policy.md#dynamic-metering)
|
|
89
|
+
from a custom outbound policy to report a value derived from the response — for
|
|
90
|
+
example, the actual token count an LLM returned. An event for a request that
|
|
91
|
+
consumed 50 tokens looks like this:
|
|
79
92
|
|
|
80
93
|
```json
|
|
81
94
|
{
|
|
@@ -83,7 +96,7 @@ configured to report 50 tokens per request:
|
|
|
83
96
|
"specversion": "1.0",
|
|
84
97
|
"type": "tokens",
|
|
85
98
|
"source": "monetization-policy",
|
|
86
|
-
"subject": "
|
|
99
|
+
"subject": "01KNVXHQG356VA7T7W0V9N21GH",
|
|
87
100
|
"subscription": "01KNVXHQG356VA7T7W0V9N21GH",
|
|
88
101
|
"data": {
|
|
89
102
|
"total": 50
|
|
@@ -113,7 +126,7 @@ Each event reports the number of bytes transferred:
|
|
|
113
126
|
"specversion": "1.0",
|
|
114
127
|
"type": "data_transfer",
|
|
115
128
|
"source": "monetization-policy",
|
|
116
|
-
"subject": "
|
|
129
|
+
"subject": "01KNVXHQG356VA7T7W0V9N21GH",
|
|
117
130
|
"subscription": "01KNVXHQG356VA7T7W0V9N21GH",
|
|
118
131
|
"data": {
|
|
119
132
|
"total": 4096
|
|
@@ -53,8 +53,8 @@ Then reference it in your route's inbound policy pipeline:
|
|
|
53
53
|
|
|
54
54
|
The `MonetizationInboundPolicy` handles API key authentication internally. It
|
|
55
55
|
reads the API key from the `Authorization` header, validates it, and sets
|
|
56
|
-
`request.user`. You do not need a separate
|
|
57
|
-
routes — the monetization policy replaces it.
|
|
56
|
+
`request.user`. You do not need a separate API key authentication policy
|
|
57
|
+
(`api-key-inbound`) on monetized routes — the monetization policy replaces it.
|
|
58
58
|
|
|
59
59
|
:::
|
|
60
60
|
|
|
@@ -62,7 +62,7 @@ routes — the monetization policy replaces it.
|
|
|
62
62
|
|
|
63
63
|
| Option | Type | Default | Description |
|
|
64
64
|
| -------------------- | ------------------ | ----------------- | ------------------------------------------------- |
|
|
65
|
-
| `meters` | object | (
|
|
65
|
+
| `meters` | object | _(none)_ | Map of meter keys to increment values |
|
|
66
66
|
| `meterOnStatusCodes` | string or number[] | `"200-299"` | Status code range to meter |
|
|
67
67
|
| `authHeader` | string | `"authorization"` | Header to read the API key from |
|
|
68
68
|
| `authScheme` | string | `"Bearer"` | Expected auth scheme prefix |
|
|
@@ -71,7 +71,13 @@ routes — the monetization policy replaces it.
|
|
|
71
71
|
### `meters`
|
|
72
72
|
|
|
73
73
|
The `meters` option defines which meters to increment and by how much when a
|
|
74
|
-
request is processed. Values must be
|
|
74
|
+
request is processed. Values must be non-negative numbers.
|
|
75
|
+
|
|
76
|
+
If `meters` is omitted, the policy still authenticates the API key and validates
|
|
77
|
+
the subscription's payment status, but no usage is recorded. If `meters` is
|
|
78
|
+
provided, it must contain at least one entry — an empty object throws a
|
|
79
|
+
configuration error. To track usage at runtime instead of from static config,
|
|
80
|
+
see [Dynamic metering](#dynamic-metering).
|
|
75
81
|
|
|
76
82
|
```json
|
|
77
83
|
// Increment the api_requests meter by 1 per request
|
|
@@ -143,16 +149,22 @@ When payment fails, a configurable grace period (default 3 days) allows
|
|
|
143
149
|
continued access while retries are attempted. After the grace period, access is
|
|
144
150
|
blocked until payment succeeds.
|
|
145
151
|
|
|
146
|
-
The grace period
|
|
152
|
+
The grace period resolves in this order, with each level overriding the one
|
|
153
|
+
below it:
|
|
154
|
+
|
|
155
|
+
1. **Customer metadata** — `zuplo_max_payment_overdue_days` on the customer
|
|
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
|
|
147
161
|
|
|
148
|
-
|
|
149
|
-
- Customer metadata key: `zuplo_max_payment_overdue_days`
|
|
150
|
-
- Default: `3` (days)
|
|
162
|
+
Set the value to `0` to block requests immediately when payment is overdue.
|
|
151
163
|
|
|
152
164
|
## Multiple policies for different routes
|
|
153
165
|
|
|
154
166
|
Different routes can have different metering configurations. Define multiple
|
|
155
|
-
policy instances
|
|
167
|
+
policy instances in `policies.json`:
|
|
156
168
|
|
|
157
169
|
```json
|
|
158
170
|
[
|
|
@@ -284,17 +296,20 @@ the RFC 7807 Problem Details format:
|
|
|
284
296
|
|
|
285
297
|
Common error details:
|
|
286
298
|
|
|
287
|
-
| Condition
|
|
288
|
-
|
|
|
289
|
-
| No auth header
|
|
290
|
-
| Wrong auth scheme
|
|
291
|
-
|
|
|
292
|
-
|
|
|
293
|
-
|
|
|
294
|
-
|
|
|
295
|
-
|
|
|
296
|
-
|
|
|
297
|
-
|
|
|
299
|
+
| Condition | `detail` message |
|
|
300
|
+
| ------------------------------- | ------------------------------------------------------------------- |
|
|
301
|
+
| No auth header | `"No Authorization Header"` |
|
|
302
|
+
| Wrong auth scheme | `"Invalid Authorization Scheme"` |
|
|
303
|
+
| Empty key after the auth scheme | `"No key present"` |
|
|
304
|
+
| Cached invalid key or 401 | `"Authorization Failed"` |
|
|
305
|
+
| Invalid API key | `"API Key is invalid or does not have access to the API"` |
|
|
306
|
+
| Expired API key | `"API Key has expired."` |
|
|
307
|
+
| Expired subscription | `"API Key has an expired subscription."` |
|
|
308
|
+
| Subscription has no payment | `"Subscription payment status is not available."` |
|
|
309
|
+
| Payment not made | `"Payment has not been made."` |
|
|
310
|
+
| Payment overdue | `"Payment is overdue. Please update your payment method."` |
|
|
311
|
+
| Quota exhausted | `"API Key has exceeded the allowed limit for \"X\" meter."` |
|
|
312
|
+
| Meter not in subscription | `"API Key does not have \"X\" meter provided by the subscription."` |
|
|
298
313
|
|
|
299
314
|
## Pipeline ordering
|
|
300
315
|
|
|
@@ -73,9 +73,11 @@ Plans move through a defined lifecycle:
|
|
|
73
73
|
This example creates a Pro plan with:
|
|
74
74
|
|
|
75
75
|
- A 1-week free trial phase with 1,000 API calls
|
|
76
|
-
- A
|
|
77
|
-
|
|
78
|
-
-
|
|
76
|
+
- A `monthly_fee` flat-fee rate card on the default phase that charges $99 in
|
|
77
|
+
advance at the start of each billing period
|
|
78
|
+
- A `usage_based` rate card with 10,000 included API requests per billing period
|
|
79
|
+
and $0.01 per request overage in arrears
|
|
80
|
+
- A `priority_support` static feature granted on both phases
|
|
79
81
|
|
|
80
82
|
For step-by-step examples building plans from simple to complex, see
|
|
81
83
|
[Plan Examples](./plan-examples.mdx).
|
|
@@ -132,6 +134,18 @@ curl \
|
|
|
132
134
|
"name": "Default",
|
|
133
135
|
"duration": null,
|
|
134
136
|
"rateCards": [
|
|
137
|
+
{
|
|
138
|
+
"type": "flat_fee",
|
|
139
|
+
"key": "monthly_fee",
|
|
140
|
+
"name": "Monthly Fee",
|
|
141
|
+
"featureKey": "monthly_fee",
|
|
142
|
+
"billingCadence": "P1M",
|
|
143
|
+
"price": {
|
|
144
|
+
"type": "flat",
|
|
145
|
+
"amount": "99.00",
|
|
146
|
+
"paymentTerm": "in_advance"
|
|
147
|
+
}
|
|
148
|
+
},
|
|
135
149
|
{
|
|
136
150
|
"type": "usage_based",
|
|
137
151
|
"key": "api_requests",
|
|
@@ -150,8 +164,8 @@ curl \
|
|
|
150
164
|
"tiers": [
|
|
151
165
|
{
|
|
152
166
|
"upToAmount": "10000",
|
|
153
|
-
"flatPrice":
|
|
154
|
-
"unitPrice":
|
|
167
|
+
"flatPrice": null,
|
|
168
|
+
"unitPrice": { "type": "unit", "amount": "0.00" }
|
|
155
169
|
},
|
|
156
170
|
{
|
|
157
171
|
"flatPrice": null,
|
|
@@ -186,9 +200,12 @@ EOF
|
|
|
186
200
|
| Trial | 1 week | 1,000 (hard limit) | Free |
|
|
187
201
|
| Default | Ongoing | 10,000 included | $99/month |
|
|
188
202
|
|
|
189
|
-
After the trial ends, customers automatically move to the default phase.
|
|
190
|
-
|
|
191
|
-
|
|
203
|
+
After the trial ends, customers automatically move to the default phase. The $99
|
|
204
|
+
monthly subscription fee is charged in advance at the start of each billing
|
|
205
|
+
period via the `monthly_fee` flat-fee rate card. The plan includes 10,000 API
|
|
206
|
+
requests per period; additional requests are billed at $0.01 each in arrears
|
|
207
|
+
(the soft limit on the metered entitlement allows overage to flow through to the
|
|
208
|
+
next invoice).
|
|
192
209
|
|
|
193
210
|
## Plan Properties
|
|
194
211
|
|
|
@@ -107,6 +107,15 @@ curl -X POST "https://dev.zuplo.com/v3/metering/${ZUPLO_BUCKET_ID}/plans" \
|
|
|
107
107
|
|
|
108
108
|
Save the returned `id` — you need it to publish and invite users.
|
|
109
109
|
|
|
110
|
+
:::note
|
|
111
|
+
|
|
112
|
+
The plan `id` is a 26-character ULID (regex
|
|
113
|
+
`^[0-7][0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{25}$`), separate from the human-friendly
|
|
114
|
+
`key` you set on creation. The publish, invite, and other plan-scoped endpoints
|
|
115
|
+
require the `id`, not the `key`.
|
|
116
|
+
|
|
117
|
+
:::
|
|
118
|
+
|
|
110
119
|
The key difference from a public plan is `metadata.zuplo_private_plan` set to
|
|
111
120
|
`"true"`. Everything else (rate cards, entitlements, pricing) works the same as
|
|
112
121
|
public plans.
|
|
@@ -284,22 +284,27 @@ directory.
|
|
|
284
284
|
3. In the **Meters** configuration field, set the key to `api_requests` with a
|
|
285
285
|
value of `1` to match the meter you created in _Step 4_. This field maps the
|
|
286
286
|
meter slug to the number of units each request consumes.
|
|
287
|
+
4. Click on **Create Policy**.
|
|
288
|
+
|
|
289
|
+
The Portal saves the policy to `policies.json` as a complete entry:
|
|
287
290
|
|
|
288
291
|
```json
|
|
289
292
|
{
|
|
290
|
-
"
|
|
291
|
-
"
|
|
292
|
-
"
|
|
293
|
-
"
|
|
294
|
-
"
|
|
295
|
-
|
|
293
|
+
"name": "monetization-inbound",
|
|
294
|
+
"policyType": "monetization-inbound",
|
|
295
|
+
"handler": {
|
|
296
|
+
"export": "MonetizationInboundPolicy",
|
|
297
|
+
"module": "$import(@zuplo/runtime)",
|
|
298
|
+
"options": {
|
|
299
|
+
"cacheTtlSeconds": 60,
|
|
300
|
+
"meters": {
|
|
301
|
+
"api_requests": 1
|
|
302
|
+
}
|
|
296
303
|
}
|
|
297
304
|
}
|
|
298
305
|
}
|
|
299
306
|
```
|
|
300
307
|
|
|
301
|
-
4. Click on **Create Policy**.
|
|
302
|
-
|
|
303
308
|
### Apply the policy to routes
|
|
304
309
|
|
|
305
310
|
Next, you need to apply the Monetization policy to some or all of your routes.
|
|
@@ -317,7 +322,8 @@ Next, you need to apply the Monetization policy to some or all of your routes.
|
|
|
317
322
|
:::note
|
|
318
323
|
|
|
319
324
|
The `MonetizationInboundPolicy` handles API key authentication internally. You
|
|
320
|
-
do not need a separate `api-key-
|
|
325
|
+
do not need a separate API key authentication policy (`api-key-inbound`) on
|
|
326
|
+
monetized routes.
|
|
321
327
|
|
|
322
328
|
:::
|
|
323
329
|
|