Sessions and callbacks

How hosted flows such as onboarding work, and how the end user comes back to you.

Some things cannot happen in an API call, because they need a person: signing a contract, identifying with BankID, answering KYC questions. Invoier hosts those flows. A session is how you start one for an end user.

You do not build any of the screens. You create a session, send the end user to its url, and take them back when they are done.

Session types

TypeUsed for
ACCOUNT_ONBOARDINGOnboarding a new client. The end user signs the contract and completes KYC.
ACCOUNT_KYC_RENEWALRenewing KYC for an account that is already onboarded.

The short forms ONBOARDING and KYC_RENEWAL are accepted and mean the same. They are created on an account with POST /accounts/{accountId}/sessions.

An account can have one onboarding session at a time. Creating it again while it exists returns the existing session (200 instead of 201), so it is safe to call whenever your end user clicks "Start".

The life of a session

  1. You create the session. It is CREATED and its url is ready to hand to the end user.
  2. The flow is prepared with the signing provider, which is called provisioning. In production this starts when the end user first opens the url. The session is PROVISIONING for a few seconds, during which the end user waits, or is sent to your onProvisioning callback if you registered one. It then becomes READY. In the sandbox there is no real signing provider, so provisioning starts as soon as the session has been created.
  3. The session gets an expiresAt when provisioning is done. By default it lasts 7 days. The end user is then in the flow and the session is ACTIVE.
  4. They finish, cancel or fail. The session becomes COMPLETED or CANCELED, and the user is redirected to the matching callback.
  5. For onboarding, the account then moves on, see How it works.

See the diagram in How it works for all the statuses.

The url

The url is for the end user's browser. You can send it as soon as you have created the session, because in production the session is provisioned when it is opened, and you do not have to wait for READY. Redirect them to it or put it in a link or a button. It is tied to the session, so do not share it with anyone else, and do not call it from your server.

It starts the flow. When the flow is over, the browser is sent on to one of your callbacks.

Callbacks

🚧

Callbacks are redirects, not webhooks

Invoier does not call your server when a session changes. Callbacks send the end user's browser to a url that you chose. Nothing is sent in a request body, there are no retries and nothing is signed. Webhooks are planned, but they do not exist yet.

You register callbacks when you create the session:

{
  "session": {
    "sessionType": "ACCOUNT_ONBOARDING",
    "callbacks": {
      "onComplete": { "url": "https://app.example.com/onboarding/done?org=$ORG_NUMBER" },
      "onCancel":   { "url": "https://app.example.com/onboarding/cancelled" },
      "onError":    { "url": "https://app.example.com/onboarding/failed?message=$MESSAGE" }
    }
  }
}

onCancel, onComplete and onError are required. onProvisioning is optional. You can change callbacks later with POST /sessions/{sessionId}.

CallbackThe end user arrives here when
onCompletethey finished the flow
onCancelthey cancelled. They can open the session url again
onErrorsomething went wrong. The reason is available as $MESSAGE
onProvisioningthey opened the url before the session was ready. Optional

Tokens

Tokens are replaced when the end user is redirected. In the path of the url a token is replaced wherever it appears, as in /done/$ORG_NUMBER. In the query string a token is only replaced when it is the whole value of a parameter, as in ?org=$ORG_NUMBER. A token inside a longer value, like ?a=x$LOCALE, is left as it is. Only the tokens listed for a callback can be used in it, and unknown tokens are rejected when you create the session.

TokenValueAvailable in
$LOCALEthe language of the flow, such as sv-SEall callbacks
$URLthe session urlonCancel, onProvisioning
$MESSAGEa technical description of what went wrong, meant for your logs rather than for the end useronError
$ORG_NUMBERthe organisation number of the companyonComplete
$COUNTRYthe country of the companyonComplete
$EXPORTS_URLwhere to fetch the result of the sessiononComplete

$URL is how a "Try again" button on your cancel page can send the end user back into the flow.

What to do when the user arrives

A callback only tells you where the end user went. Treat it as a cue to show the right screen, not as proof of anything, because anyone can open a url. To find out what really happened, fetch the session or the account and read the status.

You are in charge of the loop. Make sure the callback pages end somewhere useful:

  • onComplete: show progress, and poll the account until it is ENABLED. Do not send the user back to the session url.
  • onCancel: offer to try again with $URL, or to leave.
  • onError: show a general error, log $MESSAGE, and offer to start over. If you create a new session each time, you can end up with several. Check the existing one first.

Parameters

parameters configure the flow. locale is sv-SE (the default) or en-US. Some parameters are only available when Invoier has enabled them for your key: expirationInDays, deferProvisioning. Asking for one that is not enabled is a 400.

Parameters cannot be changed after the session has been provisioned.

Pre-filling the KYC form

If you already asked your client the KYC questions, send the answers as parameters and the end user can skip the form. See Pre-filling the KYC form.

Updating and deleting

  • POST /sessions/{sessionId} updates callbacks and metadata. Sessions that are completed, expired, failed or deleted cannot be updated. Send the lastModifiedAt you last read, and you get a 412 if it changed in the meantime.
  • DELETE /sessions/{sessionId} deletes a session that has not started. For sessions that are READY, ACTIVE or CANCELED, add ?force=true. Forcing needs a role that Invoier gives you.

Expiry

A session that is not completed expires at expiresAt. An expired session cannot be used, but you can create a new one for the same account.