zuplo 7.8.7 → 7.8.9

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.
Files changed (73) hide show
  1. package/docs/ai-gateway/custom-policies.mdx +2 -1
  2. package/docs/ai-gateway/integrations/claude-code.mdx +82 -180
  3. package/docs/ai-gateway/integrations/claude-desktop.mdx +61 -99
  4. package/docs/ai-gateway/integrations/codex.mdx +63 -57
  5. package/docs/ai-gateway/usage-limits.mdx +2 -2
  6. package/docs/api-management/overview.md +2 -2
  7. package/docs/articles/add-api-to-backstage.mdx +4 -3
  8. package/docs/articles/api-key-consumer-bucket-portal-ui.mdx +1 -1
  9. package/docs/articles/api-key-management.mdx +1 -1
  10. package/docs/articles/custom-domains.mdx +3 -2
  11. package/docs/articles/migrate-from-apigee.md +3 -3
  12. package/docs/articles/migrate-from-aws-api-gateway.md +3 -3
  13. package/docs/articles/migrate-from-azure-apim.md +3 -3
  14. package/docs/articles/migrate-from-kong.md +3 -3
  15. package/docs/articles/source-control-setup-github.mdx +2 -2
  16. package/docs/articles/source-control.mdx +4 -3
  17. package/docs/articles/testing.mdx +5 -5
  18. package/docs/articles/upstream-iam-auth.mdx +2 -2
  19. package/docs/cli/create-zuplo-api.mdx +4 -4
  20. package/docs/cli/openapi-merge.mdx +1 -1
  21. package/docs/cli/openapi-merge.partial.mdx +1 -1
  22. package/docs/concepts/ai-gateway.mdx +1 -1
  23. package/docs/concepts/how-zuplo-works.mdx +2 -1
  24. package/docs/concepts/mcp.mdx +4 -4
  25. package/docs/dedicated/architecture.mdx +1 -1
  26. package/docs/dev-portal/migration.mdx +2 -2
  27. package/docs/errors/not-found.mdx +1 -1
  28. package/docs/getting-started/ai-gateway/portal.mdx +181 -0
  29. package/docs/getting-started/dev-portal/agent.mdx +148 -0
  30. package/docs/getting-started/dev-portal/index.mdx +26 -0
  31. package/docs/getting-started/dev-portal/local.mdx +213 -0
  32. package/docs/getting-started/dev-portal/portal.mdx +146 -0
  33. package/docs/{articles/step-3-add-api-key-auth.mdx → getting-started/gateway/add-api-key-auth/index.mdx} +32 -23
  34. package/docs/{articles/step-3-add-api-key-auth-local.mdx → getting-started/gateway/add-api-key-auth/local.mdx} +28 -20
  35. package/docs/{articles/step-2-add-rate-limiting.mdx → getting-started/gateway/add-rate-limiting/index.mdx} +22 -13
  36. package/docs/{articles/step-2-add-rate-limiting-local.mdx → getting-started/gateway/add-rate-limiting/local.mdx} +24 -14
  37. package/docs/getting-started/gateway/agent.mdx +199 -0
  38. package/docs/{articles/step-4-deploying-to-the-edge.mdx → getting-started/gateway/deploy-to-the-edge/index.mdx} +39 -27
  39. package/docs/{articles/step-4-deploying-to-the-edge-local.mdx → getting-started/gateway/deploy-to-the-edge/local.mdx} +48 -17
  40. package/docs/{articles/step-5-dynamic-rate-limiting.mdx → getting-started/gateway/dynamic-rate-limiting/index.mdx} +24 -12
  41. package/docs/{articles/step-5-dynamic-rate-limiting-local.mdx → getting-started/gateway/dynamic-rate-limiting/local.mdx} +24 -12
  42. package/docs/getting-started/gateway/index.mdx +24 -0
  43. package/docs/{articles/step-1-setup-basic-gateway.mdx → getting-started/gateway/setup-a-basic-gateway/index.mdx} +33 -21
  44. package/docs/{articles/step-1-setup-basic-gateway-local.mdx → getting-started/gateway/setup-a-basic-gateway/local.mdx} +24 -15
  45. package/docs/getting-started/index.mdx +25 -0
  46. package/docs/getting-started/mcp-gateway/agent.mdx +161 -0
  47. package/docs/getting-started/mcp-gateway/index.mdx +25 -0
  48. package/docs/{mcp-gateway/quickstart-local.mdx → getting-started/mcp-gateway/local.mdx} +64 -38
  49. package/docs/{mcp-gateway/quickstart.mdx → getting-started/mcp-gateway/portal.mdx} +71 -54
  50. package/docs/getting-started/mcp-server/agent.mdx +152 -0
  51. package/docs/getting-started/mcp-server/index.mdx +25 -0
  52. package/docs/{articles/mcp-quickstart-local.mdx → getting-started/mcp-server/local.mdx} +24 -13
  53. package/docs/{articles/mcp-quickstart.mdx → getting-started/mcp-server/portal.mdx} +35 -15
  54. package/docs/getting-started/migrate/agent.mdx +129 -0
  55. package/docs/getting-started/migrate/index.mdx +25 -0
  56. package/docs/getting-started/migrate/local.mdx +141 -0
  57. package/docs/getting-started/migrate/portal.mdx +129 -0
  58. package/docs/home.mdx +6 -0
  59. package/docs/mcp-gateway/connect-clients/chatgpt.mdx +2 -1
  60. package/docs/mcp-gateway/connect-clients/claude-code.mdx +2 -1
  61. package/docs/mcp-gateway/connect-clients/claude-desktop.mdx +23 -20
  62. package/docs/mcp-gateway/connect-clients/cursor.mdx +2 -1
  63. package/docs/mcp-gateway/connect-clients/overview.mdx +2 -1
  64. package/docs/mcp-gateway/connect-clients/vs-code.mdx +2 -1
  65. package/docs/mcp-gateway/how-it-works.mdx +2 -2
  66. package/docs/mcp-gateway/how-to/connect-upstream-api-key.mdx +4 -3
  67. package/docs/mcp-gateway/how-to/curate-tools.mdx +3 -2
  68. package/docs/mcp-gateway/overview.mdx +3 -3
  69. package/docs/policies/ai-gateway-smart-router-inbound/doc.md +14 -0
  70. package/docs/policies/akamai-ai-firewall-v2-inbound/doc.md +7 -0
  71. package/package.json +5 -5
  72. package/docs/ai-gateway/getting-started.mdx +0 -307
  73. package/docs/dev-portal/local-development.mdx +0 -72
