Skip to content
Open Markdown

Start a device authorization ​

POST /v1/public/device/code

  • Operation ID: createDeviceCode
  • Tags: Device authorization
  • Base URL: http://localhost:8787

RFC 8628 style. The device calls this unauthenticated, shows user_code / verification_uri to the user, then polls POST /v1/public/device/token with device_code. The user approves at the portal (POST /v1/me/device/approve). Codes live 900 seconds. Limited to 3 requests per minute per client IP (429 rate_limited). An invalid body returns 400 { "error": "invalid_request" }.

Workflow: Integration guide.

Authentication: none (public).

Rate limit: 3 requests per 60s per ip.

Authentication ​

  • Auth: none (public endpoint, no token required).

Rate limit ​

3 requests per 60 seconds per ip. Exceeding it returns 429.

Request body ​

Required: yes.

Content type: application/json

Schema: object

FieldTypeRequiredDescription
user_dataobjectno-
client_namestringnoMax length: 200

Example

json
{
  "client_name": "Example CLI"
}

Responses ​

200 ​

Device and user codes issued.

  • Header x-request-id: Request identifier; quote it when reporting an error. Content type: application/json

Schema: object (DeviceCodeResponse)

FieldTypeRequiredDescription
device_codestringyesSecret polled by the device; 64 hex chars. Never shown to the user.
user_codestringyesShort code the user types at verification_uri, formatted XXXX-XXXX.
verification_uristring<uri>yes-
verification_uri_completestring<uri>yesverification_uri with the user code prefilled.
expires_inintegeryesSeconds until the code expires (900).
intervalintegeryesMinimum seconds between token polls (5).

Errors ​

400 ​

Malformed body (OAuth-style error, not the standard error body).

  • Header x-request-id: Request identifier; quote it when reporting an error. Content type: application/json

Schema: object (DeviceOAuthError)

FieldTypeRequiredDescription
errorstringyesRFC 8628 style error string.. One of: "invalid_request", "unsupported_grant_type", "authorization_pending", "slow_down", "expired_token", "access_denied", "invalid_grant", "server_error"
intervalintegernoCurrent polling interval in seconds; set for authorization_pending and slow_down.

429 ​

Rate limit exceeded (rate_limited).

  • Header x-request-id: Request identifier; quote it when reporting an error. Content type: application/json

Schema: object (ErrorBody)

FieldTypeRequiredDescription
errorstringyesStable machine-readable error code, e.g. forbidden, not_found.
detailsanynoOptional structured context. For 403 it is { perm }.

Error response

json
{
  "error": "rate_limited"
}

500 ​

Unexpected server error (internal). No details are returned.

  • Header x-request-id: Request identifier; quote it when reporting an error. Content type: application/json

Schema: object (ErrorBody), fields as described above.

Error response

json
{
  "error": "internal"
}

503 ​

Maintenance can refuse this write before authentication (degraded blocks session start and checkout; lockdown blocks other public/user writes).

  • Header x-request-id: Request identifier; quote it when reporting an error.
  • Header Retry-After: Maintenance retry delay in seconds; present for panic_mode. Example: 30. Content type: application/json

Schema: object (MaintenanceErrorBody)

FieldTypeRequiredDescription
errorstringyesOne of: "panic_mode"
levelstringyesOne of: "degraded", "lockdown"
reasonstring | nullyes-

Maintenance lockdown

json
{
  "error": "panic_mode",
  "level": "lockdown",
  "reason": "Scheduled maintenance"
}

Examples ​

curl ​

bash
curl -X POST 'http://localhost:8787/v1/public/device/code' \
  -H 'Content-Type: application/json' \
  --data '{"client_name":"Example CLI"}'

TypeScript ​

ts
const response = await fetch("http://localhost:8787/v1/public/device/code", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "client_name": "Example CLI"
  }),
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
const data = response.status === 204 || response.status === 205
  ? undefined
  : response.headers.get("content-type")?.includes("json")
    ? await response.json()
    : await response.text();

Try this endpoint

Requests go directly from your browser to the API. Send request executes this endpoint against the selected server.

POST/v1/public/device/code

Your token stays in this tab's memory and is sent only when you press Send request.

Request body (required)

Built for developers. Ready for agents.