Skip to content

CLI device flow

The CLI’s browser login uses the device authorization grant (RFC 8628). It lets a terminal — even a headless one over SSH — obtain an API key by having you approve in a browser, without pasting a long secret into the shell. You normally trigger it with loomta auth:login; this page documents the underlying endpoints if you want to build your own client.

CLI Loomta API Browser (you)
│ │ │
│ POST /auth/device/start │ │
│ ─────────────────────────────► │ │
│ ◄───── device_code, │ │
│ user_code, URIs │ │
│ │ │
│ show user_code + open URL ───────────────────────────────► │
│ │ POST /auth/device/approve │
│ │ ◄─────────────────────────── │ (session + org)
│ │ mints loomta_sk_… key │
│ POST /auth/device/token │ │
│ (poll every `interval`s) ───► │ │
│ ◄───── authorization_pending │ │
│ … │ │
│ ◄───── { api_key } │ (returned exactly once) │
▼ ▼ ▼
Terminal window
curl -X POST https://api.loomta.com/auth/device/start \
-H "Content-Type: application/json" -d '{}'
{
"device_code": "…secret…",
"user_code": "LMT-7QX2-9KFP",
"verification_uri": "https://loomta.com/cli-auth",
"verification_uri_complete": "https://loomta.com/cli-auth?code=LMT-7QX2-9KFP",
"interval": 5,
"expires_in": 600
}
  • device_code — your secret; keep it to poll with. Never shown to the user.
  • user_code — the LMT-XXXX-XXXX code the user confirms in the browser.
  • verification_uri / …_complete — where to send the user. The _complete form pre-fills the code.
  • interval — minimum seconds between polls (5).
  • expires_in — the codes are valid for 600 seconds (10 minutes).

Open verification_uri_complete. The user signs in (if needed), picks a workspace, and authorizes. Under the hood the web app calls:

POST /auth/device/approve
Authorization: Bearer <session-token>
x-organization-id: <workspace-uuid>
{ "userCode": "LMT-7QX2-9KFP" }

This mints a new loomta_sk_… key scoped to the chosen workspace and binds it to the pending request. (This call is made by the Loomta web app, not by your CLI.)

Poll no faster than interval:

Terminal window
curl -X POST https://api.loomta.com/auth/device/token \
-H "Content-Type: application/json" \
-d '{ "device_code": "…secret…" }'

While the user hasn’t approved yet, you get HTTP 400 with one of these error values (RFC 8628 §3.5):

error Meaning What to do
authorization_pending Not approved yet. Keep polling at interval.
slow_down You polled too fast. Add 5s to your interval, continue.
expired_token The 10-minute window elapsed. Start over with /start.
access_denied Denied, invalid, or already claimed. Stop; restart if needed.

On success you get 200 once:

{ "status": "approved", "api_key": "loomta_sk_…" }

Unless you’re writing your own client, you don’t need any of this — run:

Terminal window
loomta auth:login # opens the browser, polls, saves the key
loomta auth:login --no-browser # headless: prints the URL + code instead

See CLI authentication for all three login methods.