> For the complete documentation index, see [llms.txt](https://docs.kangal.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.kangal.dev/authentication/api-tokens.md).

# Admin API tokens

Admin API tokens are machine-to-machine bearer credentials for automation, deployment jobs, and external control-plane clients. They are not user session tokens, consumer API keys, or SCIM provisioning tokens.

## Credential types

| Credential        | Prefix or form  | Purpose                                | Tenant behavior                                                          |
| ----------------- | --------------- | -------------------------------------- | ------------------------------------------------------------------------ |
| User access token | JWT bearer      | Interactive console and user API calls | Fixed to the user's tenant.                                              |
| Admin API token   | `kangaladm_...` | Admin API automation                   | Tenant-scoped, except an explicitly authorized global super-admin token. |
| SCIM token        | `kscim_...`     | Directory provisioning only            | Permanently fixed to its issuing tenant.                                 |

Do not use an Admin API token on a public runtime route or distribute it to application consumers.

## Create a token in the console

1. Open a gateway and select **Keys**. The localized route is `/<locale>/dashboard/admin/gateways/<gateway-id>/keys`.
2. Select **New key**.
3. Enter a descriptive name, role, comma-separated scopes, and optional expiry.
4. Create the key and copy the plaintext immediately.

The Keys page lists every Admin API token in the gateway's tenant, not a gateway-specific credential. Creating it from one gateway does not restrict the token to that gateway.

The plaintext is returned only in the create response. Kangal stores a SHA-256 hash and a short display prefix. List and detail responses never return the plaintext or hash.

## Roles and scopes

Both the token role and scope must authorize a request.

| Scope            | Allowed requests                                         |
| ---------------- | -------------------------------------------------------- |
| `admin:read`     | `GET`, `HEAD`, and `OPTIONS` admin requests.             |
| `admin:write`    | Mutating admin requests and read requests.               |
| `admin:*` or `*` | All admin requests allowed by the token role and tenant. |

| Role             | Behavior                                                                               |
| ---------------- | -------------------------------------------------------------------------------------- |
| `admin_readonly` | Read-only admin role. It cannot be combined with `admin:write`.                        |
| `admin`          | Tenant admin. A tenant ID is required and cannot be changed by the caller.             |
| `super_admin`    | Global operator role. Only a signed-in super-admin can create it; its tenant is empty. |

When `role` is omitted, read-only scopes produce `admin_readonly`; a write-capable scope produces `admin`. A regular admin can create tokens only for their own tenant. A cross-tenant create is rejected with `403`.

## Create with the API

Use an existing authorized admin session or token to create the replacement. The example values are placeholders.

```bash
export KANGAL_URL='https://api.example.com'
export KANGAL_ADMIN_BEARER='<authorized-admin-bearer>'

curl --request POST "$KANGAL_URL/admin/api-tokens" \
  --header "Authorization: Bearer $KANGAL_ADMIN_BEARER" \
  --header 'Content-Type: application/json' \
  --data '{
    "name": "ci-readonly",
    "tenant_id": "acme",
    "role": "admin_readonly",
    "scopes": ["admin:read"],
    "expires_at": "2027-01-01T00:00:00Z"
  }'
```

Store the response `token` in an approved secret manager. Keep only its record ID and prefix in runbooks.

Use it as a standard bearer token:

```bash
export KANGAL_API_TOKEN='<one-time-visible-token>'

curl "$KANGAL_URL/admin/gateways" \
  --header "Authorization: Bearer $KANGAL_API_TOKEN"
```

## Inventory and status

```bash
curl "$KANGAL_URL/admin/api-tokens?tenant_id=acme" \
  --header "Authorization: Bearer $KANGAL_ADMIN_BEARER"
```

Records include `token_prefix`, role, scopes, creation time, optional expiry, revocation time, and `last_used_at`. A token is usable only while it is neither expired nor revoked. Treat `expires_at` and `revoked_at` as the authoritative lifecycle fields when reviewing inventory.

## Rotation and revocation

Admin API tokens are not updated in place.

1. Create a replacement with the minimum required role, scopes, tenant, and a bounded expiry.
2. Store the new plaintext and update the client.
3. Verify a read or write operation appropriate to its scope.
4. Revoke the old token by record ID.

```bash
curl --request DELETE "$KANGAL_URL/admin/api-tokens/<token-id>" \
  --header "Authorization: Bearer $KANGAL_ADMIN_BEARER"
```

Revocation is immediate for subsequent authentication. Create and revoke operations are audit logged without recording plaintext credentials.

## Security practices

* Prefer `admin:read` for inventory and reporting jobs.
* Use a tenant token unless global access is an explicit operational need.
* Give each workload its own name and expiry; do not share one key across CI, operators, and third parties.
* Never send a token in a query parameter, URL, ticket, screenshot, or log.
* Redact the `Authorization` header in proxies and observability tooling.
* Revoke a credential immediately if its plaintext is exposed.

## Troubleshooting

| Symptom                                         | Cause and action                                                                                                         |
| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| Plaintext cannot be retrieved                   | This is expected. Create a replacement and revoke the old record.                                                        |
| `401 Invalid token`                             | The token is unknown, malformed, expired, or revoked. Confirm that the full value, not the display prefix, was supplied. |
| `403 Insufficient admin token scope`            | The HTTP method requires `admin:write`, or the role is read-only. Issue a correctly scoped replacement.                  |
| `403 Cannot access another tenant`              | The token is fixed to a different tenant. Do not override it with headers or query parameters.                           |
| A non-revoked token still fails                 | Verify `expires_at`, role, scopes, and tenant in the API record.                                                         |
| A key created under one gateway can see another | Tokens are tenant-scoped, not gateway-scoped. Use separate tenants for hard isolation.                                   |


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.kangal.dev/authentication/api-tokens.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