@@ -0,0 +1,181 @@
1
+ ---
2
+ category: AI Gateway
3
+ showLastModified: false
4
+ suggestEdit: false
5
+ sidebar_label: Portal
6
+ title: Getting Started with AI Gateway - Portal
7
+ description:
8
+ Create an AI Gateway in the Portal, try the free demo model, and send an
9
+ authenticated chat request with cURL or the OpenAI SDK.
10
+ ---
11
+
12
+ <WizardSteps build="ai-gateway" method="portal" />
13
+
14
+ Create an AI Gateway, send a chat request through it, and connect an OpenAI SDK
15
+ client. This tutorial uses the free Zuplo Demo provider, so you don't need a
16
+ provider API key.
17
+
18
+ ## Prerequisites
19
+
20
+ - A [Zuplo account](https://portal.zuplo.com).
21
+ - A terminal with cURL to test the gateway from outside the Portal.
22
+
23
+ ## Create and test your AI Gateway
24
+
25
+ <Stepper>
26
+
27
+ 1. **Create an AI Gateway project**
28
+
29
+ Open [**Projects**](https://portal.zuplo.com/+/account/projects), click **New
30
+ Project**, and select **AI Gateway**. Name the project `my-ai-gateway` and
31
+ click **Create Project**.
32
+
33
+ <ModalScreenshot>
34
+
35
+ ![New project dialog with AI Gateway selected](/media/ai-gateway-getting-started/create-project.png)
36
+
37
+ </ModalScreenshot>
38
+
39
+ When setup finishes, the project opens with **Create your first app**. The
40
+ gateway is already deployed.
41
+
42
+ 2. **Check the demo provider**
43
+
44
+ Open
45
+ [**Settings → AI Providers**](https://portal.zuplo.com/+/account/project/ai/settings/data-models).
46
+ The new project includes **Zuplo Demo**, which provides test models without a
47
+ provider API key. This tutorial uses `zuplodemo/pirate`.
48
+
49
+ <ModalScreenshot>
50
+
51
+ ![AI provider settings with the Zuplo Demo provider](/media/ai-gateway-getting-started/providers.png)
52
+
53
+ </ModalScreenshot>
54
+
55
+ The part before `/` names the provider; the part after `/` names the model.
56
+ Later, use **Add Provider** to configure your own provider and its models.
57
+ See [AI Providers](../../ai-gateway/providers.mdx).
58
+
59
+ 3. **Create an app**
60
+
61
+ Open [**Apps**](https://portal.zuplo.com/+/account/project/ai/apps) and click
62
+ **Create App**. Enter `Tutorial app` as the **App name**. Keep the
63
+ **Default** pool selected and click **Create app**.
64
+
65
+ <ModalScreenshot>
66
+
67
+ ![New app dialog with an app name and the Default pool](/media/ai-gateway-getting-started/create-app.png)
68
+
69
+ </ModalScreenshot>
70
+
71
+ An app represents the software that calls your gateway. Each app has its own
72
+ URL and API key. The first app also creates the Default pool, which groups
73
+ apps and supplies shared policy settings.
74
+
75
+ 4. **Review authentication**
76
+
77
+ Open the app's **Policies** tab. The new app inherits **API Key
78
+ Authentication** from its pool. Keep this policy in place: requests must send
79
+ the app's key in the `Authorization` header.
80
+
81
+ <ModalScreenshot>
82
+
83
+ ![App policy chain with inherited API Key Authentication](/media/ai-gateway-getting-started/policies.png)
84
+
85
+ </ModalScreenshot>
86
+
87
+ You can add policies for model filtering, budgets, and other controls here.
88
+ For this first request, keep the default chain. See
89
+ [Policy Chains](../../ai-gateway/policy-chains.mdx).
90
+
91
+ 5. **Send a request in the Playground**
92
+
93
+ Click **Playground** in the app header. Select `zuplodemo/pirate`, enter
94
+ `Say hello`, and send the message. A pirate-themed reply confirms that the
95
+ app can reach the provider through the gateway.
96
+
97
+ <ModalScreenshot>
98
+
99
+ ![AI Playground showing a successful response from the pirate demo model](/media/ai-gateway-getting-started/playground.png)
100
+
101
+ </ModalScreenshot>
102
+
103
+ 6. **Call the gateway from a terminal**
104
+
105
+ In the **Playground**, click **Copy cURL**. Paste the copied command into
106
+ your terminal and run it. The command includes the selected model, your
107
+ conversation, and the app's API key. The response streams into the terminal
108
+ as it arrives.
109
+
110
+ Treat the copied command as a credential because it contains your API key.
111
+
112
+ </Stepper>
113
+
114
+ ## Connect the OpenAI SDK
115
+
116
+ Copy **API URL** from the app header and the app's key from its **API Key** tab.
117
+ The URL includes the app ID after the hostname. Copy the complete URL without
118
+ adding `/v1` yet.
119
+
120
+ <ModalScreenshot>
121
+
122
+ ![App API Key tab with the key concealed](/media/ai-gateway-getting-started/api-key.png)
123
+
124
+ </ModalScreenshot>
125
+
126
+ Set these values in your terminal, replacing the placeholders:
127
+
128
+ ```bash
129
+ export ZUPLO_APP_URL="https://YOUR_GATEWAY.zuplo.app/YOUR_APP_ID"
130
+ export ZUPLO_APP_API_KEY="YOUR_APP_API_KEY"
131
+ ```
132
+
133
+ With Node.js 24 or later installed, create a directory for the sample and
134
+ install the SDK in the same terminal:
135
+
136
+ ```bash
137
+ mkdir ai-gateway-client
138
+ cd ai-gateway-client
139
+ npm init -y
140
+ npm install openai
141
+ ```
142
+
143
+ Save this as `sample.mjs` and run `node sample.mjs` from the terminal where you
144
+ set the environment variables:
145
+
146
+ ```javascript title="sample.mjs"
147
+ import OpenAI from "openai";
148
+
149
+ const client = new OpenAI({
150
+ baseURL: `${process.env.ZUPLO_APP_URL}/v1`,
151
+ apiKey: process.env.ZUPLO_APP_API_KEY,
152
+ });
153
+
154
+ const result = await client.chat.completions.create({
155
+ model: "zuplodemo/pirate",
156
+ messages: [{ role: "user", content: "Say hello" }],
157
+ });
158
+
159
+ console.log(result.choices[0].message.content);
160
+ ```
161
+
162
+ The OpenAI SDK uses the app URL with `/v1` appended. It sends the app key to
163
+ Zuplo; provider credentials stay in the gateway. See the
164
+ [OpenAI SDK integration](../../ai-gateway/integrations/openai.mdx) for more
165
+ examples.
166
+
167
+ ## Troubleshooting
168
+
169
+ | Result | What to check |
170
+ | ------------------------------ | --------------------------------------------------------------------------------------------------------- |
171
+ | `401 Unauthorized` | Use the app's API key, not a provider key. Include the `Bearer` prefix. |
172
+ | `404` | Copy the complete app URL, including its app ID, then append `/v1/chat/completions`. |
173
+ | Model not found or not allowed | Use `zuplodemo/pirate` and confirm Zuplo Demo is enabled. If you added Model Filtering, allow this model. |
174
+
175
+ ## Next steps
176
+
177
+ - [Add providers](../../ai-gateway/providers.mdx) to connect your own models.
178
+ - [Configure policy chains](../../ai-gateway/policy-chains.mdx) to restrict
179
+ models or add budgets and guardrails.
180
+ - [Connect source control](../../ai-gateway/source-control.mdx) to deploy code
181
+ changes from Git.
@@ -0,0 +1,148 @@
1
+ ---
2
+ category: Developer Portal
3
+ showLastModified: false
4
+ suggestEdit: false
5
+ sidebar_label: Agent
6
+ title: Getting Started with Developer Portal - Agent
7
+ description:
8
+ Build a developer portal with a coding agent, review its API key
9
+ configuration, and verify a signed-in request from the API playground.
10
+ ---
11
+
12
+ <WizardSteps build="dev-portal" method="agent" />
13
+
14
+ <MethodTabs build="dev-portal" method="agent" />
15
+
16
+ Have a coding agent build a developer portal with an introduction, interactive
17
+ API reference, and API key management. You'll assign a test key and verify the
18
+ portal yourself.
19
+
20
+ ## Prerequisites
21
+
22
+ - [Node.js](https://nodejs.org/en/download) 24 or later and Git.
23
+ - A [Zuplo account](https://portal.zuplo.com).
24
+ - An installed coding agent and an email address for the portal sign-in test.
25
+
26
+ ## Build your developer portal with an agent
27
+
28
+ <Stepper>
29
+
30
+ 1. **Create the project**
31
+
32
+ Run:
33
+
34
+ ```bash
35
+ npx create-zuplo-api@latest my-api-project
36
+ cd my-api-project
37
+ ```
38
+
39
+ When prompted, choose **Yes** to create a matching Portal project, complete
40
+ sign-in, and select your Zuplo account. Select the coding agent you use when
41
+ asked. The CLI creates and links the hosted project. See
42
+ [`create-zuplo-api`](../../cli/create-zuplo-api.mdx).
43
+
44
+ :::tip{title="Zuplo skills"}
45
+
46
+ The scaffold installs Zuplo skills in this project for Codex and Cursor and
47
+ configures the Claude Code plugin. If setup fails, follow
48
+ [Agent Skills](../../build-with-ai.mdx#agent-skills) from this directory
49
+ before starting your agent.
50
+
51
+ :::
52
+
53
+ 2. **Ask the agent to build the portal**
54
+
55
+ Open your agent in the project directory and give it this prompt:
56
+
57
+ ```text
58
+ Read the project instructions and bundled Zuplo docs. Customize the default
59
+ developer portal for a Todo API. Keep the generated project structure.
60
+
61
+ In docs/zudoku.config.tsx, set site.title and metadata.title to
62
+ "Todo Developer Portal" and metadata.description to
63
+ "Explore the Todo API and manage your API keys." Keep the API reference
64
+ sourced from ../config/routes.oas.json at /api. Keep demo authentication
65
+ and apiKeys.enabled for this tutorial.
66
+
67
+ Replace docs/pages/introduction.mdx with a short introduction telling users
68
+ to sign in, find their assigned key under their profile's API Keys menu,
69
+ and try Get all todos in API Reference. Keep it in navigation. Explain
70
+ that an administrator assigns a consumer to the user's key manager email;
71
+ do not implement automatic provisioning.
72
+
73
+ In config/policies.json add api-key-auth, policyType api-key-inbound,
74
+ using ApiKeyInboundPolicy from $import(@zuplo/runtime) with
75
+ allowUnauthenticatedRequests false. Put it first in the inbound chain for
76
+ GET /todos in config/routes.oas.json. Preserve the existing URL forward
77
+ handler. Set this route's corsPolicy to anything-goes for the browser demo.
78
+ Update info.description so it says GET /todos requires a bearer API key.
79
+ Do not claim the other sample routes are protected.
80
+
81
+ Ensure .env and .env.zuplo are ignored and do not read or print credentials.
82
+ Run npm run dev. Report the gateway and Docs Server URLs. Verify GET /todos
83
+ without a key returns 401. Do not deploy; show me the diff and explain how
84
+ to finish the signed-in browser test with a Dev consumer.
85
+ ```
86
+
87
+ 3. **Review and open the result**
88
+
89
+ Review `git diff`. Confirm the reference still points to
90
+ `../config/routes.oas.json`, API keys remain enabled, and authentication is
91
+ attached to `GET /todos`.
92
+
93
+ With `npm run dev` running, open the **Docs Server** URL printed in the
94
+ terminal, normally `http://localhost:9200`. Open `/introduction` to check
95
+ your new page, then open **API Reference** to check the Todo API.
96
+
97
+ 4. **Assign a test key**
98
+
99
+ In the Zuplo Portal, open your project and go to
100
+ [**Services → API Keys → Consumers**](https://portal.zuplo.com/+/account/project/services).
101
+ Select **Dev** and click **Create Consumer**. Enter `tutorial-consumer` for
102
+ **Subject**, your email under **Key managers**, and `{}` for **Metadata**.
103
+ Click **Save consumer**.
104
+
105
+ <ModalScreenshot>
106
+
107
+ ![Create a consumer and assign a key manager email](/media/api-key-consumer-bucket-portal-ui/create-consumer-modal.png)
108
+
109
+ </ModalScreenshot>
110
+
111
+ 5. **Verify the signed-in experience**
112
+
113
+ Sign in to the local developer portal with the key manager email. Open **API
114
+ Keys** from your profile menu and confirm the key appears. Then open **API
115
+ Reference → Get all todos**, click **Test**, expand **Authentication**,
116
+ select `tutorial-consumer`, and click **Send**. Expect `200 OK` and todos in
117
+ the response.
118
+
119
+ Test without credentials in a terminal:
120
+
121
+ ```bash
122
+ curl -i http://localhost:9000/todos
123
+ ```
124
+
125
+ Expect `401 Unauthorized`. If browser requests fail while cURL succeeds,
126
+ check the route's [CORS configuration](../../articles/cors.mdx). If the key
127
+ is missing, check the consumer's email and Dev bucket.
128
+
129
+ 6. **Publish after review**
130
+
131
+ Follow [Deploy to the edge](../gateway/deploy-to-the-edge/local.mdx), then
132
+ open **Deployment URLs → Dev Portal** in the Zuplo Portal. Create a consumer
133
+ in the **Prod** bucket to test the deployed portal.
134
+
135
+ Replace the demo authentication with your own
136
+ [identity provider](../../dev-portal/zudoku/configuration/authentication.md)
137
+ and restrict CORS to your portal's origin before inviting users.
138
+
139
+ </Stepper>
140
+
141
+ ## Next steps
142
+
143
+ - [Editor tutorial](./local.mdx) for the exact policy configuration and manual
144
+ walkthrough.
145
+ - [Create consumers on login](../../dev-portal/dev-portal-create-consumer-on-auth.mdx)
146
+ to automate key assignment with your own identity provider.
147
+ - [Customize navigation](../../dev-portal/zudoku/configuration/navigation.mdx)
148
+ and add pages for your API's users.
@@ -0,0 +1,26 @@
1
+ ---
2
+ category: Developer Portal
3
+ fullWidth: true
4
+ disablePager: true
5
+ showLastModified: false
6
+ suggestEdit: false
7
+ centered: false
8
+ toc: false
9
+ title: Getting Started with Developer Portal
10
+ description:
11
+ Publish interactive API docs and let consumers manage their API keys through a
12
+ developer portal built in the Portal, locally, or with an AI agent.
13
+ ---
14
+
15
+ <WizardSteps build="dev-portal" />
16
+
17
+ A developer portal gives your API's users a place to read documentation, sign
18
+ in, and test requests. This tutorial creates a portal with Zudoku, Zuplo's
19
+ open-source documentation framework, using a sample Todo API.
20
+
21
+ Choose a workflow to publish the reference docs and connect API key management.
22
+ You assign a key to a consumer (the person or application calling your API),
23
+ then sign in to test it. You need an email address you can access for the
24
+ sign-in step.
25
+
26
+ <MethodPicker build="dev-portal" />
@@ -0,0 +1,213 @@
1
+ ---
2
+ category: Developer Portal
3
+ showLastModified: false
4
+ suggestEdit: false
5
+ sidebar_label: Editor
6
+ title: Getting Started with Developer Portal - Editor
7
+ description:
8
+ Build a developer portal locally with API documentation, an introduction,
9
+ sign-in, and API key management, then test a protected route.
10
+ ---
11
+
12
+ <WizardSteps build="dev-portal" method="local" />
13
+
14
+ <MethodTabs build="dev-portal" method="local" />
15
+
16
+ Build a developer portal with API reference documentation, a custom
17
+ introduction, and API key management. You'll run it locally, sign in as an API
18
+ consumer, and make an authenticated request from the API playground.
19
+
20
+ ## Prerequisites
21
+
22
+ - [Node.js](https://nodejs.org/en/download) 24 or later and Git.
23
+ - A [Zuplo account](https://portal.zuplo.com).
24
+ - An editor and an email address you can use to sign in to the developer portal.
25
+
26
+ ## Create and customize the portal
27
+
28
+ <Stepper>
29
+
30
+ 1. **Create a project**
31
+
32
+ Run:
33
+
34
+ ```bash
35
+ npx create-zuplo-api@latest my-api-project
36
+ cd my-api-project
37
+ npm run dev
38
+ ```
39
+
40
+ When prompted, choose **Yes** to create a matching Portal project, complete
41
+ sign-in, and select your Zuplo account. The CLI creates a hosted project and
42
+ links your local copy to it. Keep `.env.zuplo` out of source control.
43
+
44
+ The default template includes a Todo API and a developer portal. Open the
45
+ **Docs Server** URL printed in the terminal, normally
46
+ [http://localhost:9200](http://localhost:9200). You should see the Todo API
47
+ reference. The gateway runs on port `9000` and the Route Designer on `9100`.
48
+
49
+ :::tip{title="Use an existing project"}
50
+
51
+ If your project already has a `docs` directory, clone its Git repository, run
52
+ `npm install` at the project root, then run `npx zuplo login` and
53
+ `npx zuplo link`, selecting the project's **working copy**. This walkthrough
54
+ uses the Todo template; for your own API, apply the policy and tests to an
55
+ equivalent read-only route. Start all three services with `npm run dev` from
56
+ the project root. For an older project without `docs`, first follow
57
+ [Enable the Developer Portal](../../dev-portal/migration.mdx).
58
+
59
+ :::
60
+
61
+ 2. **Set your portal's title and introduction**
62
+
63
+ Open `docs/zudoku.config.tsx`. Set `site.title` and `metadata.title` to
64
+ `Todo Developer Portal`, and set `metadata.description` to
65
+ `Explore the Todo API and manage your API keys.` Keep the other settings.
66
+
67
+ The `apis` entry already reads `../config/routes.oas.json` and publishes it
68
+ at `/api`. Your API documentation updates when the OpenAPI file changes.
69
+
70
+ Replace `docs/pages/introduction.mdx` with:
71
+
72
+ ```mdx title="docs/pages/introduction.mdx"
73
+ ---
74
+ title: Welcome to the Todo API
75
+ description: Sign in, find your API key, and make your first request.
76
+ ---
77
+
78
+ Use the Todo API to retrieve tasks for your application.
79
+
80
+ 1. Sign in using the email address registered as a key manager.
81
+ 2. Open **API Keys** from your profile menu to find your assigned key.
82
+ 3. Open **API Reference** and try **Get all todos** with that key.
83
+ ```
84
+
85
+ The default navigation already includes `introduction`. Open
86
+ `http://localhost:9200/introduction` to see the page. Changes appear as you
87
+ save. See [Writing content](../../dev-portal/zudoku/writing.mdx) for more
88
+ page options.
89
+
90
+ 3. **Require an API key for the sample route**
91
+
92
+ Replace the empty policy list with:
93
+
94
+ ```json title="config/policies.json"
95
+ {
96
+ "policies": [
97
+ {
98
+ "name": "api-key-auth",
99
+ "policyType": "api-key-inbound",
100
+ "handler": {
101
+ "export": "ApiKeyInboundPolicy",
102
+ "module": "$import(@zuplo/runtime)",
103
+ "options": { "allowUnauthenticatedRequests": false }
104
+ }
105
+ }
106
+ ]
107
+ }
108
+ ```
109
+
110
+ In `config/routes.oas.json`, find `paths["/todos"].get`. Update its
111
+ `x-zuplo-route` to retain the forward handler and add the policy:
112
+
113
+ ```json
114
+ "x-zuplo-route": {
115
+ "corsPolicy": "anything-goes",
116
+ "handler": {
117
+ "export": "urlForwardHandler",
118
+ "module": "$import(@zuplo/runtime)",
119
+ "options": { "baseUrl": "https://todo.zuplo.io" }
120
+ },
121
+ "policies": { "inbound": ["api-key-auth"] }
122
+ }
123
+ ```
124
+
125
+ If you're using an existing project, add the policy to its existing list and
126
+ put it first in this route's inbound chain. Leave the other routes and their
127
+ handlers in place.
128
+
129
+ `anything-goes` enables browser requests from the developer portal. For a
130
+ production API, configure [CORS](../../articles/cors.mdx) for your portal's
131
+ origin. This step protects `GET /todos`; apply authentication to other routes
132
+ before exposing private data through them.
133
+
134
+ Update `info.description` to say that `GET /todos` requires a bearer API key,
135
+ replacing the sample's statement that no authentication is required.
136
+
137
+ In a second terminal, run `curl -i http://localhost:9000/todos`. Expect
138
+ `401 Unauthorized`.
139
+
140
+ </Stepper>
141
+
142
+ ## Assign an API key and test it
143
+
144
+ <Stepper>
145
+
146
+ 1. **Create a consumer in the Portal**
147
+
148
+ Open your project in the Zuplo Portal and go to
149
+ [**Services → API Keys → Consumers**](https://portal.zuplo.com/+/account/project/services).
150
+ Select **Dev**, then click **Create Consumer**.
151
+
152
+ Set **Subject** to `tutorial-consumer`, add your email address under **Key
153
+ managers**, and leave **Metadata** as `{}`. Click **Save consumer**.
154
+
155
+ <ModalScreenshot>
156
+
157
+ ![Create an API consumer with a subject and key manager email](/media/api-key-consumer-bucket-portal-ui/create-consumer-modal.png)
158
+
159
+ </ModalScreenshot>
160
+
161
+ Key managers can view and manage this consumer's keys after signing in to the
162
+ developer portal. Signing in alone doesn't create a consumer. See
163
+ [API key management](../../articles/api-key-management.mdx).
164
+
165
+ 2. **Sign in to the developer portal**
166
+
167
+ Open `http://localhost:9200` and sign in with the same email address you
168
+ entered as a key manager. The template includes a demo sign-in provider and
169
+ `apiKeys: { enabled: true }` for this test.
170
+
171
+ Open your profile menu and select **API Keys**. The assigned consumer and its
172
+ key should appear. If they don't, check the email address and that you
173
+ created the consumer in **Dev** for the linked project.
174
+
175
+ 3. **Call the API from the documentation**
176
+
177
+ Open **API Reference**, select **Get all todos**, and click **Test**. Expand
178
+ **Authentication**, select `tutorial-consumer`, and click **Send**. Expect
179
+ `200 OK` and a JSON response containing todos.
180
+
181
+ You can also copy the key and verify it from a second terminal:
182
+
183
+ ```bash
184
+ export TODO_API_KEY="YOUR_API_KEY"
185
+ curl -i http://localhost:9000/todos \
186
+ -H "Authorization: Bearer $TODO_API_KEY"
187
+ ```
188
+
189
+ This request should return `200`; the same request without the header should
190
+ still return `401`.
191
+
192
+ </Stepper>
193
+
194
+ ## Publish the portal
195
+
196
+ Follow [Deploy to the edge](../gateway/deploy-to-the-edge/local.mdx) to connect
197
+ your repository and deploy. In the Zuplo Portal, open **Deployment URLs** and
198
+ copy the **Dev Portal** URL. The portal deploys with the gateway.
199
+
200
+ Create a consumer in the deployed environment's **Prod** API key bucket and
201
+ repeat the sign-in and API request there. Development keys are separate from
202
+ production keys.
203
+
204
+ Before inviting users or configuring a custom domain, replace the template's
205
+ demo sign-in configuration with your own
206
+ [authentication provider](../../dev-portal/zudoku/configuration/authentication.md).
207
+
208
+ ## Next steps
209
+
210
+ - [Customize the theme](../../dev-portal/zudoku/customization/colors-theme.mdx).
211
+ - [Create consumers on login](../../dev-portal/dev-portal-create-consumer-on-auth.mdx)
212
+ if every user should receive a key automatically.
213
+ - [Add more documentation](../../dev-portal/zudoku/writing.mdx).