Returning to your app (callbackUri)
In the account opening and portability journeys, the holder leaves your app, completes the facial capture (or the authorization) on a page of ours and, at the end, used to be left on a CorpX completion screen. The optional callbackUri field closes that loop: when the step is finished, the holder is taken back to your app or site, and the outcome travels in the URI itself.
It applies to both flows, because both come in through the same endpoint:
- PF account opening (
POST /v1/accreditations/pf) and PJ (POST /v1/accreditations/pj); - automatic portability, which uses the same
POST /v1/accreditations/pf.
Prerequisite: register the URI
The URI must be registered in advance for your tenant. Talk to CorpX support telling them exactly which URI (or URIs) you are going to use — our team is the one who registers it.
The comparison is exact. If you register https://app.yourcompany.com/onboarding/done, that is the string you must send; we only append the outcome parameters at the end. There is no wildcard and no prefix matching: https://app.yourcompany.com/onboarding/done/extra is a different URI and needs its own registration.
Sending a URI that is not registered refuses the creation with 400 invalid_callback_uri — you find out right away, and not after the holder has burned the whole journey.
What is accepted
https:// | https://app.yourcompany.com/onboarding/done |
| Your application's scheme (deeplink) | yourcompany://onboarding/done |
What is rejected
http://without TLS, and any scheme the browser executes or reads from disk (javascript:,data:,file:,blob:);- embedded credentials (
https://user:password@...); localhost, internal network addresses (10.x,192.168.x,172.16–31.x,169.254.x,::1,fd00::/8) and local suffixes (.local,.internal);- a literal IP address over
https— use a domain name; - a fragment (
#), because it would end the URI before the parameters; - a host with no domain (
https://intranet/).
How to send it
curl -X POST "https://tenant.api.corpx.com/v1/accreditations/pf" \
-H "Authorization: Bearer {token}" \
-H "X-Tenant-Id: tenant-yourcompany" \
-H "Content-Type: application/json" \
-d '{
"person": { "...": "..." },
"address": { "...": "..." },
"callbackUri": "https://app.yourcompany.com/onboarding/done"
}'
The field is optional. Without it, nothing changes: the holder finishes on the CorpX completion page, as before.
The accepted URI is also returned in callbackUri in the POST response and in GET /v1/accreditations/{id}.
Parameters that arrive on the way back
| Parameter | When | Description |
|---|---|---|
accreditationId | Always | The same accreditationId you received at creation. |
outcome | Always | Outcome of that step (table below). |
personId | Steps belonging to one person (biometrics and acceptance) | Identifier of the person within the accreditation, returned in persons[].personId since creation. We never send the CPF in the URI: it ends up in browser history, proxy logs and app telemetry. |
remainingPeople | Accreditation with more than one person | How many people still have to complete the step. 0 means that one was the last. |
Example of the return of a PJ partner who completed the facial capture and still has one partner pending:
https://app.yourcompany.com/onboarding/done?accreditationId=acr_9f8e7d6c5b4a&outcome=biometry_approved&personId=prs_3b1c9a7f2e4d&remainingPeople=1
outcome vocabulary
The negative outcomes reuse the same errorReason strings you already handle in the webhooks.
outcome | When it happens |
|---|---|
biometry_approved | Facial biometrics approved. |
biometry_pending | The selfie was submitted and the result was still under analysis at the moment the holder came back. This is the most common case — the result arrives right after, via webhook. |
biometry_failed | Facial biometrics rejected. A retry is possible. |
biometry_expired | The biometrics link expired. A retry is possible. |
acceptance_recorded | Acceptance of the Bring Your Own (BYO) biometrics flow recorded. |
acceptance_expired | The acceptance link expired. |
acceptance_blocked | Acceptance blocked by the device analysis. |
consent_accepted | The holder authorized the portability. |
consent_declined | The holder refused. |
consent_expired | The authorization link expired. |
consent_identity_failed | The holder's identity verification was not approved. |
consent_blocked | Authorization blocked by the device analysis. |
error | Unexpected failure on our side. Do not conclude anything about the journey; query the accreditation. |
biometry_approved is not an active accountIn onboarding, finishing the biometrics does not finish the accreditation: the review, the integration with the settlement partner and the activation still come. biometry_approved with remainingPeople=0 means the biometrics step is over — nothing beyond that.
The account is only ready to operate when the accreditation.active webhook arrives (or GET /v1/accreditations/{id} answers ACTIVE) with the accountId.
The parameters are not proof of anything
They travel in the address bar and the holder can edit them. There is no signature: they are an interface hint, so your app can choose which screen to show when the user comes back.
Any decision with an effect — granting access, marking the registration as complete, enabling an operation — has to rely on:
- the webhook (
accreditation.updated,accreditation.active,accreditation.failed), or - a query to
GET /v1/accreditations/{accreditationId}.
Treat the return as "the user is back in my app, probably with this result" and confirm it through the server-to-server channel.
PJ with several partners
Each partner has their own link, so each one generates their own return, with the personId of whoever completed it. remainingPeople says whether anyone is still missing, and exactly one of the returns comes out with remainingPeople=0.
Two things that matter for your app:
remainingPeople=0means there is nothing pending anymore, not that everyone was approved. A rejected partner can repeat the biometrics, and while they do not repeat it they do not count as pending. The individual status of each person comes inpersons[].biometryStatusin theGET.- The individual outcome (
outcome) and the group one (remainingPeople) are distinct pieces of information and arrive together on purpose.
When the redirect does not happen
The holder finishes on our completion page, without an error, when:
- the accreditation has no
callbackUri; - the URI was removed from the allowlist in the middle of the journey — the authorization is rechecked at the moment of the jump, and the removal takes effect immediately;
- it was not possible to build the return URI.
In those cases the accreditation goes on normally; only the jump back does not happen. Since the real outcome arrives by webhook, nothing is lost.
Deeplinks and the browser
The jump is made by a page of ours, with a visible link, and not by an HTTP redirect: an application scheme does not survive a redirect reliably in every browser. If the app is not installed, the holder sees the link and the outcome message instead of a browser error screen.