With decoupled authentication the bank carries out an additional form of authentication external to the API integration. This model allows for a number of solutions, for example using a mobile phone to authenticate, or using biometrics for authentication through a separate terminal, such as a point of sale (POS) device.
See the Token.io API reference for details of the following endpoints:
GET /banksPOST /token-requestGET /token-requests/{request ID}/resultPOST /token-requests/{tokenRequestId}/authorizationGET /transfers/{transferId}
The image below is a simplified swim lane diagram of the integration flow.

Details of the steps within the integration flow are shown below.
The TPP retrieves the list of available banks using the GET /banks call.
We recommend that this step is performed once daily, outside any payment initiation, e.g., at 11pm UTC, and that the result is cached.
In the GET /banks request, there are numerous filtering criteria you can add as parameters to narrow your query. As a minimum, you should filter by your member ID.
See Mandatory fields for information on which fields must be provided for a given bank to accept a payment initiation request.

a. TPP -> Token.io - The TPP requests the list of banks from Token.io.
b. Token.io -> TPP - Token.io supplies the list of banks to the TPP.
The user selects the bank.

a. TPP -> User - The TPP displays the bank selection screen.
b. User -> TPP - The user selects the bank.
The TPP collects consent from the user. For more information see User consent collection.

a. TPP -> User - The TPP initiates consent acceptance from the user.
b. User -> TPP - The user gives consent to the TPP.
The TPP initiates the token request with Token.io using the POST /token-requests call. Token.io responds by acknowledging the details of the request.

a. TPP -> Token.io - The TPP creates the token request using the POST /token-requests call.
If you're using Settlement accounts and you're an unregulated TPP, you'll always need to send the Settlement account id of the sub-TPP in the POST /token-requests request.
b. Token.io -> TPP - Token.io generates a response to the token request.
The TPP requests authorization using the POST /token-requests/{tokenRequestId}/authorization call. Token.io responds with the fields object, containing the initial fields. In the fields object the type is returned as DECOUPLED.

a. TPP -> Token.io - The TPP requests the authorization URL using the POST /token-requests/{tokenRequestId}/authorization call. Initial credentialFields from the response to GET /banks may be sent in the credentials field. The useCredentialFlow flag must be set to true. When this flag is set to true, the credential flow is triggered. The credentials map must be populated if required by the bank (see the credentialFields in the response to GET /banks), otherwise empty credentials are used.
The useCredentialFlow field should not be confused with useWebAppCredentialsFlow.
When useWebAppCredentialsFlow is set to true and the bank's flow includes embedded steps, these steps are handled by Token.io's HP, rather than by the customer's own pages.
See the POST /token-requests/{tokenRequestId}/authorization request for more details.
b. Token.io -> TPP - Token.io returns the fields object with type set to DECOUPLED and the message to be displayed to the user.
When implementing the decoupled authentication flow, you should consider the following:
If you receive a redirect URL, you should redirect the user.
If you receive
credential-fieldsin the callback, base-64 decode the contents and display the fields indicated.If you receive fields in the
POST /token-requests/{tokenRequestId}/authorizationresponse, display them and collect the user input - keep repeating until you receivestatus:AUTHENTICATEDortype:DECOUPLED.If you get
status:AUTHENTICATEDyou can continue the flow by callingGET /token-requests/{request ID}/resultandGET /transfers.If you get
type:DECOUPLED, callPOST /token-requests/{tokenRequestId}/authorizationagain until you getstatus:AUTHENTICATED, then you can continue the flow by callingGET /token-requests/{request ID}/resultandGET /transfers.
See Decoupled callback for more information on the Token.io callback.
The TPP is expected to show the message to the user and to wait for confirmation of receipt before proceeding to the next step.
The bank will then challenge the user to authenticate themselves outside the API integration. This could be in the form of an SMS to the user’s registered phone number with the bank, or biometrics through a POS device.

a. TPP -> User - The TPP displays the message to the user.
b. User -> TPP - The user confirms receipt of the message.
c. Bank -> User - The bank requests additional authentication from the user, through an external source, e.g., SMS. POS.
d. User -> Bank - The user provides additional authentication credentials to the bank via an external source.
Using the POST /token-requests/{tokenRequestId}/authorization call, the TPP submits the additional credentials to the bank via Token.io.

a. TPP -> Token.io - The TPP sends additional credentials using the POST /token-requests/{tokenRequestId}/authorization call.
b. Token.io -> TPP - Token.io acknowledges receipts of the additional credentials and requests more, if required.
c. Token.io -> Bank - Token.io sends this additional information to the bank.
d. Bank -> Token.io - The bank confirms receipt of the information from Token.io.
The user completes authorization with the bank.

a. Bank -> User - The bank displays the authorization page to the user.
b. User -> Bank - The user authorizes the payment with the bank.
Token.io recommends that TPPs subscribe to webhooks to receive payment status updates. As a fallback, TPPs can also poll Token.io for the current status of an initiated payment request.

a. Token.io -> TPP - Token.io sends the payment status to the TPP in a webhook.
If you're subscribed to webhooks, Token.io sends the payment status in a webhook every time a transfer is created or updated.
See Webhooks for an example webhook notification.
If the webhook is not received within 30 minutes, the TPP should call GET /transfers/{transferId} to retrieve the payment status.
If you're not using webhooks, you'll need to use GET /token-requests/{tokenRequestId}/result to obtain the transferId to then call GET /transfers/{transferId}.
See Alternative to webhooks for more details.
b. TPP -> Token.io - The TPP checks the status with Token.io with the GET /transfers/{transferId} request.
c. Token.io -> TPP - Token.io confirms the payment status with the GET /transfers/{transferId} response.
If you're using Settlement accounts and the payment initiation request is successful, once the payment has been reconciled, the status will be SETTLEMENT_COMPLETED. Depending on the payment rail used, this can take a while.
If you're not using Settlement accounts and the payment initiation request is successful, the status will be SUCCESS, but this does not guarantee that the payment has been settled.
If the status is non-final, e.g., PROCESSING, the TPP should wait for additional webhooks to be sent. If the webhook is not received, the TPP should use a polling call (GET /transfers/{transferId}) every 15 mins.
The recommended maximum polling time is 30 days. If the bank doesn't update the status, it will change to INITIATED after 30 days.
See HTTP errors for information on HTTP error status codes.
If you have any feedback about the developer documentation, please contact devdocs@token.io