> ## 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.

# Get Token

> POST /oauth/token exchanges an approved device code for a Noorle JWT access token — the only grant type this endpoint accepts

Exchange an approved device code for an access token.

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

<Warning>
  **This endpoint accepts one grant type only:**
  `urn:ietf:params:oauth:grant-type:device_code`. `refresh_token` and
  `client_credentials` are rejected.
</Warning>

## Request

```bash theme={null}
curl -X POST https://api.noorle.com/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "grant_type": "urn:ietf:params:oauth:grant-type:device_code",
    "code": "dc_k3Jd82nfPqW0zXbA7yTvR5mLcH1sGe4U",
    "client_id": "noorle-cli"
  }'
```

| Field        | Type   | Required | Description                                            |
| ------------ | ------ | -------- | ------------------------------------------------------ |
| `grant_type` | string | Yes      | Must be `urn:ietf:params:oauth:grant-type:device_code` |
| `code`       | string | Yes      | The `device_code` from `/oauth/device/authorize`       |
| `client_id`  | string | Yes      | The same client id used to start the flow              |

<Note>
  The field is named **`code`**, not `device_code`. The device-authorize
  response calls it `device_code`; the field you send it back in is `code`. A
  request with an empty `code` is rejected as a malformed request.
</Note>

## Response

The token response is returned **bare**. Unlike the `/v1` endpoints, it is not
wrapped in a `data` envelope — read the fields off the top level of the body.

```json theme={null}
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "..."
}
```

| Field          | Type    | Description                                |
| -------------- | ------- | ------------------------------------------ |
| `access_token` | string  | HS256 JWT. Send as `Authorization: Bearer` |
| `token_type`   | string  | Always `Bearer`                            |
| `expires_in`   | integer | **3600** — one hour                        |
| `scope`        | string  | Granted scope                              |

The access token's claims carry `iss` (the management API host), `sub` (your
user id), `aud` (the `client_id`), `account_id`, `scope`, `iat`, and `exp`.

A successful exchange **consumes the device session** — the device code is
single-use.

<Warning>
  **There is no refresh flow on this host.** `api.noorle.com` accepts the
  device-code grant and nothing else, so there is no way to exchange a token for
  a fresh one. When the hour is up, run the device flow again — or use an API
  key, which does not expire on a fixed clock.
</Warning>

## Polling responses

Poll on the `interval` returned by `/oauth/device/authorize`. The exchange sits
in one of four states.

| Status | Meaning                                                                                                                         | What to do                                                     |
| ------ | ------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
| 200    | Approved — token issued                                                                                                         | Stop polling                                                   |
| 400    | Either still pending, or the device code is no longer usable — unknown, already redeemed, expired, or an unsupported grant type | Wait `interval` seconds and poll again, up to your attempt cap |
| 403    | Denied — the human rejected the request                                                                                         | Stop polling                                                   |

**Branch on the status code.** A 400 does not distinguish "not decided yet" from
"this code is finished", so a correct client polls on the interval and gives up
after a bounded number of attempts rather than waiting for a terminal 400.

A denial deletes the device session, so a poll after a 403 reports the code as
unusable rather than denied.

## Status codes

| Code | Meaning                                                                 |
| ---- | ----------------------------------------------------------------------- |
| 200  | Token issued                                                            |
| 400  | Not approved yet, an unusable device code, or an unsupported grant type |
| 403  | The human denied the authorization                                      |

There is no rate limit on this endpoint.

## Using the token

```bash theme={null}
curl https://api.noorle.com/v1/capabilities \
  -H "Authorization: Bearer eyJhbGc..."
```

Store it with restrictive permissions — it is a bearer credential with your
account authority for the next hour.

## Related

* [Device Authorization](/docs/reference/rest/oauth-device-authorize)
* [Authentication](/docs/reference/authentication)
* [Errors and rate limits](/docs/reference/errors-and-rate-limits)
