All endpoints

DocsAPIGetting a key

POST/device-authorizations

Start 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

  • clientNamestringrequired

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

  • projectHintstringoptional

    The directory or package name the client is running in, so a person with two terminals open can tell which one asked.

  • scopesarray of stringrequired

    The 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

  • deviceCodestringrequired

    Your secret. Send it to /device-authorizations/token. Never displayed to a human.

  • userCodestringrequired

    What the person reads off your output, e.g. "BQ7K-F3XM".

  • verificationUrlstringrequired

    Where the human approves. Show this even when you also open a browser.

  • verificationUrlCompletestringrequired

    The same page with the code already filled in. This is the one to open.

  • expiresAtstringrequired

    ISO-8601 timestamp, UTC.

  • expiresInintegerrequired

    Seconds until this authorization dies.

  • intervalintegerrequired

    Seconds 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