Skip to content

Sign-in

People sign in with a passkey and nothing else; there are no passwords. A passkey is bound to the address it was made at, so the ceremony runs on this server's own page in the browser, and the app collects the result with a one-time code bound to a PKCE challenge. An account gets its first passkey by signing up (where the operator allows it) or from a one-time ticket the operator issues; further passkeys from a ticket a signed-in person issues.

GET /passkey

The sign-in page

Unauthenticated

Callable with no credential.

The page every passkey ceremony runs on, opened in the system browser. What it does is read from its address: mode=signin (the default) or mode=signup, or a ticket to add a passkey to the account the ticket names. An app passes its PKCE challenge and a state; when the ceremony succeeds the page opens revoked://auth?code=…&state=…, and the app redeems the code at /api/passkeys/token.

Parameters

Name In Required Notes
mode query no
ticket query no
challenge query no PKCE S256 challenge, base64url.
state query no

Responses

Status Meaning
200 The page.

POST /api/passkeys/login/begin

Start a passkey sign-in

Unauthenticated

Callable with no credential.

Returns WebAuthn request options for a discoverable credential — no account is named; the authenticator's answer says whose passkey it is. Must be called at the server's own address (localhost on a development machine, never a loopback IP). Rate-limited per IP.

Request body

application/json

Field Type Notes
challenge string The app's PKCE S256 challenge; the code issued at the end is bound to it.

Responses

Status Meaning
200 The ceremony, good for five minutes.
400 passkey_request_invalid or passkey_origin_invalid.
429 Too many attempts. Password gates, probes and challenges are limited per client, so a slug cannot be brute-forced.

Response fields

Field Type Notes
session string
options object {publicKey: PublicKeyCredentialRequestOptions}, binary members base64url.

POST /api/passkeys/login/finish

Finish a passkey sign-in

Unauthenticated

Callable with no credential.

Verifies the authenticator's assertion and returns a one-time code, good for two minutes, that only the holder of the PKCE verifier can redeem. A ceremony is answered once, right or wrong.

Request body

application/json

Field Type Notes
session string
credential object The PublicKeyCredential, binary members base64url.

Responses

Status Meaning
200 The one-time code.
400 passkey_ceremony_invalid, passkey_request_invalid or passkey_origin_invalid.
401 passkey_verification_failed.
429 Too many attempts. Password gates, probes and challenges are limited per client, so a slug cannot be brute-forced.

Response fields

Field Type Notes
code string

POST /api/passkeys/register/begin

Start registering a passkey

Unauthenticated

Callable with no credential.

Either a new account — email, where the operator allows signups — or a ticket, which adds a passkey to the account it names. Returns WebAuthn creation options that require a discoverable credential and user verification. Nothing is written until the ceremony finishes.

Request body

application/json

Field Type Notes
email string The new account's address. Ignored with a ticket.
ticket string A one-time ticket from /api/passkeys/tickets or the operator.
name string What the passkey is called in the owner's list.
challenge string The app's PKCE S256 challenge, when the result should sign an app in.

Responses

Status Meaning
200 The ceremony, good for five minutes.
400 passkey_email_invalid, passkey_ticket_invalid, passkey_request_invalid or passkey_origin_invalid.
403 signups_disabled: no ticket, and this server does not accept registrations.
409 passkey_email_taken.
429 Too many attempts. Password gates, probes and challenges are limited per client, so a slug cannot be brute-forced.

Response fields

Field Type Notes
session string
options object {publicKey: PublicKeyCredentialCreationOptions}, binary members base64url.

POST /api/passkeys/register/finish

Finish registering a passkey

Unauthenticated

Callable with no credential.

Verifies the attestation, then writes the passkey — and, for a signup, the account — and spends the ticket. Returns a one-time code when the ceremony was started with a PKCE challenge.

Request body

application/json

Field Type Notes
session string
credential object The PublicKeyCredential, binary members base64url.

Responses

Status Meaning
200 Saved.
400 passkey_ceremony_invalid, passkey_verification_failed, passkey_ticket_invalid or passkey_origin_invalid.
409 passkey_email_taken.
429 Too many attempts. Password gates, probes and challenges are limited per client, so a slug cannot be brute-forced.

Response fields

Field Type Notes
code string Present when a PKCE challenge was given.

POST /api/passkeys/token

Exchange a code for a session (the app)

Unauthenticated

Callable with no credential.

Spends the one-time code — right or wrong, a code works once — and, when the verifier matches its challenge, returns a session token. Not needed for API-key access.

Request body

application/json

Field Type Notes
code string
verifier string The PKCE verifier.

Responses

Status Meaning
200 A session token and the user record.
400 passkey_grant_invalid.
429 Too many attempts. Password gates, probes and challenges are limited per client, so a slug cannot be brute-forced.

Response fields

Field Type Notes
token string
record object

POST /api/passkeys/tickets

Issue a link to add a passkey

Auth: Session

Returns a one-time link, good for fifteen minutes, to the sign-in page where the caller's account can register another passkey — on this device or, opened elsewhere, on another. Users only: an API key cannot let itself in as the person behind it.

Responses

Status Meaning
200 The link.
401 Refused, with a named reason.

Response fields

Field Type Notes
url string
expiresAt string

DELETE /api/passkeys/{id}

Remove a passkey

Auth: Session

Removes one of the caller's passkeys; it stops signing in at once. The last one cannot be removed. Passkeys are listed through /api/collections/passkeys/records (name, created, last used — never the key).

Parameters

Name In Required Notes
id path yes

Responses

Status Meaning
204 Removed.
401 Refused, with a named reason.
404 No such resource, or the caller may not see it.
409 passkey_last.