Skip to main content

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

ParameterWhenDescription
accreditationIdAlwaysThe same accreditationId you received at creation.
outcomeAlwaysOutcome of that step (table below).
personIdSteps 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.
remainingPeopleAccreditation with more than one personHow 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.

outcomeWhen it happens
biometry_approvedFacial biometrics approved.
biometry_pendingThe 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_failedFacial biometrics rejected. A retry is possible.
biometry_expiredThe biometrics link expired. A retry is possible.
acceptance_recordedAcceptance of the Bring Your Own (BYO) biometrics flow recorded.
acceptance_expiredThe acceptance link expired.
acceptance_blockedAcceptance blocked by the device analysis.
consent_acceptedThe holder authorized the portability.
consent_declinedThe holder refused.
consent_expiredThe authorization link expired.
consent_identity_failedThe holder's identity verification was not approved.
consent_blockedAuthorization blocked by the device analysis.
errorUnexpected failure on our side. Do not conclude anything about the journey; query the accreditation.
biometry_approved is not an active account

In 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=0 means 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 in persons[].biometryStatus in the GET.
  • 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.

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.