/device-authorizations/tokenPoll for the approved key
Exchange a device code for the key a human approved.
Auth header
No key needed: this is how something without a key gets one.
Request body
deviceCodestringrequiredThe secret from POST /device-authorizations.
Example request
curl -X POST "https://app.tagsentry.ai/api/v1/device-authorizations/token" \ -H "Content-Type: application/json" \ -d '{"deviceCode":"ABCD-EFGH"}'Response 200
statusstringrequired"pending" means keep polling and is NOT an error. Every other outcome -- denied, expired, already claimed, unknown -- is a 4xx with a code, because each one means stop.
One of
pending,issuedapiKeystring | nullrequiredTHE ONLY TIME THIS VALUE EXISTS OUTSIDE OUR MINT. It is not stored in clear and cannot be recovered; a lost key is re-minted, never recalled. Null while pending.
accountIdstring | nullrequiredprefixstring | nullrequiredThe key's visible prefix, for display and for logs.
scopesarray of stringrequiredWhat the human actually approved. May be empty while pending.
expiresAtstring | nullrequiredWhen the issued key stops working (ISO 8601). Run
tagsentry loginfor a new one. Null while pending.
Example response
{ "status": "pending", "apiKey": "…", "accountId": "3f6c1b8e-2d4a-4c7e-9a51-0b8f2e6d7c10", "prefix": "…", "scopes": [ "…" ], "expiresAt": "2026-09-26T14:02:00.000Z"}More about this endpoint
UNAUTHENTICATED -- the device code IS the credential, which is why it must never be logged or displayed.
status: "pending" with a 200 means nobody has answered yet: wait interval seconds and ask again. Anything else is a 4xx and means stop -- denied, expired, already claimed and unknown are each their own refusal, because the right next step differs for each.
The key is minted BY THIS CALL, not by the approval, so its secret exists for the first time in this response and is never stored in clear anywhere. Write it somewhere outside your repository.
Errors400 · 404 · 409 · 429 · 500
400The request did not validate against this operation's schema.404No such resource on this account. A site id belonging to a DIFFERENT account answers 404, never 403 -- a 403 would confirm the id exists somewhere.409The request conflicts with existing state.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