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

# Device Authorization

> POST /oauth/device/authorize starts the OAuth device flow and returns a device code, user code, verification URI, and polling interval

Start the OAuth 2.0 device flow. This is how a terminal client authenticates a
human without handling their credentials.

```
POST https://api.noorle.com/oauth/device/authorize
Content-Type: application/json
```

## Request

```bash theme={null}
curl -X POST https://api.noorle.com/oauth/device/authorize \
  -H "Content-Type: application/json" \
  -d '{"client_id": "noorle-cli"}'
```

| Field       | Type   | Required | Description                                               |
| ----------- | ------ | -------- | --------------------------------------------------------- |
| `client_id` | string | Yes      | OAuth client identifier. The Noorle CLI uses `noorle-cli` |
| `scope`     | string | No       | Requested scope                                           |

## Response

Returned **bare** — unlike the `/v1` endpoints, it is not wrapped in a `data`
envelope.

```json theme={null}
{
  "device_code": "dc_k3Jd82nfPqW0zXbA7yTvR5mLcH1sGe4U",
  "user_code": "KHJM-7RQP",
  "verification_uri": "https://<portal-host>/oauth/device/verify",
  "verification_uri_complete": "https://<portal-host>/oauth/device/verify?user_code=KHJM-7RQP",
  "expires_in": 900,
  "interval": 5
}
```

| Field                       | Type    | Description                                                                       |
| --------------------------- | ------- | --------------------------------------------------------------------------------- |
| `device_code`               | string  | Opaque code, `dc_` followed by 32 alphanumerics. Send this back to `/oauth/token` |
| `user_code`                 | string  | Eight characters, formatted `XXXX-XXXX`, for the human to type                    |
| `verification_uri`          | string  | Where the human approves — the device-verify page in the Portal                   |
| `verification_uri_complete` | string  | The same URL with `user_code` prefilled                                           |
| `expires_in`                | integer | **900** — the lifetime advertised for the device code, in seconds                 |
| `interval`                  | integer | **5** seconds — the minimum poll interval                                         |

<Note>
  `verification_uri` is served by the **Portal**, not by the management API, and
  its host is built from the environment the API runs in. Show the human the
  value that came back in the response — do not hard-code a host.
</Note>

## Flow

```mermaid theme={null}
sequenceDiagram
    participant C as Your client
    participant A as api.noorle.com
    participant U as Human (browser)

    C->>A: POST /oauth/device/authorize
    A-->>C: device_code, user_code, verification_uri, interval
    C->>U: Show verification_uri and user_code
    U->>A: Approve in the Portal
    loop every `interval` seconds
        C->>A: POST /oauth/token
        A-->>C: still pending (HTTP 400)
    end
    A-->>C: access_token (HTTP 200)
```

1. **Request a device code.** The response carries both codes, the verification
   URL, and the poll interval.
2. **Show the human `verification_uri` and `user_code`.** Or open
   `verification_uri_complete` directly.
3. **Poll `/oauth/token`** every `interval` seconds until it returns a token or
   a terminal answer.

## Polling

Poll no faster than `interval` — **5 seconds**. A 400 means the human has not
decided; keep waiting. Stop on a 200 (you have a token) or a 403 (the human
declined), and bound the number of attempts.

Branch on the status code rather than on the contents of the error body. Once a
device code stops being usable, the answer is terminal: request a new device
code and start over rather than continuing to poll the old one.

<Note>
  On approval, the exchange consumes the session — the device code is
  single-use. A second poll after a successful exchange finds no session.
</Note>

## Status codes

| Code | Meaning                |
| ---- | ---------------------- |
| 200  | Codes issued           |
| 400  | Malformed request body |

There is no rate limit on this endpoint.

## Related

* [Get Token](/docs/reference/rest/oauth-token) — the polling step
* [Authentication](/docs/reference/authentication)
