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.
Exchange device code or refresh token for an access token.
Request
Method: POST
Endpoint: /oauth/token
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",
"device_code": "ABCD1234EFGH5678",
"client_id": "noorle-cli"
}'
Request Body
Device Code Grant
{
"grant_type": "urn:ietf:params:oauth:grant-type:device_code",
"device_code": "ABCD1234EFGH5678",
"client_id": "noorle-cli"
}
| Field | Type | Required | Description |
|---|
grant_type | string | Yes | Must be urn:ietf:params:oauth:grant-type:device_code |
device_code | string | Yes | Device code from /device/authorize |
client_id | string | Yes | OAuth client ID |
Refresh Token Grant
{
"grant_type": "refresh_token",
"refresh_token": "refresh_...",
"client_id": "noorle-cli"
}
| Field | Type | Required | Description |
|---|
grant_type | string | Yes | Must be refresh_token |
refresh_token | string | Yes | Refresh token from previous response |
client_id | string | Yes | OAuth client ID |
Client Credentials Grant
{
"grant_type": "client_credentials",
"client_id": "noorle-sa-1234567890",
"client_secret": "secret_abcdefg"
}
| Field | Type | Required | Description |
|---|
grant_type | string | Yes | Must be client_credentials |
client_id | string | Yes | Service account client ID |
client_secret | string | Yes | Service account secret |
Response
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "refresh_...",
"scope": "api"
}
| Field | Type | Description |
|---|
access_token | string | Bearer token for API requests |
token_type | string | Always “Bearer” |
expires_in | integer | Seconds until token expires (typically 1 hour) |
refresh_token | string | Token to refresh access_token |
scope | string | Granted scopes |
Status Codes
| Code | Meaning |
|---|
| 200 | Success, token returned |
| 400 | Invalid request parameters |
| 401 | Invalid credentials |
| 429 | Rate limited |
Pending Responses
Authorization Pending
User hasn’t approved yet. Keep polling:
{
"error": "authorization_pending",
"error_description": "The user has not granted authorization yet"
}
Action: Retry after interval seconds.
Slow Down
Server request too many polls. Increase interval:
{
"error": "slow_down",
"error_description": "Polling too frequently. Increase interval."
}
Action: Increase polling interval by 5 seconds, retry.
Access Denied
User rejected the request:
{
"error": "access_denied",
"error_description": "User denied the authorization request"
}
Action: Request new device code and try again.
Expired Token
Device code or refresh token expired:
{
"error": "expired_token",
"error_description": "Device code expired. Request new authorization."
}
Action: Call /device/authorize again.
Examples
Device Code to Token
# 1. Get device code
DEVICE=$(curl -s -X POST https://api.noorle.com/oauth/device/authorize \
-H "Content-Type: application/json" \
-d '{"client_id": "noorle-cli"}')
DEVICE_CODE=$(echo $DEVICE | jq -r '.device_code')
# 2. User approves at verification_uri
# (shown to user)
# 3. Exchange for token
TOKEN=$(curl -s -X POST https://api.noorle.com/oauth/token \
-H "Content-Type: application/json" \
-d "{
\"grant_type\": \"urn:ietf:params:oauth:grant-type:device_code\",
\"device_code\": \"$DEVICE_CODE\",
\"client_id\": \"noorle-cli\"
}")
ACCESS=$(echo $TOKEN | jq -r '.access_token')
echo "Token: $ACCESS"
Refresh Token
# Original response included refresh_token
REFRESH=$(echo $TOKEN | jq -r '.refresh_token')
# Refresh when access_token expires
NEW_TOKEN=$(curl -s -X POST https://api.noorle.com/oauth/token \
-H "Content-Type: application/json" \
-d "{
\"grant_type\": \"refresh_token\",
\"refresh_token\": \"$REFRESH\",
\"client_id\": \"noorle-cli\"
}")
NEW_ACCESS=$(echo $NEW_TOKEN | jq -r '.access_token')
Service Account
# Service accounts use client_credentials grant
TOKEN=$(curl -s -X POST https://api.noorle.com/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "client_credentials",
"client_id": "noorle-sa-1234567890",
"client_secret": "secret_abcdefg"
}')
ACCESS=$(echo $TOKEN | jq -r '.access_token')
Token Handling
Store Token Securely
# ❌ Don't hardcode
TOKEN="eyJhbGc..."
# ✅ Use files with restricted permissions
echo "$TOKEN" > ~/.noorle/token
chmod 600 ~/.noorle/token
# ✅ Use environment variables
export NOORLE_TOKEN="$TOKEN"
Refresh Before Expiry
import time
import json
def ensure_fresh_token():
with open("~/.noorle/token.json") as f:
token_data = json.load(f)
# Refresh if expiry is within 5 minutes
if time.time() + 300 > token_data["expires_at"]:
new_token = refresh_token(token_data["refresh_token"])
save_token(new_token)
return new_token["access_token"]
return token_data["access_token"]
Use Bearer Token
curl https://api.noorle.com/v1/capabilities \
-H "Authorization: Bearer $ACCESS"
Token Lifetime
- Access token: 1 hour
- Refresh token: 30 days
- Device code: 30 minutes
After access token expires:
- Use refresh_token to get new access_token
- If refresh_token expired, start device flow again