Skip to content

Trade a device refresh token for a fresh session JWT

POST
/login/refresh
curl --request POST \
--url https://quikstik-core.fly.dev/login/refresh \
--header 'Content-Type: application/json' \
--data '{ "refresh_token": "example" }'

Unauthenticated by design — the refresh token is the credential. Mints a session JWT with the same claims as the original establish but kind = device, and records the use (last_used_at_ms). Does not rotate the refresh token. Rate-limited with the same limiter as POST /login/requests (keyed on the credential’s digest + client IP); over the limit is 429. The three 401 reasons are stable machine strings.

Media typeapplication/json
object
refresh_token
required
string
>= 32 characters

Example generated

{
"refresh_token": "example"
}

Fresh device session minted

Media typeapplication/json
object
session_token
required

Session JWT with kind = device; accepted anywhere a session is.

string
scopes
required

The effective permission tokens of the session’s membership, sorted (empty when the membership is not active).

Array<string>
role
required

The highest role the scopes imply. Open (see contracts/openapi/README.md); the app reads ownership here, not from GET /team.

string

Example generated

{
"session_token": "example",
"scopes": [
"example"
],
"role": "example"
}

Malformed body

Media typeapplication/json
object
reason
required
string

Example generated

{
"reason": "example"
}

Refresh refused; reason is one of refresh_revoked, refresh_expired, refresh_unknown

Media typeapplication/json
object
reason
required
string

Example generated

{
"reason": "example"
}

Rate limited (reason = rate_limited)

Media typeapplication/json
object
reason
required
string

Example generated

{
"reason": "example"
}

No durable backend is wired for this operation (fail-closed)

Media typeapplication/json
object
reason
required

Stable, machine-readable reason code (e.g. “sold_out”, “processor_not_configured”, “rail_refused”).

string

Example

{
"reason": "sold_out"
}