> ## Documentation Index
> Fetch the complete documentation index at: https://unkey.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> Unkey is two separate products. Compute builds, deploys, and runs apps behind a gateway. API Management issues API keys, enforces rate limits, manages identities and permissions, and reports usage. Say which product a page belongs to; a reader can use either without the other.
> Every Unkey API endpoint is an HTTP POST to https://api.unkey.com/v2/{service}.{procedure} with a root key in the Authorization: Bearer header. Root keys are workspace scoped.
> Error codes have the form err:{system}:{category}:{specific} and each has a page at /errors/{system}/{category}/{specific}.
> The word environment means production or preview in Compute. Rate limiting has four meanings on this site; the glossary lists them.

# Configure root keys

> Assign scoped permissions in the dashboard or create and update root keys through the API.

export const DashboardScreenshot = ({src, alt, width}) => <>
    <img className="block dark:hidden" src={`${src}-light.png`} alt={alt} style={{
  width,
  maxWidth: "100%",
  height: "auto"
}} />
    <img className="hidden dark:block" src={`${src}-dark.png`} alt={alt} style={{
  width,
  maxWidth: "100%",
  height: "auto"
}} />
  </>;

You can create a root key and assign its permissions in the dashboard or through the API. Both methods control the same access. If you're still deciding what your service or agent needs, start with [Root key permissions](/docs/platform/root-keys/permissions).

This guide creates a key for a debugging agent. The agent can read deployment status and runtime logs in the billing app's staging environment. It cannot deploy changes or access other environments. The dashboard steps and API request below configure that same permission set.

## Create a key in the dashboard

You need the workspace [admin role](/docs/platform/workspace/team) to manage root keys in the dashboard.

<Steps>
  <Step title="Open the root key form">
    Open **Root Keys** in your workspace and click **New Root Key**.

    <Frame>
      <DashboardScreenshot
        target="root-key-list"
        capture="target"
        description="Open Root Keys in a disposable local workspace with synthetic
billing-staging-ci, billing-staging-debugger, and customer-key-verifier
keys. Show the list with masked secrets and no open menus at 1440 by 450
CSS pixels, scale 2, in both themes. Crop to the page content, keeping
the header and all table columns. Never show a full secret."
        src="/docs/platform/root-keys/root-key-list"
        alt="Root key list with the New Root Key button in the top right"
        capturedAt="2026-10-02T14:11:28Z"
      />
    </Frame>
  </Step>

  <Step title="Name the key">
    Enter a name such as `billing-staging-debugger` that describes the key's job.

    <Frame>
      <DashboardScreenshot
        target="root-key-create"
        capture="target"
        description="Open New Root Key in the disposable local workspace. Enter
billing-staging-debugger in Name. Leave Permissions at the initial
template gallery with no policy added. Capture both themes at 1440 by
650 CSS pixels, scale 2, with transparent padding of 24 CSS pixels and
no outer shadow. Do not submit creation."
        src="/docs/platform/root-keys/root-key-create-name"
        alt="New root key form with billing-staging-debugger entered as the name"
        width={816}
        capturedAt="2026-10-02T16:04:45Z"
      />
    </Frame>
  </Step>

  <Step title="Start a custom policy">
    Under **Permissions**, select **Start new**. The policy editor opens with no actions selected.

    <Frame>
      <DashboardScreenshot
        target="root-key-create"
        capture="target"
        description="Open New Root Key and name it billing-staging-debugger.
Select Start new. Keep the initial Workspace scope and every action
unselected. Collapse each resource group so all zero-selection counts
fit in the panel. Capture both themes at 1440 by 1000 CSS pixels,
scale 2, with transparent padding of 24 CSS pixels and no outer shadow.
Do not submit creation."
        src="/docs/platform/root-keys/root-key-create-policy"
        alt="Custom policy editor with Workspace scope and no permissions selected"
        width={816}
        capturedAt="2026-10-02T16:04:48Z"
      />
    </Frame>
  </Step>

  <Step title="Choose the environment">
    Set **Scope** to **Environments**, then select the billing app's staging environment instead of all environments.

    <Frame>
      <DashboardScreenshot
        target="root-key-create"
        capture="target"
        description="In New Root Key, name the key billing-staging-debugger and
