Nothing else is in the URL. No verdict, no risk level, no reasons, no document 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.
What to put in subject_id
Because kyc_id travels in a URL, it will appear in browser history, in your web server’s access log and in any proxy or CDN log between the person and you. Make subject_id an opaque reference - an id from your own system that means nothing outside it, such as kyc_t5l7ogyk9m8v09mj - and never a name, an email address, a phone number, a national id number or anything else that identifies the person on its own.
Two rules apply, at two moments:
- At open, the value is bounded the way
stateis: non-empty, at most 200 characters, no control characters. Anything else is refused with422 SUBJECT_TOO_LONGor422 SUBJECT_INVALID, on the hosted and the server-to-server path alike. Nothing narrower is refused here - a name with a space or an email address still opens a session - so references your integration already uses keep working. - At the redirect,
kyc_idis sent only when the stored value is an opaque URL token: ASCII letters, digits,-,_,.and~, nothing else, and not a run of eight or more digits alone. A value outside that -jane.doe@example.com,Jane Doe,+2348012345678,Doe,Jane,acct/42, a non-ASCII letter,12345678901- opens and completes normally but is not put in the URL; the redirect then carriescode,session_idandstateonly, the exchange still names the subject, and theRESULT_CODE_ISSUEDaudit event carrieskyc_id_omittedwithNOT_OPAQUE,DIGITS_ONLY, orTOO_LONG/EMPTY/CONTROL_CHARACTERfor a session stored before the open-time rule.
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 behttps with a host and no credentials or #fragment; a deployment running a development profile also accepts http://localhost and http://127.0.0.1.
Query parameters of your own are kept, in the order you wrote them, and Esectra’s four follow. Your URL may not name code, session_id, kyc_id or state itself, in any letter case or percent-encoded spelling: saving or publishing such a flow is refused with 422 INVALID_FLOW and a message naming the parameter, so the redirect can never carry two values for one name or a value you set where your server expects Esectra’s.
Exchange the code
Your server receives the redirect and exchanges the code under its API key. The answer names the session’ssubject_id; check it against the kyc_id you were redirected with before you act on the result, and treat a mismatch as a request to ignore:
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 - and nothing here depends on a webhook having arrived:status: "PENDING" with no decision. A completion that fails on the server - the session could not be marked finished - issues no code and sends the person nowhere; the page stays on its own screen.
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; RESULT_CODE_ISSUED carries kyc_id_omitted when the redirect went out without a kyc_id. A completion that could not be given a redirect at all - the frozen return URL no longer passes this deployment’s rules, or the code could not be stored - is RETURN_REDIRECT_NOT_ISSUED with the cause; the person still sees the finished screen, and the result is readable by session id. The code itself is stored only as a hash and appears in no log or audit event.
A return URL published before code, session_id, kyc_id and state were reserved keeps working: the stale parameter is dropped and the server’s value sent, each name exactly once. It cannot be re-saved until the parameter is removed.
