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.