End-User Field & Endpoint Matrix
This is the single reference for which end-user attributes Zyphr stores, which endpoint returns each one, and at which credential scope. If you are trying to determine whether a field exists — or where to read it from — start here rather than probing endpoints.
Credential scopes
Every row in the matrix is tagged with the scope that can read (or write) it. There are three distinct credentials in Auth-as-a-Service, and they are not interchangeable:
| Scope | Credential | Runs | Reads |
|---|---|---|---|
| End-user token | X-Application-Key (za_live_pub_* / za_test_pub_*) + Authorization: Bearer <access_token> | Browser / mobile / any client | The signed-in user's own profile and sign-in state |
| Application key/secret | X-Application-Key (za_live_pub_*) + X-Application-Secret (za_live_sec_*) | Server-side only | Any user in the authenticating application (admin scope) |
| Dashboard JWT | Authorization: Bearer <dashboard JWT> | The Zyphr dashboard UI | Account-scoped management. Not for integrators — this is what our own dashboard uses. |
X-Application-Secret and the dashboard JWT are server-side only. Client apps
use the publishable key (za_live_pub_* / za_test_pub_*) plus the end user's own access token — never
the secret. See Client-side Auth for the credential
boundary.
Attribute matrix
Scope uses the abbreviations above: app-key = application key/secret
(server-side admin), user-token = end-user token (self).
| Attribute | Endpoint(s) | Scope | Read/Write |
|---|---|---|---|
id | GET /auth/users/:id, GET /auth/users/me | app-key, user-token | Read |
email | GET /auth/users/:id, GET /auth/users/me, PATCH /auth/users/:id | app-key, user-token | Read; write (app-key PATCH) |
name | GET /auth/users/:id, GET /auth/users/me | app-key, user-token | Read; write (PATCH /auth/users/me, app-key PATCH) |
avatar_url | GET /auth/users/:id, GET /auth/users/me | app-key, user-token | Read; write (PATCH /auth/users/me, app-key PATCH) |
status | GET /auth/users/:id | app-key | Read; write (app-key PATCH, active/suspended) |
is_anonymous | GET /auth/users/:id | app-key | Read |
metadata | GET /auth/users/:id, GET /auth/users/me | app-key, user-token | Read; write (PATCH /auth/users/me, app-key PATCH) |
email_verified | GET /auth/users/:id, GET /auth/users/:id/auth-methods, GET /auth/users/me | app-key, user-token | Read |
phone_verified | GET /auth/users/:id, GET /auth/users/:id/auth-methods | app-key | Read |
mfa_enabled | GET /auth/users/:id, GET /auth/users/:id/auth-methods | app-key | Read |
first_activity_at | GET /auth/users/:id | app-key | Read |
last_login_at | GET /auth/users/:id, GET /auth/users/me | app-key, user-token | Read |
created_at | GET /auth/users/:id, GET /auth/users/me | app-key, user-token | Read |
updated_at | GET /auth/users/:id | app-key | Read |
has_password | GET /auth/users/:id/auth-methods | app-key | Read |
oauth_providers | GET /auth/users/:id/auth-methods, GET /auth/oauth/connections | app-key; user-token (self) | Read |
passkeys (count) | GET /auth/users/:id/auth-methods | app-key | Read |
Notes
GET /auth/users/:idis the application-key-scoped directory lookup (the SDK'sgetEndUserById). It returns the core profile and verification flags but nothas_password,oauth_providers, or the passkey count — those live on the sign-in-methods endpoint below.GET /auth/users/meis the self endpoint (end-user token). It returns the subset of profile fields the user is allowed to see about themselves. It does not returnstatus,is_anonymous, orupdated_at.metadatais not trusted for authorization. It is writable by the end user through their own token. For authorization data use trusted custom claims.
Sign-in methods: GET /auth/users/:id/auth-methods
The single, application-key-scoped endpoint that answers "how can this user sign in?" — everything you need to build a sign-in methods settings screen without stitching together several calls. It returns:
{
"data": {
"has_password": true,
"oauth_providers": ["google", "apple"],
"mfa_enabled": false,
"email_verified": true,
"phone_verified": false,
"passkeys": 2
}
}
| Field | Type | Meaning |
|---|---|---|
has_password | boolean | User has a password credential (false for OAuth-only / passwordless users) |
oauth_providers | string[] | Distinct linked OAuth providers (e.g. google, apple) |
mfa_enabled | boolean | Multi-factor authentication is enabled |
email_verified | boolean | Email address is verified |
phone_verified | boolean | Phone number is verified |
passkeys | integer | Number of registered WebAuthn passkeys |
curl https://api.zyphr.dev/v1/auth/users/USER_ID/auth-methods \
-H "X-Application-Key: za_live_pub_xxxx" \
-H "X-Application-Secret: za_live_sec_xxxx"
has_password, oauth_providers, and the passkey count are only hereThese three are not on GET /auth/users/:id. If you need any of them, call
auth-methods. An end user can read their own linked providers via the
self-scoped GET /auth/oauth/connections (end-user token) — see OAuth.
Application self-metadata: GET / PATCH /auth/application
Application-secret-scoped read and write of the authenticating application's
own metadata — both GET and PATCH require X-Application-Key and
X-Application-Secret. GET returns it; PATCH sets the test_recipients
allowlist. Both are scoped to the calling application via its credentials — there is
no application id in the path or body, so there is no cross-tenant reach.
{
"data": {
"id": "app_abc123",
"name": "My SaaS App",
"environment": "live",
"signing_algorithm": "HS256",
"test_recipients": ["qa@example.com", "dev@example.com"]
}
}
| Field | Read | Write | Meaning |
|---|---|---|---|
id | ✓ | — | The application ID |
name | ✓ | — | The application's display name |
environment | ✓ | — | live or test (from the credentials that authenticated the request); null for legacy application-level keys |
signing_algorithm | ✓ | — | HS256 (default), RS256, or ES256 — the algorithm your end-user tokens are signed with |
test_recipients | ✓ | ✓ (PATCH) | The auth-email allowlist — see below |
# Read your application's metadata (including the current allowlist)
curl https://api.zyphr.dev/v1/auth/application \
-H "X-Application-Key: za_live_pub_xxxx" \
-H "X-Application-Secret: za_live_sec_xxxx"
signing_algorithm is the authoritative way to check whether your app issues
symmetric (HS256) or asymmetric (RS256/ES256) tokens. Do not infer it from
the presence of a JWKS — see Verifying tokens & JWKS.
Setting the auth-email allowlist from config: PATCH /auth/application
test_recipients is the per-application auth-email allowlist. When non-empty,
it gates all auth emails — only the listed addresses receive them, which is how
you keep test/staging apps from mailing real users. Historically this was set by
clicking through the dashboard; it is now settable directly from your own
infrastructure config with a single application-secret-scoped PATCH:
# Set the allowlist from your deploy pipeline / IaC (application secret required)
curl -X PATCH https://api.zyphr.dev/v1/auth/application \
-H "X-Application-Key: za_live_pub_xxxx" \
-H "X-Application-Secret: za_live_sec_xxxx" \
-H "Content-Type: application/json" \
-d '{ "test_recipients": ["qa@example.com", "dev@example.com"] }'
# Pass an EMPTY array to clear the allowlist and disable the gate
curl -X PATCH https://api.zyphr.dev/v1/auth/application \
-H "X-Application-Key: za_live_pub_xxxx" \
-H "X-Application-Secret: za_live_sec_xxxx" \
-H "Content-Type: application/json" \
-d '{ "test_recipients": [] }'
The PATCH response is identical to GET /auth/application, so you can read
back exactly what you set. Notes:
- Application-SECRET scoped — requires
X-Application-Secret, so this is a server-side / CI operation, never a client one. GETandPATCHare symmetric —test_recipientsnow appears in theGETresponse too, so config-management tooling can diff desired vs. actual state.- An empty array clears the allowlist (disables the gate). A malformed email,
a non-array body, or a non-string entry returns
400 validation_error. - This is the same allowlist the dashboard manages and that gates the auth-email test send endpoint — see Test send allowlist.
Because the allowlist is now a single secret-scoped PATCH, you can commit it to
your deploy pipeline and set it per environment — no dashboard clicks. For a
staging app, list only your QA addresses; for production, send an empty array to
turn the gate off.
Required credential by endpoint
Which credential each end-user endpoint requires. Use this to design your client/server split up front instead of discovering it per 401. The three credentials (see Credential scopes above):
- publishable —
X-Application-Keyonly (za_*_pub_*). Safe for browser / mobile. - secret —
X-Application-Key+X-Application-Secret(za_*_sec_*). Server-side only. - + end-user token — additionally requires
Authorization: Bearer <access_token>(the signed-in user acting on themselves).
Endpoints marked secret (login, register, magic-link, phone, email-OTP send/verify,
the user directory, claims) must run from your backend. refresh is the exception —
it is publishable, because the refresh token is the credential, so client apps can
keep a session alive without a backend. This table is derived from the route middleware
and reflects the shipped behavior.
Sessions & tokens
| Endpoint | Required credential |
|---|---|
POST /auth/users/login | secret |
POST /auth/users/register | secret |
POST /auth/sessions/refresh | publishable |
POST /auth/sessions/revoke | secret |
POST /auth/sessions/revoke-all | secret + end-user token |
GET /auth/sessions | publishable + end-user token |
Profile (self)
| Endpoint | Required credential |
|---|---|
GET /auth/users/me | publishable + end-user token |
PATCH /auth/users/me | publishable + end-user token |
DELETE /auth/users/me | publishable + end-user token |
POST /auth/users/change-password | publishable + end-user token |
GET /auth/password-requirements | publishable |
Directory & claims (admin, server-side)
| Endpoint | Required credential |
|---|---|
GET /auth/users, GET /auth/users/by-email, GET /auth/users/:id | secret |
GET /auth/users/:id/auth-methods | secret |
PATCH /auth/users/:id, DELETE /auth/users/:id | secret |
POST /auth/users/invite | secret |
GET / PUT /auth/users/:id/claims | secret |
Passwordless & alternate methods
| Endpoint | Required credential |
|---|---|
POST /auth/magic-link/send, /magic-link/verify | secret |
POST /auth/forgot-password, /reset-password, /validate-reset-token | secret |
POST /auth/verify-email/send, /verify-email/resend | secret + end-user token |
POST /auth/verify-email/confirm | secret |
POST /auth/phone/{register,login}/{send,verify} | secret |
POST /auth/email-otp/{register,login}/{send,verify} | secret |
GET /auth/phone/available, /email-otp/available | publishable |
POST /auth/users/anonymous, /game-center, /google-play-games | publishable |
POST /auth/users/convert | publishable + end-user token |
OAuth & WebAuthn
| Endpoint | Required credential |
|---|---|
GET /auth/oauth/providers, /oauth/:provider/authorize | publishable |
GET /auth/oauth/connections, DELETE /auth/oauth/:provider | publishable + end-user token |
GET /auth/oauth/tokens/:provider, POST /oauth/tokens/:provider/refresh | secret |
GET /auth/webauthn/credentials, /webauthn/status | publishable + end-user token |
POST /auth/webauthn/registration/* | secret + end-user token |
POST /auth/webauthn/authentication/* | secret |
See also
- End User Authentication — full registration, login, session, and profile API
- Managing End Users — server-side directory, invites, and trusted custom claims
- Verifying tokens & JWKS — per-algorithm claim sets and offline verification