# Getting Started with Developer Portal - Editor

<WizardSteps build="dev-portal" method="local" />

<MethodTabs build="dev-portal" method="local" />

Build a developer portal with API reference documentation, a custom
introduction, and API key management. You'll run it locally, sign in as an API
consumer, and make an authenticated request from the API playground.

## Prerequisites

- [Node.js](https://nodejs.org/en/download) 24 or later and Git.
- A [Zuplo account](https://portal.zuplo.com).
- An editor and an email address you can use to sign in to the developer portal.

## Create and customize the portal

<Stepper>

1. **Create a project**

   Run:

   ```bash
   npx create-zuplo-api@latest my-api-project
   cd my-api-project
   npm run dev
   ```

   When prompted, choose **Yes** to create a matching Portal project, complete
   sign-in, and select your Zuplo account. The CLI creates a hosted project and
   links your local copy to it. Keep `.env.zuplo` out of source control.

   The default template includes a Todo API and a developer portal. Open the
   **Docs Server** URL printed in the terminal, normally
   [http://localhost:9200](http://localhost:9200). You should see the Todo API
   reference. The gateway runs on port `9000` and the Route Designer on `9100`.

   :::tip{title="Use an existing project"}

   If your project already has a `docs` directory, clone its Git repository, run
   `npm install` at the project root, then run `npx zuplo login` and
   `npx zuplo link`, selecting the project's **working copy**. This walkthrough
   uses the Todo template; for your own API, apply the policy and tests to an
   equivalent read-only route. Start all three services with `npm run dev` from
   the project root. For an older project without `docs`, first follow
   [Enable the Developer Portal](../../dev-portal/migration.mdx).

   :::

2. **Set your portal's title and introduction**

   Open `docs/zudoku.config.tsx`. Set `site.title` and `metadata.title` to
   `Todo Developer Portal`, and set `metadata.description` to
   `Explore the Todo API and manage your API keys.` Keep the other settings.

   The `apis` entry already reads `../config/routes.oas.json` and publishes it
   at `/api`. Your API documentation updates when the OpenAPI file changes.

   Replace `docs/pages/introduction.mdx` with:

   ```mdx title="docs/pages/introduction.mdx"
   ---
   title: Welcome to the Todo API
   description: Sign in, find your API key, and make your first request.
   ---

   Use the Todo API to retrieve tasks for your application.

   1. Sign in using the email address registered as a key manager.
   2. Open **API Keys** from your profile menu to find your assigned key.
   3. Open **API Reference** and try **Get all todos** with that key.
   ```

   The default navigation already includes `introduction`. Open
   `http://localhost:9200/introduction` to see the page. Changes appear as you
   save. See [Writing content](../../dev-portal/zudoku/writing.mdx) for more
   page options.

3. **Require an API key for the sample route**

   Replace the empty policy list with:

   ```json title="config/policies.json"
   {
     "policies": [
       {
         "name": "api-key-auth",
         "policyType": "api-key-inbound",
         "handler": {
           "export": "ApiKeyInboundPolicy",
           "module": "$import(@zuplo/runtime)",
           "options": { "allowUnauthenticatedRequests": false }
         }
       }
     ]
   }
   ```

   In `config/routes.oas.json`, find `paths["/todos"].get`. Update its
   `x-zuplo-route` to retain the forward handler and add the policy:

   ```json
   "x-zuplo-route": {
     "corsPolicy": "anything-goes",
     "handler": {
       "export": "urlForwardHandler",
       "module": "$import(@zuplo/runtime)",
       "options": { "baseUrl": "https://todo.zuplo.io" }
     },
     "policies": { "inbound": ["api-key-auth"] }
   }
   ```

   If you're using an existing project, add the policy to its existing list and
   put it first in this route's inbound chain. Leave the other routes and their
   handlers in place.

   `anything-goes` enables browser requests from the developer portal. For a
   production API, configure [CORS](../../articles/cors.mdx) for your portal's
   origin. This step protects `GET /todos`; apply authentication to other routes
   before exposing private data through them.

   Update `info.description` to say that `GET /todos` requires a bearer API key,
   replacing the sample's statement that no authentication is required.

   In a second terminal, run `curl -i http://localhost:9000/todos`. Expect
   `401 Unauthorized`.

</Stepper>

## Assign an API key and test it

<Stepper>

1. **Create a consumer in the Portal**

   Open your project in the Zuplo Portal and go to
   [**Services → API Keys → Consumers**](https://portal.zuplo.com/+/account/project/services).
   Select **Dev**, then click **Create Consumer**.

   Set **Subject** to `tutorial-consumer`, add your email address under **Key
   managers**, and leave **Metadata** as `{}`. Click **Save consumer**.

   <ModalScreenshot>

   ![Create an API consumer with a subject and key manager email](/media/api-key-consumer-bucket-portal-ui/create-consumer-modal.png)

   </ModalScreenshot>

   Key managers can view and manage this consumer's keys after signing in to the
   developer portal. Signing in alone doesn't create a consumer. See
   [API key management](../../articles/api-key-management.mdx).

2. **Sign in to the developer portal**

   Open `http://localhost:9200` and sign in with the same email address you
   entered as a key manager. The template includes a demo sign-in provider and
   `apiKeys: { enabled: true }` for this test.

   Open your profile menu and select **API Keys**. The assigned consumer and its
   key should appear. If they don't, check the email address and that you
   created the consumer in **Dev** for the linked project.

3. **Call the API from the documentation**

   Open **API Reference**, select **Get all todos**, and click **Test**. Expand
   **Authentication**, select `tutorial-consumer`, and click **Send**. Expect
   `200 OK` and a JSON response containing todos.

   You can also copy the key and verify it from a second terminal:

   ```bash
   export TODO_API_KEY="YOUR_API_KEY"
   curl -i http://localhost:9000/todos \
     -H "Authorization: Bearer $TODO_API_KEY"
   ```

   This request should return `200`; the same request without the header should
   still return `401`.

</Stepper>

## Publish the portal

Follow [Deploy to the edge](../gateway/deploy-to-the-edge/local.mdx) to connect
your repository and deploy. In the Zuplo Portal, open **Deployment URLs** and
copy the **Dev Portal** URL. The portal deploys with the gateway.

Create a consumer in the deployed environment's **Prod** API key bucket and
repeat the sign-in and API request there. Development keys are separate from
production keys.

Before inviting users or configuring a custom domain, replace the template's
demo sign-in configuration with your own
[authentication provider](../../dev-portal/zudoku/configuration/authentication.md).

## Next steps

- [Customize the theme](../../dev-portal/zudoku/customization/colors-theme.mdx).
- [Create consumers on login](../../dev-portal/dev-portal-create-consumer-on-auth.mdx)
  if every user should receive a key automatically.
- [Add more documentation](../../dev-portal/zudoku/writing.mdx).
