← API reference

Pairing

Device-code pairing for clients with no browser.

4 of 4 · v0.120.0
POST /1/pair/start #
Public

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

Optional.

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

200

Pairing started.

application/json

object

A pairing, begun.

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.

429

Rate limited.

application/json

object

Every error body in the API has this shape.

code integer · int32 required

Mirrors the HTTP status.

error string required

Human-readable. Not a stable identifier — do not branch on it.

POST /1/pair/poll #
Public

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

The token, collected.

status string required

Always approved.

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

Always 400.

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

Human-readable.

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

Mirrors the HTTP status.

error string required

Human-readable. Not a stable identifier — do not branch on it.

GET /api/v1/pair/{user_code} #
Session

Describe a pending pairing, for the approval screen.

Requires a signed-in session — this is the browser half.

Parameters

user_code path required

As shown on the device, XXXX-XXXX.

string

Responses

200

OK.

application/json

object

A pending pairing, for the approval screen.

client_name string required

What the client called itself.

scopes string required

What approving grants, space-separated.

can_read boolean required

Whether approving grants read.

can_write boolean required

Whether approving grants write.

approved boolean required
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

Mirrors the HTTP status.

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

Mirrors the HTTP status.

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.

string

Request body required

Optional.

application/json

object

The body is optional.

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.

status string required
400

Not one of your chains.

application/json

object

Every error body in the API has this shape.

code integer · int32 required

Mirrors the HTTP status.

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

Mirrors the HTTP status.

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

Mirrors the HTTP status.

error string required

Human-readable. Not a stable identifier — do not branch on it.