Begin device-code pairing.
Unauthenticated by design — the client has no credential yet. Returns a
device_code to poll with and a short user_code for the human to type
into Settings.
Vocabulary echoes RFC 8628 because client authors have met that shape,
but this is not OAuth: no client registration, no client_id, and
the result is an ordinary Tapedeck token.
The user code is eight characters and therefore guessable, so its safety
is structural: a five-minute window, single use in both directions, rate
limiting, and approval requiring a signed-in session.
Request body required
application/json
object
Both fields are optional, and so is the body.
client_name string | null
What to call the token once it exists — "Pano on Android", "fooyin".
Shown to the user when approving, so name it per device. At most 60
characters; name is accepted too.
scopes string | null
Requested grants, space- or comma-separated: submit, read,
write. submit is always granted; anything else unknown is ignored.
The approval screen shows the user what they are granting.
Responses
application/json
object
device_code string required
Poll /1/pair/poll with this. Keep it secret — it collects the token.
user_code string required
The short code for the human to type into Settings → API Tokens, as
XXXX-XXXX.
verification_uri string required
Where the human goes to approve it, as far as the server can tell from
the request. A bare path when it cannot.
expires_in integer · int64 required
Seconds until the request lapses. Five minutes.
interval integer · int64 required
Minimum seconds between polls.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
Poll for approval.
400 authorization_pending until approved, then the token once — it is
deleted from the request the moment it is collected. Expired and unknown
device codes are both 400 expired_token, so polling cannot be used to
learn whether a code was ever real.
Request body required
application/json
object
device_code string required
Responses
200 Approved. The token is in this body and nowhere else.
application/json
object
token string required
The new API token. In this body and nowhere else — it is deleted
from the request the moment it is collected.
user_name string | null
Whose token it is — the account that approved the request.
400 authorization_pending (keep polling) or expired_token (start again).
application/json
object
Not collected, and why — RFC 8628's error vocabulary, in Tapedeck's error
shape.
code integer · int32 required
error string required
authorization_pending — nobody has approved it yet, keep polling —
or expired_token, which is also what an unknown device code gets.
error_description string required
interval integer | null · int64
Minimum seconds before the next poll. Only on authorization_pending.
429 Polling too fast. Back off and keep the pairing alive.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
POST /api/v1/pair/{user_code}/approve # Session
Approve a pairing request.
Session-only, and single use — approving twice mints nothing the second
time. The token is never in this response; it goes to the polling
client. A live credential in a second place is how it ends up in a
screenshot.
Parameters
user_code path required
As shown on the device, XXXX-XXXX.
Request body required
application/json
object
default_chain_id integer | null · int64
The chain listens from this client should be attributed to — rung 3 of
the ladder, and the reason one token per app is worth the trouble.
Must be one of your chains.
Responses
200 Approved. status is approved.
application/json
object
An acknowledgement with nothing else to report.
status names what happened — ok, updated, deleted, cleared,
started and so on; the operation says which it sends. A client needs only
the HTTP status to know it worked.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
401 No valid session cookie or token. Also returned when a token is
presented to a session-only endpoint — the endpoint does not accept
tokens at all, so the scope is irrelevant.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.
{
"code": 401,
"error": "Authentication required. Log in to access this endpoint."
}
404 No such resource, or it belongs to another user.
application/json
object
Every error body in the API has this shape.
code integer · int32 required
error string required
Human-readable. Not a stable identifier — do not branch on it.