{BASE} = https://authentica-merge-integration.t2.sa. Ask what happened to a verification. Idempotent, read-only, and safe to poll.

Why this is the whole result channel

Nothing is delivered to your server. The browser’s return to your returnUrl carries an identifier and your own state — neither proves anything — so this call is where the outcome actually comes from. It is also what makes the redirect trustworthy: because you re-read the result over an authenticated channel, a forged or replayed return URL buys an attacker nothing. Three situations all resolve here:
  • The normal path. The browser lands on your returnUrl; your server reads GET /session/{id} and acts on the answer.
  • The verification failed. The browser lands on your returnUrl exactly as it does on success — same query, nothing to distinguish the two. This call is what tells them apart. See When it fails.
  • The browser never came back. The user closed the tab, or lost connectivity. No redirect ever happens. A reconciliation sweep over your pending actions reads the outcome here.
Every terminal outcome is retained for 24 hours under its sessionId.

The sessionId

POST /session returns sessionId (for example vs_9f3c8a…). It is public and non-secret — safe to log, safe to store, safe to show on an internal admin screen. It is not a capability: reading a session requires your Basic credentials, and a session created by another client is invisible to you. Store it against the pending action at the moment you create the session, next to state.

Response

status values

pending also covers “we have never heard of this session id”, and an id belonging to a different client_id. There is no 404 here — never treat pending as proof that a session exists. Trust your own record for that.

When it fails [#when-it-fails]

A verification that ends badly does not strand the user on our page. It sends them back to your returnUrl with the same session_id and state a successful one carries — because a user whose code locked out still needs a way back into your app, and you still need to close out the pending action rather than leave it hanging until it ages out. That means your landing route cannot assume success. Two arrivals that look identical in the URL can mean opposite things; only this endpoint distinguishes them.
The redirect carries no error code, deliberately. Anyone can craft that URL, so a ?error= in it would prove nothing — exactly as session_id alone proves nothing on the success path. status here is the authoritative answer, and reason is only ever a message to show the user.

reason values

New values may be added over time. Branch on status and treat an unrecognised reason as a generic failure rather than an error.

Handling both outcomes

Before you act on verified

Three things have to agree, every time:
  1. The state in the response equals the state on your pending record.
  2. That record is one you created, and it is not already fulfilled.
  3. The userRef in the response is the user you started the verification for.
Only then run fulfilment — and make it idempotent, because the landing page can be reloaded.

curl

Errors

How to poll without being silly

1

Read once on the landing page

The common case is a single read: the browser arrives, you call this endpoint, you get a terminal status. No loop needed.
2

Back off if you do loop

A verification takes as long as a human takes to read an SMS. Poll at a few seconds, then back off; do not poll in a tight loop for the whole session lifetime.
3

Stop at a terminal state

verified, failed and expired never change afterwards. Stop.
4

Sweep the stragglers

For sessions whose browser never returned, a periodic sweep over your own pending records — one read each — closes the gap inside the 24 h window.