/device-authorizationsStart a device authorization
How a terminal with no credential gets one.
Auth header
No key needed: this is how something without a key gets one.
Request body
clientNamestringrequiredHow this client will name itself on the approval screen, e.g. "tagsentry-cli 0.1.0 (darwin 24.0)". Displayed to a human as text and trusted for nothing.
projectHintstringoptionalThe directory or package name the client is running in, so a person with two terminals open can tell which one asked.
scopesarray of stringrequiredThe scopes the issued key should carry. Restricted to what a terminal is allowed to be granted this way -- see lib/api/deviceAuthorization.ts. A request for anything outside that set is refused whole, so the approval screen can never be made to ask a human for a scope this flow cannot grant.
Example request
curl -X POST "https://app.tagsentry.ai/api/v1/device-authorizations" \ -H "Content-Type: application/json" \ -d '{"clientName":"…","projectHint":"…","scopes":["…"]}'Response 201
deviceCodestringrequiredYour secret. Send it to /device-authorizations/token. Never displayed to a human.
userCodestringrequiredWhat the person reads off your output, e.g. "BQ7K-F3XM".
verificationUrlstringrequiredWhere the human approves. Show this even when you also open a browser.
verificationUrlCompletestringrequiredThe same page with the code already filled in. This is the one to open.
expiresAtstringrequiredISO-8601 timestamp, UTC.
expiresInintegerrequiredSeconds until this authorization dies.
intervalintegerrequiredSeconds to wait between polls.
Example response
{ "deviceCode": "ABCD-EFGH", "userCode": "ABCD-EFGH", "verificationUrl": "https://silverpine.example/", "verificationUrlComplete": "https://silverpine.example/", "expiresAt": "2026-09-26T14:02:00.000Z", "expiresIn": 0, "interval": 0}More about this endpoint
UNAUTHENTICATED, and the only operation on this surface that is.
You get a userCode to show the person and a deviceCode to keep. Send them to verificationUrlComplete, where they sign in -- creating an account if they have none -- and approve or refuse the request. Poll POST /device-authorizations/token with the device code until it answers.
WHY THERE IS NO ENDPOINT THAT JUST CREATES AN ACCOUNT: minting an identity with nobody present means no verified email and no ceiling on abuse. A human in a browser is the control, and this grant is how a headless process borrows one.
THE KNOWN WEAKNESS, stated plainly because every implementation of this grant has it: an attacker can start a flow and talk somebody into approving it, and what they get is that person's own account. Do not send a user code to anyone. Our approval screen names the client and the directory that asked and tells a person who did not just run a CLI to press Deny.
Errors400 · 429 · 500
400The request did not validate against this operation's schema.429A rate limit or quota was exceeded. The body names WHICH one.500Something failed on our side. The requestId in the body is what to quote.
Every error has the same body: { error: { code, message, requestId } }.
From the OpenAPI document, version 2026-08-26. Raw OpenAPI