start a custom policy. Set Scope to Environments and select only
Billing staging. Close the selector, leave every action unchecked,
and keep all resource groups expanded. Capture both themes at 1440 by
1000 CSS pixels, scale 2, with transparent padding of 24 CSS pixels and
no outer shadow. Do not submit creation."
        src="/docs/platform/root-keys/root-key-create-scope"
        alt="Policy scoped to the billing app's staging environment with no actions selected"
        width={816}
        capturedAt="2026-10-02T15:59:36Z"
      />
    </Frame>
  </Step>

  <Step title="Select the actions">
    Select **Read** on **Deployments** and **Runtime logs**. Leave the other actions and resources unselected.

    <Frame>
      <DashboardScreenshot
        target="root-key-create"
        capture="target"
        description="In New Root Key, name the key billing-staging-debugger and
scope a custom policy to Billing staging. Select only Deployments Read
and Runtime logs Read. Keep all groups expanded and the search empty.
Capture both themes at 1440 by 1000 CSS pixels, scale 2, with transparent
padding of 24 CSS pixels and no outer shadow. Do not submit creation."
        src="/docs/platform/root-keys/root-key-create-actions"
        alt="Only deployment and runtime log read actions selected for staging"
        width={816}
        capturedAt="2026-10-02T15:59:39Z"
      />
    </Frame>
  </Step>

  <Step title="Review and create the key">
    Close the policy editor with its × button to review the summary. Check every policy on the key, then click **Create key**.

    <Frame>
      <DashboardScreenshot
        target="root-key-create"
        capture="target"
        description="Open New Root Key in the disposable local workspace. Name it
billing-staging-debugger. Add a policy scoped to the billing app's staging
environment with only Deployments read and Runtime logs read. Collapse
the policy to show the final summary and Create key button. Do not submit
creation. Capture both themes at 1440 by 650 CSS pixels, scale 2, with
transparent padding of 24 CSS pixels and no outer shadow."
        src="/docs/platform/root-keys/root-key-create"
        alt="New root key panel with a read-only staging policy ready to create"
        width={816}
        capturedAt="2026-10-02T14:03:58Z"
      />
    </Frame>
  </Step>

  <Step title="Store the secret">
    Use the copy button beside the key and save the secret in your secret manager. Unkey shows it only once. Click **Done** after saving it.

    <Frame>
      <DashboardScreenshot
        target="root-key-list"
        capture="viewport"
        description="In the disposable local workspace only, create a temporary
billing-staging-debugger root key with just deployment and runtime log
read permissions in Billing staging. Capture the Root Key created dialog
over the root key list at 960 by 600 CSS pixels, scale 2, in both themes.
Keep the secret hidden and the copy button visible. Wait for fonts and
dialog animation to finish. Never reveal the secret. Delete only this
temporary key after both captures, preserving the original fixture keys."
        src="/docs/platform/root-keys/root-key-create-secret"
        alt="Root Key created dialog with a hidden secret, copy button, and one-time storage warning"
        width={960}
        capturedAt="2026-10-02T16:03:31Z"
      />
    </Frame>
  </Step>
</Steps>

A policy groups a scope and its selected actions. Selecting the environment sets the boundary for the deployment and log permissions; it doesn't automatically grant access to environment settings or variables.

Use **Add policy** when the key needs another scope. Policies add access together, so a workspace-wide policy can make a narrower environment policy ineffective as a restriction.

Templates are starting points, not least-privilege recommendations. For example, **All read permissions** includes more data than deployment status and logs, and **Verify keys** starts with every keyspace. Review both the resources and actions before saving. Changing **Scope** resets that policy's selections.

Use the API below if you need an expiry or a permission on one exact deployment rather than all deployments in the selected environment.

## Edit permissions in the dashboard

Open the key's row menu and select **Edit root key**. Change its policies, review the complete selection, then click **Save changes**.

<Frame>
  <DashboardScreenshot
    target="root-key-permission-editor"
    capture="target"
    description="Edit the synthetic billing-staging-debugger key in the local
