Skip to main content
Two events tell you the outcome of a login without waiting for the browser to come back. They use the same webhook system as the rest of sajn: the same event body, Standard Webhooks signing, retries, and delivery log. To receive them, add login.completed and login.failed to the events of a webhook. For setup, see Webhooks.

Why you might want them

The normal flow is synchronous: the user is redirected back and you exchange the code. Webhooks matter for the case that redirect never arrives. The common one is mobile. A user opens the login in an in-app browser, authenticates, and closes the browser before it redirects. They believe they logged in; your app never heard anything. sajn completes the authentication server-side regardless — a background watchdog finishes the flow if the browser stops responding — so the result exists. The webhook is how you learn about it.
If you use OpenID Connect, a login.completed webhook can’t be exchanged for an id_token — the authorization code goes to the browser and expires in 60 seconds. Treat the webhook as a signal to prompt the user to try again, or to reconcile your own records. With the Session API you can act on it directly: GET the session for the claims.

login.completed

The fields of data.object:
The payload deliberately contains no identity. No name, no personnummer — only the pairwise sub. A webhook body lands in your logs and every proxy between us, so the claims stay behind an authenticated read.

login.failed

data.object has the same sessionId, clientId, mode, and state as login.completed, plus reason, the BankID hint code, and failedAt. This fires only for terminal failures. Recoverable ones — the user cancelled, the QR failed to scan, the app was not found — do not fire, because the login page offers a retry within the same session and notifying on each would flood your endpoint. Typical reason values are expiredTransaction and certificateErr. For the full list, see Errors.

Delivery

Login events are delivered like every other sajn event: signed according to Standard Webhooks, retried with backoff, and visible in the delivery log under Inställningar → Utvecklare → Webhooks. Two rules worth repeating:
  • Verify the signature with the webhook-id, webhook-timestamp, and webhook-signature headers before you act on an event. See Verify signatures.
  • Expect duplicates. Deliveries are at-least-once. The event id is stable per event, and data.object.id on login.completed is stable per authentication. Use either as an idempotency key.
Never treat a webhook as an authentication in itself. It says an authentication happened; it is not a credential and does not establish a session for whoever is holding a browser. Bind the session through the redirect, or through the Session API read.