La documentación se publica en inglés.

DocumentationSign in

How the flow works

authorize → consent → code → token → userinfo, and what each step is actually responsible for.

Five steps

  1. 1

    authorize

    Send the person to GET /v1/oauth/authorize. It redirects to the hosted flow and forwards explicit prompt, max_age and login_hint values. With no prompt, a sufficient existing session and unchanged permissions continue automatically; otherwise authentication or consent is shown.

  2. 2

    consent

    UnifyID signs the person in. The first time, it shows them the concrete release — the claim names, each identity document by name, how many contacts leave — with your purpose above it, and approving is a single decision. Someone already connected sees *Continue as* instead, or only the permissions you added. Cancelling returns access_denied.

  3. 3

    code

    An authorization code arrives at your registered redirect_uri, with the state you sent. response_types_supported is ["code"]: there is no other response type.

  4. 4

    token

    POST /v1/oauth/token exchanges the code, with your PKCE verifier (required of every client) and your client authentication, for an access token and — when openid was granted — an RS256 ID token that says whether this was the person's first sign-in to you.

  5. 5

    userinfo

    GET /v1/userinfo returns a flat map of the claim values the person approved and the platform holds.

The discovery document is the contract

Do not hard-code endpoints from a page you read once. GET /.well-known/openid-configuration returns issuer, authorization_endpoint, token_endpoint, userinfo_endpoint and jwks_uri, and your application's quickstart returns the same set under endpoints.

Recorded from the running API
{
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code"],
  "subject_types_supported": ["pairwise"],
  "id_token_signing_alg_values_supported": ["RS256"],
  "token_endpoint_auth_methods_supported": [
    "client_secret_basic", "client_secret_post", "none"
  ],
  "code_challenge_methods_supported": ["S256"],
  "prompt_values_supported": ["none", "login", "consent", "select_account"]
}

The JWKS answers with RSA keys carrying kid, use: "sig" and alg: "RS256". Select by kid, and re-fetch when you meet one you do not know.

Your one sentence

purpose is the only text you write that the person reads at the moment they decide. It is one string, at most 200 characters, stored on the consent record. If you send nothing, the screen says Sign in to <your application name>.

There is no per-claim reason field. You cannot annotate date_of_birth with why you want it on the screen. One request, one sentence.

First sign-in or returning

The first approval creates a connection. Later sign-ins reuse the active consent unless more permissions are requested. With a sufficient session and unchanged permissions, continuation is automatic. Authentication is requested when needed, and each completed sign-in is recorded.

What the person sees

first time
The whole consent: the concrete release with your purpose above it.
returning, nothing new
Continue automatically when the session is sufficient. Explicit requests to authenticate, select an account or show consent are respected.
returning, more asked for
Only the permissions you added, with what they already share named beside them. Approving adds; it never removes a scope they approved before.
returning, less asked for
Treated as nothing new. The grant stays as it is; the token carries the scopes you asked for, intersected with it.

How you tell them apart

first_sign_inID token
true on the sign-in that created the connection, false on every later one. A person who disconnected you and connects again is true again.
consent_idID token
The consent backing this sign-in. Bind your application session to it so a delayed revocation of an older consent cannot end a newer connection.
connected_atID token
ISO 8601: when the connection was made, from the person's first approval, however many times what they share changed since.
sign_in.completedwebhook
Every completed sign-in, with data.firstSignIn. See Revocation and webhooks.
connection.createdwebhook
Sent when a connection is created, including reconnection after withdrawal.

prompt, max_age and login_hint

Three OpenID Connect parameters authorize reads. Anything else you send is forwarded and ignored.

no promptdefault
Reuse a sufficient session and active consent. Ask for authentication when needed and consent for new permissions.
prompt=login
Explicitly request fresh authentication even when a UnifyID session exists.
prompt=select_account
The sign-in page opens on *Use another account*, so somebody sharing a device can switch.
prompt=consent
The whole consent is shown again, even to somebody already connected.
prompt=none
No screen at all. A person who is signed in with a passkey, already connected with every scope you asked for and — if you set max_age — recent enough gets a code. Everyone else is sent back to your redirect_uri with error=login_required, consent_required or interaction_required and your state. none cannot be combined with another value.
max_age
Seconds. With prompt=none, the person's last proof (auth_time) must be newer than this or you get login_required. auth_time is the most recent of their sign-in, a passkey re-check and the confirmation an application sign-in asks for.
login_hint
The address you believe the person uses. It prefills the sign-in form; it decides nothing.

A private sign-in application cannot send max_age, which is refused with invalid_request, and its login_hint is dropped. See Private sign-in.

Subjects are pairwise

subject_types_supported is ["pairwise"]. The sub is derived per client id. Your staging application and your production application see different sub values for the same human being, and so do two products you own.

Key your user records on it anyway — just do not treat it as a UnifyID-wide person id, and do not try to join two applications on it.

A private sign-in application's sub is per client id too, and never equal to a pairwise one. It belongs to the person rather than to their UnifyID account: in production it stays the same after they delete their account and register again with the same identity document, where a standard application's sub would be new. See Private sign-in.

What this does not do

  • Automatic continuation reuses approval. It requires an adequate session and existing permission for every requested scope. A first connection, withdrawn permission or additional scope requires consent. Explicit prompt values are respected.
  • No refresh grant is advertised. The discovery document lists authorization_code alone.
  • The token is bound to the consent, not just to the clock. Read Revocation and webhooks before you decide how long to cache a claim.

Verified against API commit 234dba22 on 2026-10-18. The routes, scopes, checklist, events and limits on these pages are read from that export, not typed.