OAuth 2.0 device authorization flow (RFC 8628) for signing in from CLIs, IDEs, and other devices without a browser on the same machine.
The belt and infsh CLIs use this flow by default when you run belt login or infsh login in a terminal. You can also call the endpoints directly to build your own login experience.
No API key is required to start or poll a device authorization. Approving a request requires a logged-in workspace session in the browser.
→ Authentication · Magic link sign-in · CLI setup
Flow overview
1sequenceDiagram2 participant CLI as CLI or client3 participant API as api.inference.sh4 participant Browser as Browser (logged in)56 CLI->>API: POST /device/auth7 API-->>CLI: user_code, device_code, approve_url, interval8 CLI->>Browser: Open approve_url (optional)9 Browser->>API: POST /device/auth/approve (session cookie)10 loop Every interval seconds11 CLI->>API: GET /device/auth/poll/{device_code}12 API-->>CLI: status pending | approved | denied | expired13 end14 API-->>CLI: api_key when approved- Your client calls
POST /device/authand receives a short user code and a secret device code. - The user opens approve_url in a browser (or enters the user code at the workspace device-auth page) and approves while signed in.
- Your client polls
GET /device/auth/poll/{device_code}every interval seconds until the status isapproved,denied, orexpired. - On
approved, the poll response includes an api_key (30-day CLI key namedCLI,source: device-auth). Store it securely and send it asAuthorization: Bearer …on subsequent API calls.
Codes expire after 300 seconds (5 minutes). The recommended poll interval is 3 seconds.
Start device authorization
POST /device/auth
Authentication: None.
Response
| Field | Type | Description |
|---|---|---|
user_code | string | Short code shown to the user (format XXXX-XXXX) |
device_code | string | Secret code used only for polling (do not display to the user) |
approve_url | string | Workspace URL to open for one-click approval |
poll_url | string | Convenience URL (workspace host); clients should poll the API path below |
expires_in | number | Seconds until the code expires (300) |
interval | number | Recommended seconds between poll requests (3) |
Example:
1curl -X POST https://api.inference.sh/device/auth \2 -H "X-API-Version: 2"1{2 "user_code": "ABCD-2345",3 "device_code": "01HXYZ…",4 "approve_url": "https://app.inference.sh/auth/device?code=ABCD-2345&utm_source=cli&utm_medium=app",5 "poll_url": "https://app.inference.sh/device/auth/poll/01HXYZ…",6 "expires_in": 300,7 "interval": 38}Poll for approval
GET /device/auth/poll/{device_code}
Authentication: None.
Poll until status is terminal (approved, denied, or expired). Wait at least interval seconds between requests.
Response
| Field | Type | Description |
|---|---|---|
status | string | pending, approved, denied, or expired |
api_key | string | Present when status is approved (starts with inf_) |
team_id | string | Team the key was issued for (when approved) |
Example:
1curl "https://api.inference.sh/device/auth/poll/DEVICE_CODE_HERE" \2 -H "X-API-Version: 2"1{2 "status": "approved",3 "api_key": "inf_…",4 "team_id": "team_abc123"5}While status is pending, the response omits api_key.
Check user code (approval page)
GET /device/auth/code/{code}
Authentication: None.
Used by the workspace device-auth UI to validate a user code before approval. Accepts codes with or without the dash (ABCD2345 or ABCD-2345).
Response
| Field | Type | Description |
|---|---|---|
user_code | string | Normalized code |
valid | boolean | Whether the code can still be approved |
status | string | pending, approved, or expired |
expires_at | string | ISO timestamp (when known) |
Approve device authorization
POST /device/auth/approve
Authentication: Workspace session (browser cookie after SSO or magic link). Guests cannot approve.
Creates a CLI API key for the signed-in user (30-day expiry, full scopes unless scopes is set).
Request
| Field | Type | Required | Description |
|---|---|---|---|
code | string | Yes | User code from POST /device/auth |
team_id | string | No | Team to issue the key for (defaults to active team) |
scopes | string[] | No | API key scopes; omit for full access |
1{2 "code": "ABCD-2345"3}Response
1{2 "success": true,3 "message": "Device authorized successfully"4}Errors include invalid_code, expired, and already_processed when the code is no longer pending.
CLI usage
1belt login # device flow, then fallback to manual key entry2belt login --key inf_… # skip device flow (required in CI / non-interactive shells)3belt auth token # print stored key to stdout (no newline) for scriptsIn non-interactive environments (no TTY, CI set, or BELT_NON_INTERACTIVE=1), device authorization is skipped; pass --key or set INFSH_API_KEY. After login, use belt auth token in subshells: curl -H "Authorization: Bearer $(belt auth token)" ...
Session-start hook: When the belt plugin is installed for Claude Code or Codex, the SessionStart hook calls belt plugin hook session-start. If no API key is configured (including anonymous guest sessions), the hook opens a browser for device authorization, prints the approval URL and user code into session context, and polls for up to 45 seconds. A 6-hour cooldown prevents reopening the browser on every session (~/.inferencesh/hook-auth-cooldown). When already logged in, the hook prints your account email instead.
→ CLI setup — Login · Coding agents — SessionStart hook
Related
- Authentication — API keys and SDK setup
- REST overview — Base URL and API versioning