Authentication
Every request except sign-up, sign-in, and a handful of public preview endpoints carries a bearer access token. Six independent entry paths mint one — pick whichever fits your client.
The header
Authorization: Bearer <accessToken>
The decoded payload carries userId, householdId, profileId, and sessionId — every household-scoped query in this API filters by the token's householdId, never a client-supplied one.
Token lifetimes
JWT_ACCESS_TTLJWT_REFRESH_TTLJWT_ANON_TTL_SECONDSSTEP_UP_TTL_SECONDSSign up
/auth/signupno authCreates a new household and its first profile. Consent capture is mandatory in the same transaction — there is no account state that predates a recorded consent.
emailstringrequiredUnique per account.
passwordstringrequiredMinimum 10 characters.
namestringrequiredDisplay name for the first profile.
regionstringrequiredISO country code — selects the locale pack (doc types, lead times).
consentobjectrequired{ version, locale } — captured in the same transaction as account creation.
claimTokenstringoptionalAn anon session's access token — converts a pre-account session into this new account.
inviteTokenstringoptionalJoin an existing household as a family member instead of creating a new one.
referralCodestringoptionalCredits both households once this account uploads its first document.
curl https://api.dociya.com/auth/signup \
-H "Content-Type: application/json" \
-d '{
"email": "priya@example.com",
"password": "correct-horse-battery-staple",
"name": "Priya",
"region": "us",
"consent": { "version": "2026-01-01", "locale": "en" }
}'Log in
/auth/loginno authPassword sign-in for an existing account.
emailstringrequiredpasswordstringrequiredcurl https://api.dociya.com/auth/login \
-H "Content-Type: application/json" \
-d '{ "email": "priya@example.com", "password": "correct-horse-battery-staple" }'Request a magic link
/auth/magic-link/requestno authSends a single-use sign-in link, valid for 15 minutes. Exchange it with POST /auth/magic-link/verify (body: { token }) for the same { accessToken, refreshToken } pair signup and login return.
emailstringrequiredcurl https://api.dociya.com/auth/magic-link/request \
-H "Content-Type: application/json" \
-d '{ "email": "priya@example.com" }'Refresh a session
/auth/refreshno authRotates both tokens — the old refresh token stops working the moment a new pair is issued. Web clients never see the refresh token in JS; it's set and read entirely via the httpOnly cookie, which this endpoint re-seats.
refreshTokenstringoptionalOmit on web — the refresh token travels in an httpOnly cookie (dociya_rt, path /auth) instead.
curl https://api.dociya.com/auth/refresh \
-H "Content-Type: application/json" \
-d '{ "refreshToken": "8f2c1a9e..." }'Step up for a sensitive reveal
/auth/step-upRe-verifies the caller's password (or, via POST /auth/webauthn/assert/verify, a passkey) and mints a short-lived token required by the two field-reveal endpoints under /documents — the only routes in this API gated by more than a bearer token alone.
stepUpTokenstringoptionalPass as X-Step-Up-Token on the reveal endpoint.
expiresInnumberoptionalAlways 120 (seconds).
curl https://api.dociya.com/auth/step-up \
-H "Authorization: Bearer $DOCIYA_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "password": "correct-horse-battery-staple" }'Other ways in
POST /auth/oauthSign in with an Apple or Google ID token, verified against the provider's JWKS.POST /auth/phone-otp/request → verifySMS one-time code — off by default, feature-flagged per deployment.POST /auth/anonMint a 24h provisional session before any account exists, so a first document can be captured pre-signup. Pass the resulting token back as claimToken on signup or login.GET /auth/devices · DELETE /auth/devices/:idList or revoke sessions (device management).POST /auth/logoutRevoke the current session and clear the refresh cookie.