Skip to content

Start device sign-in with a one-time code (RFC 8628)

POST
/oauth/device_authorization
curl --request POST \
--url https://api.harperharbor.com/oauth/device_authorization \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data client_id=example \
--data scope=example \
--data 'resource=<API_BASE>/open/v1'

For command-line tools and devices that cannot receive a browser redirect. Accepts application/x-www-form-urlencoded (or multipart form) fields only; no client authentication (public clients). Show user_code and verification_uri to the person: they open the verification page on any device, sign in, type the code the tool shows and approve. Meanwhile poll POST /oauth/token with grant_type=urn:ietf:params:oauth:grant-type:device_code, device_code and client_id, waiting at least interval seconds between polls, until it returns tokens or a final error. Both codes expire after 15 minutes. Device sign-in is off by default: the administrator of the application that owns the client turns it on per client. Other clients get unauthorized_client and should use /oauth/authorize.

object
client_id
required

A client with device sign-in turned on.

string
scope

Optional, space-separated. Defaults to every scope the client is allowed; each requested scope must be one of them. Device sign-in never grants console: asking for it, or a default that includes it, is invalid_scope.

string
resource
required

Resource indicator (RFC 8707), same values as /oauth/authorize. The issued token is only valid on this resource.

string
Allowed values: <API_BASE>/open/v1 <API_BASE>/mcp/card-writer

Codes for the person and for polling. Sent with Cache-Control: no-store.

Media typeapplication/json
object
device_code
required

Opaque secret for polling /oauth/token. Keep it in the client; never show it.

string
user_code
required

Eight letters from BCDFGHJKLMNPQRSTVWXZ, formatted XXXX-XXXX. The verification page ignores case and any character that is not a letter.

string
/^[BCDFGHJKLMNPQRSTVWXZ]{4}-[BCDFGHJKLMNPQRSTVWXZ]{4}$/
verification_uri
required

Verification page. Show it next to user_code; the person opens it on any device and types the code there.

string format: uri
expires_in
required

Seconds until both codes expire: 900.

integer
interval
required

Minimum seconds between polls of /oauth/token: 5. Add 5 after each slow_down.

integer
Examplegenerated
{
"device_code": "example",
"user_code": "example",
"verification_uri": "https://example.com",
"expires_in": 1,
"interval": 1
}

unauthorized_client — device sign-in is not turned on for this client (use /oauth/authorize); invalid_client; client_not_active; invalid_scope; invalid_resource.

Media typeapplication/json
object
error
required

OAuth error code.

string
redirect_url

Only on the internal consent endpoints when the request expired.

string
retry_after

Seconds to wait before trying again; only with rate_limited.

integer
Examplegenerated
{
"error": "example",
"redirect_url": "example",
"retry_after": 1
}

rate_limited, with retry_after in seconds — too many codes requested from one address in the past hour, or too many pending requests for this client.

Media typeapplication/json
object
error
required

OAuth error code.

string
redirect_url

Only on the internal consent endpoints when the request expired.

string
retry_after

Seconds to wait before trying again; only with rate_limited.

integer
Examplegenerated
{
"error": "example",
"redirect_url": "example",
"retry_after": 1
}