dashboard. Expand its environment policy to show the billing app's
staging environment selected, Deployments read and Runtime logs read
checked, and write and delete unchecked. Keep the search empty. Do not
save. Capture both themes at 1440 by 1000 CSS pixels, scale 2, with
transparent padding of 24 CSS pixels and no outer shadow."
    src="/docs/platform/root-keys/root-key-permission-editor"
    alt="Permission editor with only deployment and runtime log read access in staging"
    width={816}
    capturedAt="2026-10-02T14:01:00Z"
  />
</Frame>

Saving replaces the key's permission set. To narrow access, remove broader policies as well as adding narrower ones. Clearing a search filter before reviewing helps you see selections that the filter hides.

For rotation and deletion, see [Root keys](/docs/platform/root-keys/overview).

## Create a key through the API

Authenticate with a root key that has `unkey:v1:ws_xxx:rootKeys/*#write` and covers every permission you're assigning. If you don't have a management key yet, create one in the dashboard first. Keep that credential in trusted automation, not in the agent that receives the new key.

This request creates a debugging key that expires in one hour. IDs ending in `_xxx` are placeholders. Replace each one with the full resource ID, not a name or slug. Use the billing app's ID for `app_xxx` and its staging environment's ID for `env_xxx`. Set `UNKEY_ROOT_KEY` through your secret manager or environment, rather than putting the secret in the script.

```bash wrap theme={"system"}
EXPIRES_AT_MS=$(( ($(date +%s) + 3600) * 1000 ))

curl --fail-with-body https://api.unkey.com/v2/rootKeys.createKey \
  -H "Authorization: Bearer $UNKEY_ROOT_KEY" \
  -H "Content-Type: application/json" \
  --data @- <<JSON
{
  "name": "billing-staging-debugger",
  "expires": $EXPIRES_AT_MS,
  "permissions": [
    "unkey:v1:ws_xxx:projects/proj_xxx/apps/app_xxx/environments/env_xxx/deployments/*#read",
    "unkey:v1:ws_xxx:projects/proj_xxx/apps/app_xxx/environments/env_xxx/deployments/*/logs#read"
  ]
}
JSON
```

The response contains `data.key`, the secret shown only once, and `data.keyId`, the ID used to manage the key. Store the secret without sending it to CI logs or application logs. Give the agent the new secret, not `UNKEY_ROOT_KEY`.

The `permissions` array is required. Every entry must be a supported resource/action combination in the authenticated workspace and fit within the caller's permissions. The API accepts an empty array, but that key grants no resource access.

`expires` is a future Unix timestamp in milliseconds. If the caller's root key expires sooner than one hour from now, use an earlier expiry. An expiring caller must set an expiry no later than its own. A nonexpiring caller can omit `expires` or use `null` for a key with no expiry, but use a short lifetime for temporary tasks.

## Replace permissions through the API

Use `rootKeys.updateKey` with the target `keyId` and the complete permission list you want to keep. The caller needs `write` on that root key and must cover every permission in the replacement list.

For example, narrow the debugging key to one deployment:

```bash wrap theme={"system"}
curl --fail-with-body https://api.unkey.com/v2/rootKeys.updateKey \
  -H "Authorization: Bearer $UNKEY_ROOT_KEY" \
  -H "Content-Type: application/json" \
  --data '{
    "keyId": "key_xxx",
    "permissions": [
      "unkey:v1:ws_xxx:projects/proj_xxx/apps/app_xxx/environments/env_xxx/deployments/d_xxx#read",
      "unkey:v1:ws_xxx:projects/proj_xxx/apps/app_xxx/environments/env_xxx/deployments/d_xxx/logs#read"
    ]
  }'
```

Replace `key_xxx` with `data.keyId` from creation and `d_xxx` with the deployment's ID. This request removes the environment-wide deployment permissions and replaces them with permissions on that deployment. It doesn't append to the existing list.

Omitting `permissions` leaves the existing permissions unchanged. Sending `"permissions": []` removes all permissions. An expiring root key can only update keys that expire no later than itself.

## Verify the resulting access

Call an operation the key needs, then check a read outside its scope. For the single-deployment debugging key, `deployments.getDeployment` on the deployment represented by `d_xxx` must succeed, while a lookup of a different deployment must fail.

If a request fails unexpectedly, compare its required action and resource IDs with the full permission list. Check list filters too: a request for all deployments asks for more access than a request scoped to one environment. See [permission troubleshooting](/docs/errors/unkey/authorization/insufficient_permissions).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.