Skip to main content
A published flow may carry a return URL. When a hosted session under that flow finishes, the completion screen still says only that the submission is done - and offers a Return button. The button leads to your URL with three query parameters: Nothing else is in the URL. No verdict, no personal data, no API key, and never the capture token - the code is minted separately, after the session has concluded, and the two have nothing in common.

Configure the return URL

The URL belongs to the flow, set by an organisation admin, and is frozen into each session when it opens. Nothing in the capture link, the query string or the person’s browser can change where they are sent. It must be https with a host and no credentials or #fragment; a deployment running a development profile also accepts http://localhost and http://127.0.0.1. Existing query parameters on your URL are kept; code, session_id and state are appended.

Exchange the code

Your server receives the redirect and exchanges the code under its API key:
The answer is the session’s result:
Reasons and check details are worded from fixed vocabularies keyed on stable codes - the same wording Control shows. No score, model name or threshold is ever in them; a stored code the API does not describe is reported as UNSPECIFIED. The exchange is atomic and single-use: the code is bound to your tenant, spent by the request that wins, and refused afterwards. Refusals are 404 RESULT_CODE_INVALID (no such code for your tenant - another tenant’s code looks exactly like this), 409 RESULT_CODE_CONSUMED (already exchanged) and 410 RESULT_CODE_EXPIRED. If a store fails after the code has been spent, the answer is 503 RESULT_UNAVAILABLE_AFTER_EXCHANGE and its message names the session id to read instead; the code is not given back.

Read the result without the code

The code is a pointer, not the only way in. Read the same result at any time by session id with your API key, so a lost exchange response or a missed webhook costs nothing:
A session that has not finished reads as status: "PENDING" with no decision.

What is recorded

Issuing, exchanging and refusing a code are audit events (RESULT_CODE_ISSUED, RESULT_CODE_EXCHANGED, RESULT_CODE_REFUSED) that name the session and the outcome. The code itself is stored only as a hash and appears in no log or audit event.