zuplo 6.68.15 → 6.68.17

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.
@@ -186,23 +186,53 @@ The response will look like this:
186
186
 
187
187
  ### Roll a Consumer's Keys
188
188
 
189
- Sometimes you will want to create a new key and expire the current keys. Instead
190
- of calling the API for each key and manually creating a new key, you can simply
191
- call the roll key endpoint.
189
+ Key rotation is a critical part of API key lifecycle management. Instant
190
+ revocation of a key can break every system that depends on it, so Zuplo supports
191
+ **rolling transitions** — creating a new key while keeping the old key active
192
+ for a defined grace period.
193
+
194
+ When you call the roll key endpoint, Zuplo:
195
+
196
+ 1. Creates a new key with no expiration for the consumer
197
+ 2. Sets the `expiresOn` date on all existing keys to the value you specify
198
+
199
+ This gives consumers time to update their integration to the new key before the
200
+ old one stops working.
201
+
202
+ #### Choosing a transition period
203
+
204
+ The right `expiresOn` value depends on how quickly consumers can update their
205
+ keys:
206
+
207
+ - **Leaked key response** — set `expiresOn` to a past date or within minutes.
208
+ Security incidents demand immediate action; a brief disruption is preferable
209
+ to continued exposure.
210
+ - **Routine rotation** — 24 to 72 hours gives most teams enough time to
211
+ propagate the new key through CI/CD pipelines, environment variables, and
212
+ secret managers.
213
+ - **Scheduled rotation** — for large organizations, 7 to 14 days accommodates
214
+ deploy freezes, multi-team coordination, and staged rollouts.
215
+
216
+ :::tip{title="Multiple keys per consumer"}
217
+
218
+ Consumers can also manage rotation themselves by creating a second key,
219
+ deploying it, verifying traffic, and then deleting the old key on their own
220
+ schedule. This pattern avoids any expiration deadline and puts the consumer in
221
+ full control.
222
+
223
+ :::
192
224
 
193
225
  :::tip{title="Tags for Request Authorization"}
194
226
 
195
- One useful feature of the API Key service is that most requests can have `tags`
196
- added to the query parameter even if they aren't get requests. This is useful
197
- when you want to call the API and ensure some basic condition is met without
198
- having to first do a GET to retrieve data on the object. For example, in the
199
- roll key request below the `orgId` tag is set on the request - this ensures that
200
- the consumer being updated is tagged with that org.
227
+ Most API requests support `tags` as query parameters, even on non-GET requests.
228
+ This lets you enforce conditions without a separate lookup. In the example
229
+ below, the `orgId` tag ensures the consumer being updated belongs to that
230
+ organization.
201
231
 
202
232
  :::
203
233
 
204
- The following call with set all existing keys to have the expiration date set in
205
- the request body and will create a new key without an expiration.
234
+ The following call sets all existing keys to expire on the specified date and
235
+ creates a new key without an expiration.
206
236
 
207
237
  ```shell
208
238
  export ORG_ID=1234
@@ -210,8 +240,9 @@ export CONSUMER_NAME=my-consumer
210
240
  curl \
211
241
  https://dev.zuplo.com/v1/accounts/$ACCOUNT_NAME/key-buckets/$BUCKET_NAME/consumers/$CONSUMER_NAME/roll-key?tag.orgId=$ORG_ID \
212
242
  --request POST \
213
- --header "Authorization: Bearer $API_KEY"
214
- --data '{"expiresOn":"2023-04-18"}'
243
+ --header "Authorization: Bearer $API_KEY" \
244
+ --header "Content-Type: application/json" \
245
+ --data '{"expiresOn":"2026-04-19T00:00:00.000Z"}'
215
246
  ```
216
247
 
217
248
  ## Reference
@@ -3,29 +3,39 @@ title: API Key Leak Detection
3
3
  sidebar_label: Leak Detection
4
4
  ---
5
5
 
6
- ## API Key Format
6
+ ## API key format enables leak detection
7
7
 
