Skip to content

HTTP endpoints

method path what
GET /.well-known/openid-configuration discovery
GET /oidc/jwks public keys for verifying an id_token
GET /oidc/authorize start a sign-in
POST /oidc/token exchange a code
GET /oidc/userinfo claims for a bearer token
POST /oidc/register register a client (open, rate-limited); see Registering a site
GET /developers the registration form
GET /oidc/logout end the session; post_logout_redirect_uri only if registered for the client
POST /oauth/revoke revoke a token
POST /oauth/introspect is this token still live
POST /auth/challenge issue a nonce for a direct sign-in
POST /auth/verify spend a signed challenge, open a session
GET /button.js the drop-in; not ready for sign-in yet
GET /engine/*, /sdk/* the engine and the SDK

Sign-in on the standard door

Every /accounts/* call names a client registered for the standard door: client_id in the query or body, or the session behind the bearer. Without one the answer is 400 unknown_client; from an origin the client did not register, 403 origin_not_allowed. Bearer means the site session from a sign-in. Bodies are JSON, 16 KB at most.

Passwords never travel plain. The browser salts and hashes them first (PBKDF2, salt from /accounts/salt); the server takes only that hash and answers 400 invalid_password to anything else. The sign-in engine (/engine/*) does this for you.

Every call that signs someone in answers the same shape: {token, refresh_token, expires_in, account}. token is the Bearer; it lasts 15 minutes, the refresh token 30 days.

method path auth what
POST /accounts/salt none, or Bearer Send {email} or {phone}. Answers {salt} for the password hash; signed in with no contact, the account's own
GET /accounts/username?username=… none {available, message?}; 60 checks per IP per 15 minutes
POST /accounts/signup none Send {email or phone, username?, password?}. New account, signed in, and a code sent to verify the contact; 409 username_taken
POST /accounts/verify Bearer Send {code}. The contact is now the account's; 409 contact_taken if someone verified it first
POST /accounts/verify/resend Bearer {sent: true}
POST /accounts/login none Send {email or phone, password}. Signed in, or 401 bad_login
POST /accounts/code/start none Send {email or phone}. Answers {sent: true}, account or not
POST /accounts/code/finish none Send {email or phone, code}. Signed in; a new account if nobody holds that contact
POST /accounts/refresh none Send {refresh_token}. New tokens; a reused refresh token ends the session (401 signed_out)
POST /accounts/reauth/start Bearer a code to the account's contact, for re-auth
POST /accounts/password Bearer + re-auth Send {password}. Set or change it; every other session ends, {ok, revoked}
POST /accounts/username Bearer Send {username}. Change it; 409 username_taken
POST /accounts/contact/change/start Bearer + re-auth Send {email or phone}. A code to the new address; a notice to the old
POST /accounts/contact/change/finish Bearer Send {code, password?, new_password?}. The contact moves, every session ends, fresh tokens come back

/accounts/password/change is the same call as /accounts/password. Re-auth is a sign-in in the last 10 minutes, or reauth: {password} or reauth: {code} in the body; otherwise 401 reauth_required. A password account changing its contact sends the password twice: password salted as it is now, new_password salted with the new address.

Codes last 10 minutes, 5 tries each. Sends: 5 per address and 20 per IP; sign-ins 50 per IP; code tries 30 per IP; all per 15 minutes, then 429. After 3 wrong answers the replies slow (up to 4 seconds); after 10 that address or account locks for the window (429). Sign-up, code sign-in, resend and contact change answer the same whether or not the address already has an account, so none of them says who is registered.

Passkeys and the Omxus key (/accounts/passkey/*, /accounts/passkeys, /accounts/omxus/*): see Passkeys.

Profiles and pictures

Bodies, limits and errors: Profiles and claims.

method path auth what
GET /accounts/me Bearer the signed-in account
GET /accounts/profile Bearer their profile, default picture filled in
POST /accounts/profile Bearer change any of display_name, bio, avatar; null clears one
POST /accounts/avatar/upload Bearer upload a photo (data URL, 64 KB) and make it the picture
GET /accounts/avatars?client_id=…&letters=TA none all sixty drawn pictures
GET /accounts/settings?client_id=… none the hosted settings screen (HTML; 404 unknown_client without a valid id)
GET /avatar/<style>-<palette>.svg none a drawn picture; the name is the picture
GET /avatar/u/<id>.png (or .jpg, .webp) none an uploaded photo
GET /picture/<opaque> none 302 to the person's current picture

Sessions, activity and the account

Bodies, re-auth and who sees what: Sessions, activity and the account.

method path auth what
GET /accounts/sessions Bearer live sign-ins; other sites collapse to one count
POST /accounts/sessions/revoke Bearer {id}, {others: true} or {all: true}
POST /accounts/logout Bearer end this session
GET /accounts/activity Bearer up to 200 events, last 90 days
GET, POST /accounts/export Bearer + re-auth everything held about the account
POST /accounts/delete Bearer + re-auth delete the account
GET /accounts/connections Bearer, Omxus Account only every site signed in to
POST /accounts/connections/revoke Bearer, Omxus Account only {site}: leave that site
GET /accounts/usernames Bearer, Omxus Account only shared and per-site usernames
GET /accounts/checkup Bearer, Omxus Account only security checkup

Operator

Not for sites: they need the provider's operator secret and refuse browsers. Every one is POST with a JSON body. Details in Operator routes.

method path auth what
POST /accounts/operator/lookup operator secret find an account by id, email or phone
POST /accounts/operator/disable operator secret block sign-in, end every session
POST /accounts/operator/enable operator secret lift a disable
POST /accounts/operator/force-reset operator secret remove the password, end every session
POST /accounts/operator/export operator secret everything held, no hashes or tokens
POST /accounts/operator/delete operator secret delete the account
POST /accounts/operator/sector operator secret give up to 50 clients one shared sub
POST /accounts/operator/sector/approve operator secret check and approve a pending sector_identifier_uri

Vouching

Trust, vouch codes and the chain. Bodies, answers and errors in Vouching and sybil resistance.

method path auth what
GET /vouch/code session cookie your vouch code
GET /vouch/resolve?code=… session cookie a vouch code back to an address and name
POST /vouch/submit the signature a signed vouch
GET /vouch/graph session cookie your score, who vouched you, who you vouched
GET /vouch/chain?address=0x… none the HER contract's record
GET /network/pulse none the network in numbers and as a graph

Checked against the live discovery document, server/router.js, server/accounts/accounts-routes.js and server/vouch/vouch-routes.js, 30 Oct 2026.