Device authorization
Device authorization
Device authorization (RFC 8628) mints a Personal Access Token for a command-line tool or headless script that can open a browser but can't receive an HTTP redirect itself. You request a code, a person approves it in their browser, and your program polls until the token is ready.
When to use this
Use device authorization when your program runs on a machine without a reachable callback URL — a terminal, a CI worker, an embedded device. If your program is a web app that can receive a redirect, use OAuth 2.1 + PKCE instead. If a person is just generating a token for their own scripts, they can skip both flows and create one directly — see Personal access tokens.
Step 1: request a device code
curl -X POST https://api.falcata.io/api/v1/auth/device/code \
-H "Content-Type: application/json" \
-d '{"label": "my-cli on laptop"}'
label is optional (defaults to CLI) and shows up later in the user's token list, so name it after the machine or integration requesting it. You can also pass scope to request a narrower PAT scope than full account access.
Response:
{
"device_code": "8f2b1c...redacted",
"user_code": "WXYZ-2345",
"verification_uri": "https://id.falcata.io/device",
"verification_uri_complete": "https://id.falcata.io/device/WXYZ-2345",
"expires_in": 600,
"interval": 5
}
device_codeis the secret your program polls with. Keep it out of logs.user_codeis the short code a human reads and types — an 8-character code from a confusion-free alphabet (no0/O/1/I/L) so it's easy to read aloud or transcribe.expires_inis 600 seconds (10 minutes). If the person doesn't approve within that window, the code expires and you must request a new one.intervalis the minimum number of seconds to wait between polls — 5 seconds.
Step 2: the user approves
Show the person user_code and either open verification_uri_complete directly or tell them to visit verification_uri and type the code in. They sign in to Falcata ID if they aren't already, see the code and what's requesting access, and approve or deny it.
Step 3: poll for the token
Poll the token endpoint no faster than every interval seconds:
curl -X POST https://api.falcata.io/api/v1/auth/device/token \
-H "Content-Type: application/json" \
-d '{"device_code": "8f2b1c...redacted"}'
While the person hasn't acted yet, you get:
{ "error": "authorization_pending" }
Keep polling on this response — it isn't a failure. Once approved, the same call returns the token:
{ "access_token": "fpat_...redacted", "token_type": "Bearer" }
Errors while polling
error | Meaning | What to do |
|---|---|---|
authorization_pending | No one has approved or denied the code yet | Keep polling at the interval you were given |
access_denied | The person explicitly denied the request | Stop polling. Tell the user the request was denied and let them retry from step 1 if they want to authorize again |
expired_token | 10 minutes passed with no approval | Stop polling. Start over from step 1 to get a fresh code |
Polling faster than interval risks being rate-limited by the server; if your requests start failing outright rather than returning authorization_pending, back off and poll at the stated interval.
What you get
The token returned is a Personal Access Token — the same kind you'd generate manually, prefixed fpat_. It doesn't expire on its own and carries whatever scope you requested in step 1 (full account access if you didn't request one). Store it securely and send it as Authorization: Bearer $FURNACE_TOKEN on API calls. Confirm it works and see who it resolves to with:
curl https://api.falcata.io/api/v1/auth/whoami \
-H "Authorization: Bearer $FURNACE_TOKEN"
To revoke, rename, or inspect the token later, see Personal access tokens. If a request with this token fails, see Troubleshooting.