---
title: "Create Token"
method: POST
path: "/tokens"
---

# Create Token

`POST /tokens`

## Request

- Base URL: `https://api.xhostd.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api.xhostd.com/tokens`
- Auth: none declared

Mint a token for the caller with the default granted scopes.

A mint never grants more than the caller already holds, so a caller
that does not hold the whole default set cannot mint it here (403
``scope_denied``). The grant is fixed, so the gate is the whole rule
on this route; the remedy is ``POST /credentials``, which grants the
caller's own set or less. The check runs before the label check: a
caller that cannot mint at all should not learn the label rules.

The route refuses the reserved console-session label, exactly like
``routes/internal.py::create_user_token``. A token under that label
hides from the console token list and escapes the per-label cap.

A mint never outlives the caller either, so the caller's remaining
life is the ceiling on ``expires_in`` and the child's expiry when the
request names none. A caller with no expiry keeps the route's
historical behaviour: no ceiling, and no expiry on the child.

## Headers

- `authorization` string, nullable

## Request body

- CreateTokenRequest
  - `label` string, nullable
  - `expires_in` integer, nullable

## Response `200`

Successful Response

- CreateTokenResponse
  - `token_id` string, uuid, required
  - `plaintext` string, required
  - `scopes` string[], required
  - `label` string, nullable, required
  - `created_at` string, date-time, required
  - `expires_at` string, date-time, nullable, required

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/xhostd/apis/xhostd-public.md) · [All operations](https://skmtc.dev/xhostd/apis/xhostd-public/llms.txt) · [OpenAPI document](https://skmtc.dev/xhostd/apis/xhostd-public/revisions/bf439c4d1789?raw)