8
- Zuplo uses a specially formatted API Key structure that allows us to
9
- [partner with GitHub's secret scanning](https://github.blog/changelog/2022-07-13-zuplo-is-now-a-github-secret-scanning-partner/)
10
- to protect your users from accidentally leaked keys.
8
+ Zuplo API keys use a structured format `zpka_<random>_<checksum>` that is
9
+ specifically designed to support automated leak detection. The `zpka_` prefix
10
+ allows scanning tools to identify Zuplo keys using a simple regex pattern, and
11
+ the checksum suffix allows the scanner to verify that a matched string is a real
12
+ Zuplo key (not a false positive) without making a database call.
11
13
 
12
- We think the safety of your API key consumers is paramount, so this feature is
13
- available to all Zuplo customers, including free.
14
+ This format is what makes Zuplo a
15
+ [GitHub secret scanning partner](https://github.blog/changelog/2022-07-13-zuplo-is-now-a-github-secret-scanning-partner/).
16
+ Leak detection is available to all Zuplo customers, including free.
14
17
 
15
- ## API Key Leak Detection
18
+ ## How leak detection works
16
19
 
17
20
  API keys should never be stored in source control. Accidentally committing API
18
- keys to source control is a common attack vector that leads to compromises of
19
- organizations both large and small.
21
+ keys is a common attack vector that leads to compromises of organizations both
22
+ large and small.
20
23
 
21
24
  Zuplo participates in
22
25
  [GitHub's Secret Scanning](https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning)
23
- program to detect if your or your customer's API Keys are checked into source
24
- control on GitHub.
25
-
26
- If an API Key for your Zuplo API Gateway is compromised by checking it into a
27
- public or private GitHub repository, Zuplo will be notified and can take action
28
- immediately.
26
+ program. When a Zuplo API key is committed to any GitHub repository public or
27
+ private the following flow executes:
28
+
29
+ 1. **Detection** GitHub's scanners match the `zpka_` prefix pattern across all
30
+ repository content, including code, config files, and commit history.
31
+ 2. **Checksum verification** — GitHub verifies the key's checksum signature to
32
+ confirm it is a structurally valid Zuplo key, filtering out false positives.
33
+ 3. **Notification to Zuplo** — GitHub sends the matched key to Zuplo's secret
34
+ scanning endpoint.
35
+ 4. **Database lookup** — Zuplo checks the key against its key service to
36
+ determine if it is an active key and which consumer it belongs to.
37
+ 5. **Customer notification** — Zuplo sends leak alerts via email and in-app
38
+ notification, including the repository URL where the key was found.
29
39
 
30
40
  ## Leak Notifications
31
41
 
@@ -40,22 +50,17 @@ send. If you need the full API Key please contact support.
40
50
 
41
51
  :::
42
52
 
43
- ## Recommended Actions
44
-
45
- If you receive an alert that an API Key has been leaked, we recommend taking one
46
- of the following actions immediately.
47
-
48
- ### Notify Your Customer
53
+ ## Recommended actions
49
54
 
50
- Notify your customer and ask them to login to your Zuplo powered developer
51
- portal and instruct them to roll the API Key. This way the old key is revoked
52
- and they get a new key.
55
+ If you receive an alert that an API key has been leaked, take one of the
56
+ following actions immediately.
53
57
 
54
- ### Roll the API Key
58
+ ### Roll the API key
55
59
 
56
- You can use the
57
- [Zuplo API to roll the API Key](https://dev.zuplo.com/docs/routes#roll-consumer-keys)
58
- for the consumer. This will create a new key and revoke the old key.
60
+ The fastest way to respond is to roll the consumer's key. Rolling creates a new
61
+ key and sets an expiration on all existing keys for that consumer. You can set
62
+ the `expiresOn` value to give the consumer a short transition period to update
63
+ their integration, or omit it to revoke the old key immediately.
59
64
 
60
65
  ```bash
61
66
  export ACCOUNT_NAME="your-account-name"
@@ -69,7 +74,26 @@ curl --request POST \
69
74
  --header 'Content-Type: application/json' \
70
75
  --data '
71
76
  {
72
- "expiresOn": "2024-01-01T00:00:00.000Z"
77
+ "expiresOn": "2026-04-17T00:00:00.000Z"
73
78
  }
74
79
  '
75
80
  ```
81
+
82
+ :::caution
83
+
84
+ For leaked keys, keep the transition period as short as possible. A leaked key
85
+ remains valid until the expiration date you set. If the leak represents an
86
+ active threat, revoke the key immediately by setting `expiresOn` to a past date
87
+ or deleting the key directly.
88
+
89
+ :::
90
+
91
+ For more on key rotation patterns and choosing transition periods, see
92
+ [Roll a Consumer's Keys](./api-key-api.mdx#roll-a-consumers-keys).
93
+
94
+ ### Notify your customer
95
+
96
+ If the leaked key belongs to an end-user who manages their own keys through your
97
+ developer portal, notify them and instruct them to roll the key themselves. The
98
+ developer portal provides a self-serve roll key flow so consumers can generate a
99
+ new key and revoke the old one without contacting your team.
@@ -3,14 +3,10 @@ title: API Keys Overview
3
3
  sidebar_label: Overview
4
4
  ---
5
5
 
6
- Zuplo allows developers to rapidly add API key based authentication to an API in
7
- minutes. There are several benefits to using Zuplo's API Key solution including
8
-
9
- - adheres to
10
- [best practices of API Key implementation](https://zuplo.com/blog/2022/12/01/api-key-authentication)
11
- - includes [API Key Leak Detection & Notification](./api-key-leak-detection.mdx)
12
- - offers [out of the box and customizable solutions](./api-key-end-users.mdx)
13
- for sharing API Keys
6
+ Zuplo provides a fully managed API key authentication system that you can add to
7
+ your API in minutes. Every key is validated at the edge across 300+ data
8
+ centers, so authentication is fast for your consumers and offloads work from
9
+ your backend.
14
10
 
15
11
  :::tip
16
12
 
@@ -19,30 +15,88 @@ To start using Zuplo API Keys in only a few minutes
19
15
 
20
16
  :::
21
17
 
22
- ## Fully Managed API Key Solution
23
-
24
- Zuplo builds and manages a global API Key solution that can handle millions (or
25
- billions) of API Keys and a virtually unlimited throughput to scale to the most
26
- demanding services.
27
-
28
- The service handles global replication of API Keys allowing your end users to be
29
- authenticated to your API key with minimal latency. Keys are replicated around
30
- the world in only a few seconds. Similarly, when keys are revoked or deleted,
31
- the change replicates in seconds so that your API isn't open to unauthorized
32
- access.
33
-
34
- ## API Key Authentication at the Edge
18
+ ## Why API keys?
19
+
20
+ API keys are the standard authentication method for API-first companies like
21
+ Stripe, Twilio, and SendGrid. They are the right choice when you need to
22
+ authenticate an organization, system, or service rather than an individual user
23
+ acting on their own behalf (which is where OAuth excels).
24
+
25
+ Compared to JWT-based auth, API keys offer simpler developer experience a
26
+ single string in a header, easy to test with curl, and no token refresh flow.
27
+ API keys are opaque, meaning they don't leak claim structure the way a decoded
28
+ JWT does. And because each key maps to a consumer identity in Zuplo, you can
29
+ revoke access instantly — you don't need to wait for a token to expire.
30
+
31
+ For a deeper comparison, see the Zuplo blog post on
32
+ [API key authentication best practices](https://zuplo.com/blog/api-key-authentication).
33
+
34
+ ## What you get with Zuplo API keys
35
+
36
+ - **Thoughtful key format** — keys use a `zpka_` prefix, cryptographically
37
+ random body, and checksum signature. The prefix enables
38
+ [GitHub secret scanning](./api-key-leak-detection.mdx), the checksum allows
39
+ instant format validation without a database call, and the underscore
40
+ formatting means a double-click selects the entire key. See
41
+ [API key format](../concepts/api-keys.md#api-key-format) for the full
42
+ breakdown.
43
+ - **Leak detection** — Zuplo is a
44
+ [GitHub secret scanning partner](./api-key-leak-detection.mdx). If a key is
45
+ committed to any GitHub repository, you are notified immediately.
46
+ - **Self-serve key management** — give your API consumers a
47
+ [developer portal](./api-key-end-users.mdx) where they can create, view, roll,
48
+ and revoke their own keys.
49
+ - **Edge validation** — keys are validated at the edge, keeping latency low and
50
+ your backend protected. See
51
+ [how validation works](../concepts/api-keys.md#how-validation-works) for
52
+ details on caching and replication.
53
+ - **Key rotation with transition periods** — the
54
+ [roll-key API](./api-key-api.mdx#roll-a-consumers-keys) creates a new key and
55
+ sets an expiration on existing keys, so consumers have time to migrate without
56
+ downtime.
57
+
58
+ ## Fully managed global infrastructure
59
+
60
+ Zuplo builds and manages the API key infrastructure so you don't have to. The
61
+ service handles key storage, global replication, edge caching, and validation at
62
+ scale — supporting millions of keys and virtually unlimited throughput.
63
+
64
+ Keys replicate around the world in seconds. When a key is created, revoked, or
65
+ deleted, the change propagates to all 300+ edge locations within seconds,
66
+ ensuring your API is never open to unauthorized access for longer than the
67
+ configured cache TTL.
68
+
69
+ ## How authentication works
70
+
71
+ When a request arrives at Zuplo with an API key, the
72
+ [API Key Authentication policy](../policies/api-key-inbound.mdx) validates it
73
+ through a multi-step process:
74
+
75
+ 1. **Format check** — the key is checked for the correct `zpka_` prefix and
76
+ structure. Malformed keys are rejected immediately without any network call.
77
+ 2. **Checksum validation** — the key's built-in checksum signature is verified.
78
+ This catches typos and garbage keys in microseconds.
79
+ 3. **Cache lookup** — the edge checks its local cache for this key. If the key
80
+ was recently validated (or recently rejected), the cached result is used.
81
+ 4. **Key service lookup** — if the key isn't cached, Zuplo's globally
82
+ distributed key service is queried. The result is then cached for the
83
+ configured TTL (default 60 seconds).
84
+
85
+ After successful validation, `request.user` is populated with the consumer's
86
+ identity and metadata, which downstream policies and handlers can use for
87
+ authorization, rate limiting, and routing. See
88
+ [how validation works](../concepts/api-keys.md#how-validation-works) for more
89
+ details.
90
+
91
+ :::note
92
+
93
+ The `cacheTtlSeconds` option on the API Key Authentication policy controls how
94
+ long validation results are cached at each edge location. Higher values reduce
95
+ latency but delay the effect of key revocation. A revoked key could still be
96
+ accepted for up to `cacheTtlSeconds` after revocation. The default of 60 seconds
97
+ is a good balance for most use cases.
35
98
 
36
- Using Zuplo's API Key Authentication policy, your API is secured from
37
- unauthorized access. Authorization checks happen at the edge in 300+ data
38
- centers around the world. This keeps load off your backend and keeps your API
39
- fast for your end-users.
40
-
41
- Zuplo manages all the complexity of replication, caching, and verifying your API
42
- keys so you don't have to.
43
-
44
- Adding API Key authentication using Zuplo takes only a few minutes.
45
- [See the quickstart to get started](../articles/step-3-add-api-key-auth.mdx).
99
+ :::
46
100
 
47
101
  ## Key Concepts
48
102
 
@@ -92,9 +146,9 @@ API Keys are the actual string value used to authenticate with an API. Unlike
92
146
  some other forms of bearer tokens, API Keys don't contain any actual data within
93
147
  the key itself.
94
148
 
95
- Zuplo API Keys are prefixed with the string `zpka_` followed by
96
- cryptographically random characters and a signature. While Zuplo's API Key
97
- management service supports custom key formats (enterprise plan required), the
98
- structured format our the key enables us to offer
99
- [key leak detection services](./api-key-leak-detection.mdx) to keep your API
100
- secure.
149
+ Zuplo API Keys use a structured format: a `zpka_` prefix, a cryptographically
150
+ random body, and a checksum signature. This format enables
151
+ [leak detection via GitHub secret scanning](./api-key-leak-detection.mdx) and
152
+ allows instant format validation without a database call. Zuplo's API Key
153
+ management service also supports custom key formats (enterprise plan required),
154
+ though custom formats lose leak detection support.
@@ -19,9 +19,9 @@ private API. The benefits of a secure tunnel are:
19
19
 
20
20
  ## How does the Tunnel Work?
21
21
 
22
- The Zuplo tunnel can run on virtually any infrastructure. The most common way
23
- users install the tunnel is as a Docker container, but we can provide you a
24
- build in virtually any format (for example an Azure VM, etc.). The tunnel itself
22
+ The Zuplo tunnel can run on virtually any Linux-based infrastructure. The most
23
+ common way users install the tunnel is as a Docker container, but it can also
24
+ run directly on a Linux virtual machine or bare-metal server. The tunnel itself
25
25
  is a lightweight service that when started makes an outbound connection to the
26
26
  Zuplo network and then through to your Zuplo Gateway.
27
27
 
@@ -4,6 +4,18 @@ title: Tunnel Setup & Use
4
4
 
5
5
  <EnterpriseFeature name="Secure tunneling" />
6
6
 
7
+ :::caution{title="Platform Requirement"}
8
+
9
+ The Zuplo tunnel service is officially supported on **Linux** only. The
10
+ [`zuplo/tunnel` Docker image](https://hub.docker.com/r/zuplo/tunnel) is a Linux
11
+ container, and any non-containerized deployment must target a Linux host. Cloud
12
+ container platforms (AWS ECS, Azure Container Instances, GCP Cloud Run,
13
+ Kubernetes) run Linux containers by default and require no extra configuration.
14
+ If you are deploying on a virtual machine or bare metal, ensure the host runs a
15
+ supported Linux distribution.
16
+
17
+ :::
18
+
7
19
  ## Setting up Tunnels
8
20
 
9
21
  A tunnel is a way to expose your _internal services_ to the Zuplo gateway
@@ -7,6 +7,9 @@ work together. When setting up your tunnel it's common for traffic to initially
7
7
  not reach your destination initially. This is almost always caused by
8
8
  configurations (firewalls, VPCs, IAM rules, etc.) in your internal network.
9
9
 
10
+ As a quick sanity check, verify that the tunnel is running on a
11
+ [supported Linux host](./secure-tunnel.mdx) before investigating further.
12
+
10
13
  ## Tunnel status
11
14
 
12
15
  As a first step, check if the tunnel is up and running. You can use the
@@ -69,18 +69,78 @@ authorization and routing. Keep it small.
69
69
  **Tags** are key-value pairs used only for management (querying, filtering,
70
70
  organizing consumers via the API). Tags are not sent to the runtime.
71
71
 
72
- ## API key format and leak detection
72
+ ## When to use API keys
73
73
 
74
- Zuplo API keys use a structured format prefixed with `zpka_` followed by
75
- cryptographically random characters and a signature. This format enables
76
- automatic [leak detection](../articles/api-key-leak-detection.mdx): if one of
77
- your keys appears in a public GitHub repository, Zuplo sends an alert with the
78
- token and the URL where it was found.
74
+ API keys are the right authentication method when you need to identify an
75
+ organization, system, or service calling your API. Companies like Stripe,
76
+ Twilio, and SendGrid use API keys because they offer a simple developer
77
+ experience a single string in a header, easy to test with curl, and no token
78
+ refresh flow.
79
79
 
80
- Leak detection is enabled automatically for all keys using the standard format.
80
+ Use API keys when:
81
81
 
82
- See [API Key Leak Detection](../articles/api-key-leak-detection.mdx) for
83
- details.
82
+ - Your API consumers are developers integrating server-to-server
83
+ - You want simple, low-friction authentication (no OAuth dance)
84
+ - You need to identify and rate-limit individual consumers
85
+ - You want instant revocation capability (unlike JWTs, which are valid until
86
+ expiry)
87
+
88
+ Use OAuth or JWT when:
89
+
90
+ - You need to authenticate on behalf of an individual end-user
91
+ - Your use case requires delegated authorization with scoped permissions
92
+ - You are building a user-facing login flow
93
+
94
+ API keys and JWT/OAuth are not mutually exclusive. Many APIs use API keys for
95
+ system-level access and OAuth for user-level actions.
96
+
97
+ ## API key format
98
+
99
+ Zuplo API keys use a structured three-part format:
100
+
101
+ ```
102
+ zpka_<random>_<checksum>
103
+ ```
104
+
105
+ Each part serves a specific purpose:
106
+
107
+ - **`zpka_` prefix** — identifies the string as a Zuplo API key. This enables
108
+ automated [leak detection](../articles/api-key-leak-detection.mdx) via GitHub
109
+ secret scanning (scanners match the prefix pattern), helps support teams
110
+ identify key types during debugging, and distinguishes Zuplo keys from other
111
+ credentials in logs and config files.
112
+ - **Random body** — a cryptographically random string that serves as the actual
113
+ credential. This portion is generated using a secure random source and
114
+ provides the entropy that makes each key unique.
115
+ - **Checksum signature** — a suffix that allows instant format validation. When
116
+ a request arrives, Zuplo can verify the checksum mathematically in
117
+ microseconds to confirm the key is structurally valid before making any
118
+ network call. This rejects typos, truncated keys, and garbage strings without
119
+ touching the database.
120
+
121
+ The underscore separators are also intentional — they ensure that a double-click
122
+ on the key in most text editors and terminals selects the entire string,
123
+ reducing the chance of accidentally copying a partial key.
124
+
125
+ ### Leak detection
126
+
127
+ This key format is what makes Zuplo an official
128
+ [GitHub secret scanning partner](https://github.blog/changelog/2022-07-13-zuplo-is-now-a-github-secret-scanning-partner/).
129
+ If a Zuplo API key is committed to any GitHub repository, GitHub detects it,
130
+ verifies the checksum, and notifies Zuplo. You receive an alert with the token
131
+ and the repository URL where it was found. Leak detection is enabled
132
+ automatically for all keys using the standard format and is available to all
133
+ customers, including free.
134
+
135
+ See [API Key Leak Detection](../articles/api-key-leak-detection.mdx) for the
136
+ full scan flow and recommended response actions.
137
+
138
+ :::note
139
+
140
+ Enterprise customers can use custom key formats, but custom formats do not
141
+ support leak detection.
142
+
143
+ :::
84
144
 
85
145
  ## Self-serve key management
86
146
 
@@ -0,0 +1,190 @@
1
+ ---
2
+ title: Custom API Identity Plugin
3
+ sidebar_label: Custom API Identities
4
+ navigation_icon: fingerprint
5
+ description:
6
+ Learn how to create a custom API identity plugin for the Dev Portal to
7
+ authenticate API playground requests with OAuth JWT tokens or other custom
8
+ credentials.
9
+ ---
10
+
11
+ The Dev Portal API playground allows users to make authenticated requests to
12
+ your API. By default, users can enter credentials (such as API keys) directly in
13
+ the playground's Authorize dialog. However, when your API uses OAuth or OpenID
14
+ Connect for authentication, you need a custom API identity plugin to
15
+ automatically attach the user's access token to playground requests.
16
+
17
+ :::tip
18
+
19
+ If you use Zuplo's built-in API key management, you do not need to create a
20
+ custom identity plugin. API keys work automatically in the playground without
21
+ any additional configuration.
22
+
23
+ :::
24
+
25
+ ## When to use a custom identity plugin
26
+
27
+ Use a custom API identity plugin when:
28
+
29
+ - Your API requires OAuth 2.0 or OpenID Connect bearer tokens for
30
+ authentication.
31
+ - You want to automatically attach the signed-in user's access token to
32
+ playground requests.
33
+ - You need to apply custom authentication logic, such as using a specific token
34
+ format or adding extra headers.
35
+
36
+ ## Prerequisites
37
+
38
+ Before creating an identity plugin, configure an
39
+ [authentication provider](./zudoku/configuration/authentication.md) for the Dev
40
+ Portal. The authentication provider handles user sign-in and token management.
41
+ The identity plugin then bridges the user's session to the API playground.
42
+
43
+ For example, using OpenID Connect:
44
+
45
+ ```ts title="zudoku.config.ts"
46
+ const config = {
47
+ authentication: {
48
+ type: "openid",
49
+ clientId: "<your-client-id>",
50
+ issuer: "https://your-idp.example.com",
51
+ },
52
+ };
53
+ ```
54
+
55
+ ## Creating an identity plugin
56
+
57
+ Use `createApiIdentityPlugin` from `zudoku/plugins` to define how the playground
58
+ authenticates API requests. The plugin provides a `getIdentities` function that
59
+ returns one or more identities, each with an `authorizeRequest` function that
60
+ modifies outgoing requests.
61
+
62
+ ### Using `signRequest`
63
+
64
+ The simplest approach uses `context.authentication.signRequest()`, which
65
+ automatically attaches the user's access token to the request:
66
+
67
+ ```ts title="zudoku.config.ts"
68
+ import { createApiIdentityPlugin } from "zudoku/plugins";
69
+
70
+ const config = {
71
+ authentication: {
72
+ type: "openid",
73
+ clientId: "<your-client-id>",
74
+ issuer: "https://your-idp.example.com",
75
+ },
76
+ plugins: [
77
+ createApiIdentityPlugin({
78
+ getIdentities: async (context) => [
79
+ {
80
+ id: "oauth-token",
81
+ label: "OAuth Token",
82
+ authorizeRequest: (request) => {
83
+ return context.authentication?.signRequest(request);
84
+ },
85
+ },
86
+ ],
87
+ }),
88
+ ],
89
+ };
90
+ ```
91
+
92
+ ### Using `getAccessToken`
93
+
94
+ If you need more control over how the token is applied, use
95
+ `context.authentication.getAccessToken()` to retrieve the token directly. This
96
+ is useful when you need to set a specific header format or add the token to a
97
+ query parameter:
98
+
99
+ ```ts title="zudoku.config.ts"
100
+ import { createApiIdentityPlugin } from "zudoku/plugins";
101
+
102
+ const config = {
103
+ authentication: {
104
+ type: "openid",
105
+ clientId: "<your-client-id>",
106
+ issuer: "https://your-idp.example.com",
107
+ },
108
+ plugins: [
109
+ createApiIdentityPlugin({
110
+ getIdentities: async (context) => [
111
+ {
112
+ id: "jwt-bearer",
113
+ label: "JWT Bearer Token",
114
+ authorizeRequest: async (request) => {
115
+ const token = await context.authentication?.getAccessToken();
116
+
117
+ if (!token) {
118
+ throw new Error(
119
+ "No access token available. Please sign in again.",
120
+ );
121
+ }
122
+
123
+ request.headers.set("Authorization", `Bearer ${token}`);
124
+ return request;
125
+ },
126
+ },
127
+ ],
128
+ }),
129
+ ],
130
+ };
131
+ ```
132
+
133
+ ## How it works
134
+
135
+ When a user signs in to the Dev Portal and makes a request from the API
136
+ playground:
137
+
138
+ 1. The Dev Portal displays the configured identities as selectable options in
139
+ the playground.
140
+ 2. When the user selects an identity and sends a request, the `authorizeRequest`
141
+ function runs before the request is sent.
142
+ 3. The function modifies the request (typically by adding an `Authorization`
143
+ header) and returns it.
144
+ 4. The playground sends the modified request to your API.
145
+
146
+ ## The `ApiIdentity` interface
147
+
148
+ Each identity returned by `getIdentities` has the following properties:
149
+
150
+ | Property | Type | Description |
151
+ | ------------------ | --------------------------------------------------- | ------------------------------------------------------------------ |
152
+ | `id` | `string` | A unique identifier for the identity. |
153
+ | `label` | `string` | A human-readable name displayed in the playground identity picker. |
154
+ | `authorizeRequest` | `(request: Request) => Request \| Promise<Request>` | A function that adds authentication credentials to the request. |
155
+
156
+ ## Multiple identities
157
+
158
+ You can return multiple identities from `getIdentities` to give users a choice
159
+ of authentication methods:
160
+
161
+ ```ts title="zudoku.config.ts"
162
+ createApiIdentityPlugin({
163
+ getIdentities: async (context) => [
164
+ {
165
+ id: "oauth-token",
166
+ label: "OAuth Token",
167
+ authorizeRequest: (request) => {
168
+ return context.authentication?.signRequest(request);
169
+ },
170
+ },
171
+ {
172
+ id: "custom-header",
173
+ label: "Custom API Header",
174
+ authorizeRequest: (request) => {
175
+ request.headers.set("X-Custom-Auth", "my-value");
176
+ return request;
177
+ },
178
+ },
179
+ ],
180
+ });
181
+ ```
182
+
183
+ ## Related pages
184
+
185
+ - [Authentication](./zudoku/configuration/authentication.md) - Configure a
186
+ sign-in provider for the Dev Portal.
187
+ - [OAuth Security Schemes](./zudoku/configuration/oauth-security-schemes.md) -
188
+ Learn how OAuth security schemes work with the Dev Portal.
189
+ - [Custom Plugins](./zudoku/custom-plugins.md) - Build other types of custom
190
+ plugins for the Dev Portal.
@@ -59,6 +59,85 @@ import { MonetizationInboundPolicy } from "@zuplo/runtime";
59
59
  const meters = MonetizationInboundPolicy.getMeters(context);
60
60
  ```
61
61
 
62
+ ## Subscription data (for customization)
63
+
64
+ The monetization policy validates the API key and attaches the **subscription**
65
+ to the request context. You can read it later in your pipeline/handler to
66
+ customize behavior (limits, feature flags, plan-based behavior, etc.).
67
+
68
+ ### Read subscription data
69
+
70
+ ```typescript
71
+ import { MonetizationInboundPolicy } from "@zuplo/runtime";
72
+
73
+ const subscription = MonetizationInboundPolicy.getSubscriptionData(context);
74
+
75
+ if (!subscription) {
76
+ // The monetization policy may not have run yet, or the request was rejected earlier.
77
+ // Decide what behavior you want in this case.
78
+ }
79
+ ```
80
+
81
+ ### Common fields you’ll likely use
82
+
83
+ - **Identity**: `subscription.id`, `subscription.customerId`,
84
+ `subscription.name`
85
+ - **Plan**: `subscription.plan.key`, `subscription.plan.version`
86
+ - **Status & dates**: `subscription.status`, `subscription.activeFrom`,
87
+ `subscription.activeTo`, `subscription.nextBillingDate`
88
+ - **Entitlements**: `subscription.entitlements[meterName]` →
89
+ `{ balance, usage, overage, hasAccess }`
90
+ - **Payment** (when present): `subscription.paymentStatus.status`,
91
+ `subscription.paymentStatus.isFirstPayment`
92
+
93
+ ### Example: gate a feature by plan
94
+
95
+ ```typescript
96
+ import { MonetizationInboundPolicy } from "@zuplo/runtime";
97
+
98
+ const subscription = MonetizationInboundPolicy.getSubscriptionData(context);
99
+ if (subscription?.plan.key !== "enterprise") {
100
+ return new Response("This feature requires the Enterprise plan.", {
101
+ status: 403,
102
+ });
103
+ }
104
+ ```
105
+
106
+ ### Example: check entitlement access or remaining balance
107
+
108
+ ```typescript
109
+ import { MonetizationInboundPolicy } from "@zuplo/runtime";
110
+
111
+ const subscription = MonetizationInboundPolicy.getSubscriptionData(context);
112
+ const entitlement = subscription?.entitlements?.["requests"];
113
+
114
+ if (!entitlement?.hasAccess) {
115
+ return new Response("Your subscription does not include this meter.", {
116
+ status: 403,
117
+ });
118
+ }
119
+
120
+ // A simple “remaining” calculation you can use for UX or soft limits.
121
+ const remaining = entitlement.balance - entitlement.usage;
122
+ if (remaining <= 0) {
123
+ return new Response("Quota exceeded.", { status: 429 });
124
+ }
125
+ ```
126
+
127
+ ### Example: customize response headers
128
+
129
+ ```typescript
130
+ import { MonetizationInboundPolicy } from "@zuplo/runtime";
131
+
132
+ const subscription = MonetizationInboundPolicy.getSubscriptionData(context);
133
+
134
+ const nextResponse = new Response(response.body, response);
135
+ if (subscription) {
136
+ nextResponse.headers.set("x-zuplo-plan", subscription.plan.key);
137
+ }
138
+ return nextResponse;
139
+ ```
140
+
62
141
  ## Meter merge behavior
63
142
 
64
143
  - The final hook merges `options.meters` and request meter increments from
@@ -1,6 +1,6 @@
1
1
  The Monetization policy allows you to track and monetize the usage of your API
2
2
  resources, declaratively and programmatically.
3
3
 
4
- Follow our
5
- [Early Access: Getting Started with Monetization Guide](https://github.com/zuplo-samples/monetization-preview)
6
- to get started.
4
+ Follow our official documentation
5
+ [API Monetization with Zuplo](https://zuplo.com/docs/articles/monetization) to
6
+ get started.
@@ -3,10 +3,15 @@ title: ZoneCache
3
3
  sidebar_label: ZoneCache
4
4
  ---
5
5
 
6
- The ZoneCache stores data in a shared cache. This cache is in the same zone (the
7
- same data center or cluster) as your API - it's not globally distributed, but
8
- rather designed to be low-latency. Common uses include caching configuration,
9
- session, or other data that needs to be frequently accessed with low latency.
6
+ The ZoneCache stores data in a shared cache that is local to a single zone (data
7
+ center or cluster). Each zone maintains its own independent cache data written
8
+ in one zone is not synchronized or replicated to other zones. If the same data
9
+ is needed in multiple zones, it must be written to each zone's cache
10
+ independently.
11
+
12
+ This makes ZoneCache ideal for caching configuration, reference data, or
13
+ expensive API responses to reduce latency. It is not suitable for transactional,
14
+ OLTP-style workloads where consistency across locations matters.
10
15
 
11
16
  The ZoneCache can store any JSON serializable data. Each cached item has a
12
17
  time-to-live (TTL) after which it expires and is removed from the cache. Each
@@ -112,15 +117,22 @@ promise rejections.
112
117
 
113
118
  ## When to use ZoneCache
114
119
 
115
- Use ZoneCache when you need low-latency access to frequently used data that's
116
- stored in a single zone. It's ideal for caching configuration data, session
117
- information, or results from external API calls that are expensive or slow to
118
- retrieve.
120
+ ZoneCache works well for:
121
+
122
+ - **Configuration and reference data** API keys, feature flags, routing rules,
123
+ or other config that changes infrequently
124
+ - **Expensive API responses** — results from slow or rate-limited upstream
125
+ services
126
+ - **Computed data** — pre-processed results that are costly to regenerate
127
+
128
+ :::caution{title="Not a distributed data store"}
119
129
 
120
- Avoid using ZoneCache for large datasets or data that needs to be globally
121
- distributed, as it's designed for small, simple objects within a single zone. It
122
- is also not suitable to be used as an OLTP to read/write important data, it's
123
- just a cache - very useful but not a database or key/value store.
130
+ ZoneCache is a per-zone cache with **no cross-zone synchronization**. A `put()`
131
+ in one data center has no effect on the cache in any other data center. Do not
132
+ use it as a database, key/value store, or for any OLTP-style read/write
133
+ workload. It is not appropriate for data where global consistency matters.
134
+
135
+ :::
124
136
 
125
137
  ## Limits
126
138
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zuplo",
3
- "version": "6.68.15",
3
+ "version": "6.68.17",
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.68.15",
23
- "@zuplo/core": "6.68.15",
24
- "@zuplo/runtime": "6.68.15",
22
+ "@zuplo/cli": "6.68.17",
23
+ "@zuplo/core": "6.68.17",
24
+ "@zuplo/runtime": "6.68.17",
25
25
  "@zuplo/test": "1.4.0"
26
26
  }
27
27
  }