Skip to main content

Full API integration

Description#

The API endpoints allow you to manage all the components needed to create connections and retrieve users' data. You will need to define the web interfaces to choose a bank, accept the PSD2 consent, perform the bank login, create and synchronize the connections and manage the events to be able to set up the necessary actions.

If you have a PSD2 agreement, you can choose to use the widget or not. Notice that this last process does not necessarily include any OXLIN UI and can be used to redirect the user seamlessly to your application.

If you do not use the widget, some of the mechanics of the API are strongly linked. When dealing with synchronizations or adding a connection, several resources interacts. For optimal operation, it is essential to understand the interactions and to make calls to the API in the right order.

The solutions implemented by the DSP2 require redirection to the financial institutions. We therefore only support this integration for existing projects. It is no longer possible to start new integrations using this model.

Full API integration flow#

API based integration
  1. Create a user: The user is the at the center of the API resources, it will be used to group connections to banking providers and store the accounts and transactions.
  2. List available providers: The provider represents a financial institution available in our aggregation platform, when enrolling a user, you may want to list available provider for him add a connection to collect data.
  3. Create a new connection
  4. Wait for data to be fetched before synchronization: Depending on the widget options you choose, you may need to implement a waiting step before the data is collected in our systems.
  5. Access account and transaction data: Once the data is made available, you will be able to collect accounts and transactions to manage your business needs.
  6. Manage connection channels lifecycle using API: PSD2 API tokens, embedded credentials have a lifecycle and needs to be updated periodically. To handle this, an API integration should monitor connection and channel statuses to make the user update its channel when needed.

Manage a connection to bank accounts#

The creation of a banking connection is a multi-step process. A client needs to select a provider and create a connection using one of the channel definition defined in the providers resource.

The channel_definitions property may require the client to redirect its user on a URL using a web browser (REDIRECT channel definition mode) or the client to build a form (EMBEDDED channel definition mode) for the user to authenticate with the provider. The channel definitions are bound to a list of account types, you may need to authenticate your user with both a REDIRECT and a EMBEDDED channel to add all the accounts available on a connection.

On a REDIRECT based channel definition, the connection will be created for you at the end of the process by OXLIN. You can choose to implement the creation of connection process.

On the other side, when managing an EMBEDDED channel, you will need to generate an encryption key to encrypt your user's credentials on the client before sending it to our API.

When a new connection is added to a provider's service, two synchronizations will be triggered:

  • At connection creation, a first synchronization of type LIST_ACCOUNTS will retrieve a list of accounts available using the chosen channel. No data is stored at this time.
  • After the client forwards the user consent, a SYNC_ALL synchronization will fetch the account data and the associated transactions.

The client can provide the user consent by using the /connections/{id}/consents endpoint with the list of accounts he agrees to share with the client.

You have the ability to poll the event queue to track progress.

To create a connection, more details on :

And to manage connection channels lifecycle:

Each channel is associated to credentials and synchronization jobs that have statuses to manage. Some statuses requires a user action like:

  • User input and challenge (statuses CHALLENGE_REQUIRED, CHALLENGE_TIMED_OUT, CHALLENGE_FAILED, CHALLENGE_CANCELLED)
  • Updating the credentials (statuses AUTH_FAILED, TOO_MANY_ATTEMPTS, PASSWORD_CHANGE_REQUIRED)
    • Update credentials for a [redirect channel](#update-credentials-for-a-redirect channel)
    • Update credentials for an [embedded channel](#update-credentials-for-an-embedded channel)
  • Asking the user to perform an action on the provider website (USER_ACTION_REQUIRED)

REDIRECT channel creation#

A redirect channel definition contains credentials of type AUTHORIZATION_CODE, ACCESS_TOKEN, REFRESH_TOKEN allows to communicate with the PSD2 API of the provider.

You can let OXLIN creates the connection for you or you can do the process.

Add a new REDIRECT connection

Let OXLIN creates the connection#

A redirect channel definition contains an endpoint used to produce an authorize URL for the client to redirect the user to input credentials.

In this definition, the user is redirected to authenticate on the bank website. Please refer to the Quickstart or the reference documentation to understand how to create a webview session for your user in order to add it to the redirection query params.

The OXLIN callback uri is hosted on the OXLIN widget service. You won't see any OXLIN UI during this process, but we need the following information in order the callback works correctly :

  • client_id and client_secret: the API keys we provide to you in order to use OXLIN API v2. Those information will allow us to refresh the access_token if it expires during the process.
  • access_token : the user access_token to consume API v2
  • provider_id : the identifier of the provider
  • redirect_uri : a callback uri hosted by you, you will receive the connection_id and some data about the status of the synchronization (connection_id, status, queue_id)
# Example, retrieve the authorization service url for Linxo Test Bank (provider ID = 87, REDIRECT channel definition id = 8701)
curl -skX POST \
https://sandbox-api.oxlin.io/v2.1/providers/87/channels/8701/authorize_url \
-H 'authorization: Bearer {access_token}' \
-H 'Content-Type: Application/json'
-d '{
"widget_params": {
"client_id": "{client_id}",
"client_secret": "{client_secret}",
"access_token": "{access_token}",
"provider_id": "87",
"redirect_uri": "https://your.callback.uri"
}
}'
# response
HTTP/2 201
location: https://stettestbank.linxo.com/authorize?create=true&redirect_uri=https%3A%2F%2Fembed.oxlin.io%2Foauth2%2Faisp&state=xec4a2pWQ7KlHA5rYMel2wOx9AULqbFc

In this definition, the user is redirected to authenticate on the bank website. When the authentication is done, the user is redirected transparently on our service for us to create the connection and then back in your application.

Your application can retrieve the connection id and a queue id to wait for the data to be collected.

Notice that PSD2 requires that you use some specific technologies for this redirection for security issues. The RFC8252 can provide additional information on this subject.

The browser view will be redirected on the provided URL with the id of the connection we just created.

This connection will not contain data before the synchronization process is over. A client needs to listen to the synchronization events to be aware of the accounts availibility as described in Synchronization section and provide consent of the user.

You want to create the connection#

Step one, you need to redirect your user on the authorization service of the provider. Choose the provider in the provider list then call the endpoint authorize_url to retrieve the URL of the authorization service of the provider.

As you won't use OXLIN callback, you need to provide some information when calling the endpoint authorize_url

The callback uri is hosted by you and we need the following information in order the callback works correctly :

  • use_client_redirect: When true, we know you will handle the connection creation.
  • redirect_uri: The callback uri which the ASPSP authorization service will contact after a successful authentication.
  • redirect_state: This parameter is optional, it's a data you will provide and you will receive in the callback uri as state query string parameter. Beware, don't use the "pipe |" caracter in your state, this is a reserved caracter for OXLIN. We use it as separator.
# First, I retrieve the channel definition for HSBC
curl -sk \
https://sandbox-api.oxlin.io/v2.1/providers/119 \
-H 'authorization: Bearer {access_token}'
# Returns the key
{
"id":"119",
"name":"HSBC",
"branch_name":"",
"base_url":"https://www.hsbc.fr/1/2/hsbc-france/particuliers/connexion",
"logo_url": "https://static.oxlin.io/common/pictures/providers_logos/119.png",
"country_code":"FRA",
"channel_definitions":[{
"id":"redirect1",
"mode":"REDIRECT",
"account_types": [
"CHECKINGS","CREDIT_CARD"
],
"interactive": false
},{
"id":"embedded1",
"mode":"EMBEDDED",
"account_types": [
"SAVINGS","LOAN"
],
"credentials":[
{
"id":"3500",
"label":"Identifiant",
"regexp":"^[\\a-zA-Z0-9@._-]{7,}$",
"type":"TEXT",
"encrypt":false
},
{
"id":"3501",
"label":"Réponse mémorable",
"regexp":".+",
"type":"PWD",
"encrypt":false
},
{
"id":"3502",
"label":"Mot de passe (8 car. min.)",
"regexp":"^.{8,}$",
"type":"PWD",
"encrypt":true
}
]
}],
"unavailable":false
}

If your are interested in checkings accounts for instance, you will need to provide the redirect channel definition. The URL you need to use for the redirection is produced by calling the endpoint with a redirect url where the user will be redirected at the end of the process and optional widget parameters if you need specific UI.

The authorize URL will contain a widget session id with necessary information to create or update the connection when the bank website will redirect back the user.

Example : Create an authorize URL on HSBC.

# Generate the authorize URL for HSBC
curl -ski POST \
https://sandbox-api.oxlin.io/v2.1/providers/119/channel_definitions/100/authorize_url \
-H 'authorization: Bearer {access_token}'
-H 'Content-Type: application/json'
-d '{
"redirect_uri" : "https://<api-client-url>",
"widget_params" : {
"access_token" : "<access_token>",
"refresh_token" : "<refresh_token>",
"client_id" : "<client_id>",
"client_secret' : "<client_secret>",
"captive_mode" : false
}
}'
# Response
HTTP/1.1 201 OK
location: https://hsbc.fr/ca-paris/authorize?response_type=code&client_id=psdfr-bdf-16928&redirect_uri=https%3A%2F%2Fembed.oxlin.io%2Foauth2%2Faisp&scope=aisp%20extended_transaction_history&state={session_id}

In this example, we are creating an authorize URL for the redirect channel to create a new connection. We have provided client information to refresh the token if the token expires before the redirect is complete.

Afer the redirect process is complete, the browser view will be redirected on the provided URL with the id of the connection we just created.

This connection will not contain data before the synchronization process is over. A client needs to listen to the synchronization events to be aware of the accounts availibility as described in Synchronization HowTo and provide consent of the user.

Forward user consent on accounts#

Once the account list is available, a client needs to ask the user to consent for each account for Oxlin to start collecting the data.

An account is synchronized by Oxlin only if a consent has been given. Notice that an account without consent decision is in the status CONSENT_PENDING and will not be synchronized.

# Consent for accounts. Accounts with consent=true will be synchronized and accounts with consent=false will be deleted.
curl -X PUT \
https://sandbox-api.oxlin.io/v2.1/connections/{id}/consents \
-H 'authorization: Bearer {access_token}'
-d '{
"consents": [
{
"account_id": "{account_id1}",
"consent": true
},
{
"account_id": "{account_id2}",
"consent": false
}
]
}'
HTTP/1.1 204 No content

When the consent is updated, a full synchronization is made to collect the missing data.

EMBEDDED channel creation#

Add a new connection

Create the encryption key#

To implement an EMBEDDED channel definition, a client will first need to create a new PGP encryption key. This obtained key will allow you to encrypt the user's bank credentials.

Each banking institution (providers) has a list of identifiers. We know that we must encrypt an identifier its value encrypted is set to true.

The encryption is done on the client side and it is the encrypted value that will be sent in the connection add payload.

Example : I want to create a connection for my client. His bank is Linxo Test Bank.

# First, I retrieve the credentials information from HSBC
curl -sk GET \
https://sandbox-api.oxlin.io/v2.1/providers/119 \
-H 'authorization: Bearer {access_token}'
# Response
HTTP/1.1 200 OK
{
"id":"119",
"name":"HSBC",
"branch_name":"",
"base_url":"https://www.hsbc.fr/1/2/hsbc-france/particuliers/connexion",
"country_code":"FRA",
"channel_definitions":[{
"id":"redirect1",
"mode":"REDIRECT",
"account_types": [
"CHECKINGS","CREDIT_CARD"
],
"credentials":[]
},{
"id":"embedded1",
"mode":"EMBEDDED",
"account_types": [
"SAVINGS","LOAN"
],
"credentials":[
{
"id":"3500",
"label":"Identifiant",
"regexp":"^[\\a-zA-Z0-9@._-]{7,}$",
"type":"TEXT",
"encrypt":false
},
{
"id":"3501",
"label":"Réponse mémorable",
"regexp":".+",
"type":"PWD",
"encrypt":false
},
{
"id":"3502",
"label":"Mot de passe (8 car. min.)",
"regexp":"^.{8,}$",
"type":"PWD",
"encrypt":true
}
]
}],
"interactive":false
}

If your are interested in savings accounts for instance, you will need to provide the embedded channel definition. The associated credentials table, the credential of ID 301 has the property encrypted: true. It will be necessary to encrypt the value using a PGP key.

Credentials encryption requires a PGP encryption key that you can generate using our API.

On the client side, you will retrieve the public key you just created to encrypt the credential.

# Creates the encryption key
curl -X POST \
https://sandbox-api.oxlin.io/v2.1/keys \
-H 'authorization: Bearer {access_token}' \
-d '{
"type": "PGP"
}'
# it returns the key id in the header location
HTTP/1.1 201 Created
location: /keys/{key_id}
# Gets the encryption key
curl -sk GET \
https://sandbox-api.oxlin.io/v2.1/keys/{key_id} \
-H 'authorization: Bearer {access_token}'
# Returns the key
{
"id": "87",
"public_key": "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: BCPG v1.43\n\nmI0EWxAaPgEEAJXZ9xLObAvsysbK27XAW2JRMVjf4Zh0XnkyDi78d1iIQo7JbKKH\ndUzHFZvXOVkGGaITb3fjozQqAWW1nAuQpXLcQU4rg0nhCU4KYCykK0MulR70Of5/\nReklDBtrbtmctQEsVux4fO+bidha1dz0l/sbpXgpVc4CY8oopjXuQDxBABEBAAG0\nCUxJTlhPIFNBU4icBBABAgAGBQJbEBo+AAoJELqVveObg+d4G74D/i0ikaoiUT6T\nsuo4nYGvcIrphslb+P3WWcP4ITJ7V9+fKLNg0z50ZwRHXs1xu8h/DXAl6qRaKX+j\n0OQmRk6sDkTe229WbIHhK5v572v0jHSOlJvGqGgXgFEMIeAnECKOS+GqZpn/CYRj\nMKe41DKSwo65mEb/Zb4sF41PWh1KntR5\n=2l0S\n-----END PGP PUBLIC KEY BLOCK-----\n",
"type": "PGP",
"expiration_date": 1527785550
}
# Encrypt the credential (encrypt: true) with the PGP library of your choice, here
-----BEGIN PGP MESSAGE-----
Version: BCPG v1.43
hIwDwj9RkuzOAeMBA/9iuYDAGJ6QcZZaGLzF2hLNG+cdCIIZl3Yva9y+q12JpuIz
hlsOuxdYaIQ/KVsQHj7tMULNkKrbLa6mzL2I/tzzbS1ZXXe43Xwcpk76zOPeqikx
gjTT4ND0gz2NWu2rNAml9Fau/1MsMTNBWXQpBFHooa9QkamYB9MBux/BQKp0Mckl
hERvVwT6pbXZu+aomrbksBonZYbFt66DQDq7lv3tpWOJht5m1Q==
=a6mL
-----END PGP MESSAGE-----

Create the connection#

To create the connection, you must provide a channel with a list of credentials representing the user input according to the credentials of the provider.

Once the connection is created, a synchronization will be triggered and the associated bank accounts will be automatically listed.

If you wish, you can follow the synchronization progress by subscribing to a queue and poll it every X seconds.

Example :

# [OPTIONAL] Subscribes to a queue with
curl -X POST \
https://sandbox-api.oxlin.io/v2.1/queues/subscribe \
-H 'authorization: Bearer {access_token}' \
-d '{
"topics": ["connections"]
}'
# [OPTIONAL] It returns the queue id in the header location
HTTP/1.1 201 Created
location: /queues/{queue_id}
# [OPTIONAL] If you poll the queue, events stack in the queue
curl -sk https://sandbox-api.oxlin.io/v2.1/queues/{queue_id} \
-H 'authorization: Bearer {access_token}'

Whether you decide to follow the triggered synchronization or not, all you have to do is to create the connection.

# To create a connection, you must provide the financial institution identifier,
# the encryption key identifier and the list of credentials required
# to authenticate the user on the financial institution.
curl -X POST \
https://sandbox-api.oxlin.io/v2.1/connections \
-H 'authorization: Bearer {access_token}'
-d '{
"provider_id": "87",
"key_id": "{key_id}",
"credentials": [
{ "id": "300", "value": "dev" },
{ "id": "301", "value": "-----BEGIN PGP MESSAGE-----\nVersion: BCPG v1.43\n\nhIwDwj9RkuzOAeMBA/9iuYDAGJ6QcZZaGLzF2hLNG+cdCIIZl3Yva9y+q12JpuIz\nhlsOuxdYaIQ/KVsQHj7tMULNkKrbLa6mzL2I/tzzbS1ZXXe43Xwcpk76zOPeqikx\ngjTT4ND0gz2NWu2rNAml9Fau/1MsMTNBWXQpBFHooa9QkamYB9MBux/BQKp0Mckl\nhERvVwT6pbXZu+aomrbksBonZYbFt66DQDq7lv3tpWOJht5m1Q==\n=a6mL\n-----END PGP MESSAGE-----\n" },
{ "id": "302", "value": "https://www.dropbox.com/s/2j3d4gcrd031fkc/sanity.txt?dl=1" }
]
}'
# Returns connection id in header
HTTP/1.1 201 Created
location: /connections/{connection_id}

User input and challenge#

Some Providers require the user to respond to a challenge to continue synchronization. The user can receive a code on his phone for example. In the API, providers have an interactive property that allows you to know if a challenge will be proposed to the user.

During synchronization, you will receive events with a specific event_status:

  • SYNC_ALL_USER_INPUT_REQUESTED: The user must respond to a challenge during a synchronization
  • LIST_ACCOUNTS_USER_INPUT_REQUESTED: The user must respond to a challenge during the recovery of his accounts at a Provider
Challenge and user input

Identify a challenge#

When you poll the queue, you will have an event with event_status: "LIST_ACCOUNTS_USER_INPUT_REQUESTED" (or SYNC_ALL_USER_INPUT_REQUESTED).

This event will contain several pieces of information:

  • the resource property: the connection associated with the event. The id of the connection will be necessary to answer the challenge
  • the event_status property : LIST_ACCOUNTS_USER_INPUT_REQUESTED (or SYNC_ALL_USER_INPUT_REQUESTED)
  • a property containing the challenges called list_accounts_user_input_requested (or sync_all_user_input_requested)

Here is an example of an event:

{
"id":"14",
"event_type":"LIST_ACCOUNTS_USER_INPUT_REQUESTED",
"resource_type":"CONNECTION",
"creation_date":1528199358,
"channel_definition_id":"embedded1",
"resource":{
"id":"17730",
"provider_id":"87",
"name":"Test Bank",
"status":"SUCCESS",
"channels": [
{
"id": "redirect1",
"expires": 1528106353
"status": "SUCCESS"
},
{
"id": "embedded1",
"status": "RUNNING"
}
],
"creation_date":0,
"last_success_date":1528199340,
"last_end_date":1528199340
},
"list_accounts_user_input_requested":{
"challenge":[
{
"id":"303",
"label":"La connexion à Banque demande la saisie d’un code envoyé par SMS sur votre téléphone au {phone}. Souhaitez vous recevoir ce code maintenant ?",
"regexp":".*",
"type":"INFO_MSG",
"encrypt":false,
"params":{
"phone":"0606060606"
}
},
{
"id":"304",
"label":"Envoyer le SMS",
"regexp":".*",
"type":"OK_CANCEL",
"encrypt":false
}
]
}
}

list_accounts_user_input_requested (or sync_all_user_input_requested) is a challenge array. The type property informs about the type of challenge:

  • INFO_MSG: This challenge is an informative message (example: a code has been sent on your phone)
  • OK_CANCEL: This challenge simulates the action of a user who would have answered OK (true) or CANCEL (false)
  • NUMERIC: This challenge expects the answer provided is a number
  • TEXT: This challenge expects the answer provided is a text
  • PWD: This challenge expects the answer provided is a password
  • NUMERIC_PWD: This challenge expects the answer provided is a numeric password
  • DATE: This challenge expects the answer provided is a date
  • TIMEOUT_MSG: In general, the answers to a challenge are limited in time, this message informs about the remaining duration
  • ERROR_MSG: This message is an error message. It happens when the response to a challenge is wrong

Challenges can have a params property. These parameters are information to be integrated in challenges displaying messages. The label property of the challenge will contain a moustache variable (example: {phone}) and in the parameters is the value of this variable

Example:

{
"id":"303",
"label":"La connexion à Banque demande la saisie d’un code envoyé par SMS sur votre téléphone au {phone}. Souhaitez vous recevoir ce code maintenant ?",
"regexp":".*",
"type":"INFO_MSG",
"encrypt":false,
"params":{
"phone":"0606060606"
}
}
# You should replace {phone} by 0606060606 in your interface

Respond to challenge#

During a synchronization, you will receive this challenge :

{
"id":"14",
"event_type":"LIST_ACCOUNTS_USER_INPUT_REQUESTED",
"resource_type":"CONNECTION",
"channel_definition_id":"embedded1",
"creation_date":1528199358,
"resource":{
"id":"17730",
"provider_id":"87",
"name":"Test Bank",
"channels": [
{
"id": "redirect1",
"expires": 1528106353
"status": "SUCCESS",
"last_success_date":1528199340,
"last_end_date":1528199340
},
{
"id": "embedded1",
"status": "RUNNING",
"last_success_date":1528199340,
"last_end_date":1528199340
}
],
"status":"SUCCESS",
"creation_date":0
},
"list_accounts_user_input_requested":{
"challenge":[
{
"id":"303",
"label":"La connexion à Banque demande la saisie d’un code envoyé par SMS sur votre téléphone au {phone}. Souhaitez vous recevoir ce code maintenant ?",
"regexp":".*",
"type":"INFO_MSG",
"encrypt":false,
"params":{
"phone":"0606060606"
}
},
{
"id":"304",
"label":"Envoyer le SMS",
"regexp":".*",
"type":"OK_CANCEL",
"encrypt":false
}
]
}
}

This challenge proposes to the user to receive a code on his phone number. This is the information we retrieved from the Provider. You must propose to the user to answer "OK" (true) or "CANCEL" (false).

If the user answers "OK", synchronization continues. If he answers "CANCEL", the synchronization stops. If he does not respond, synchronization will fail and the connection will have the status: "CHALLENGE_TIMED_OUT".

# The user respond "OK" on your interface, you have to send
curl -X POST \
https://sandbox-api.oxlin.io/v2.1/connections/{connection_id}/channels/{channel_definition_id}/user_input \
-H 'authorization: Bearer {access_token}' \
-d '{"credentials":[{"id":"304","value":"true"}]}'
# The user respond "CANCEL" on your interface, you have to send
curl -X POST \
https://sandbox-api.oxlin.io/v2.1/connections/{connection_id}/channels/{channel_definition_id}/user_input \
-H 'authorization: Bearer {access_token}' \
-d '{"credentials":[{"id":"304","value":"false"}]}'
# The synchronization continues an new events are queued
HTTP/1.1 204 No-Content

In this example, the user has just accepted a code to be sent to him on his phone. Synchronization will continue and another challenge will be proposed to the user. You will have to adapt your interface to offer him the input of the code he will receive.

{
"id":"17",
"event_type":"LIST_ACCOUNTS_USER_INPUT_REQUESTED",
"resource_type":"CONNECTION",
"channel_definition_id":"embedded1",
"creation_date":1528199358,
"resource":{
"id":"17730",
"provider_id":"87",
"name":"Test Bank",
"channels": [
{
"id": "redirect1",
"expires": 1528106353
"status": "SUCCESS",
"creation_date":0,
"last_success_date":1528199340,
"last_end_date":1528199340
},
{
"id": "embedded1",
"status": "RUNNING",
"creation_date":0,
"last_success_date":1528199340,
"last_end_date":1528199340
}
],
"status":"SUCCESS"
},
"list_accounts_user_input_requested":{
"challenge":[
{
"id":"305",
"label":"Un code a été envoyé par SMS sur votre téléphone au {phone}.",
"regexp":".*",
"type":"INFO_MSG",
"encrypt":false,
"params":{
"phone":"0606060606"
}
},
{
"id":"309",
"label":"Code invalide. Encore {remaining_attempts} tentatives",
"regexp":".*",
"type":"ERROR_MSG",
"encrypt":false,
"params":{
"remaining_attempts":"1"
}
},
{
"id":"306",
"label":"Code",
"regexp":"^[0-9]{6}$",
"type":"NUMERIC",
"encrypt":false
},
{
"id":"308",
"label":"Il vous reste {timeout} pour valider votre code.",
"regexp":".*",
"type":"TIMEOUT_MSG",
"encrypt":false,
"params":{
"timeout":"120"
}
},
{
"id":"307",
"label":"Valider",
"regexp":".*",
"type":"OK_CANCEL",
"encrypt":false
}
]
}
}

This event contains 4 challenges. 2 are messages :

  • INFO_MSG (identifier 305): We learn that the code has been sent on the phone number
  • TIMEOUT_MSG (identifier 308) : We learn that the user still has {timeout} seconds to send the received code by message. You can replace the mustache variable by the value provided in the property params of the challenge

So there are only two remaining challenges to deal with:

  • NUMERIC (identifier 306): This is the value that the user must enter. The encrypt: false property allows us to understand that the message does not have to be encrypted. It is accompanied by a regexp property allowing you to validate the user's entry
  • OK_CANCEL (identifier 307): We have already encountered this case in the previous event. The user's action is simulated. He enters his code and clicks on "OK" or he decides to abandon synchronization and clicks on "CANCEL"
# Send the input of the user to continue synchronization.
curl -X POST \
https://sandbox-api.oxlin.io/v2.1}/connections/{connection_id}/channels/{channel_definition_id}/user_input \
-H 'authorization: Bearer {access_token}' \
-d '{"credentials":[{"id":"306","value":"123456","id":"307","value":"true"}]}'
# Send the cancel action from the user
curl -X POST \
https://sandbox-api.oxlin.io/v2.1}/connections/{connection_id}/channels/{channel_definition_id}/user_input \
-H 'authorization: Bearer {access_token}' \
-d '{"credentials":[{"id":"307","value":"false"}]}'
# The input has been received and the synchronization will continue
HTTP/1.1 204 No-Content

And what happens if the user enters the wrong code?

Synchronization brings up a new event containing a challenge that is an error message. You can display it on your interface.

{
"id":"17",
"event_type":"LIST_ACCOUNTS_USER_INPUT_REQUESTED",
"channel_definition_id":"embedded1",
"resource_type":"CONNECTION",
"creation_date":1528199358,
"resource":{
"id":"17730",
"provider_id":"87",
"name":"Test Bank",
"channels": [
{
"id": "redirect1",
"expires": 1528106353
"status": "SUCCESS",
"last_success_date":1528199340,
"last_end_date":1528199340
},
{
"id": "embedded1",
"status": "RUNNING",
"last_success_date":1528199340,
"last_end_date":1528199340
}
],
"status":"SUCCESS",
"creation_date":0
},
"list_accounts_user_input_requested":{
"challenge":[
{
"id":"305",
"label":"Un code a été envoyé par SMS sur votre téléphone au {phone}.",
"regexp":".*",
"type":"INFO_MSG",
"encrypt":false,
"params":{
"phone":"0606060606"
}
},
{
"id":"309",
"label":"Code invalide. Encore {remaining_attempts} tentatives",
"regexp":".*",
"type":"ERROR_MSG",
"encrypt":false,
"params":{
"remaining_attempts":"1"
}
},
{
"id":"306",
"label":"Code",
"regexp":"^[0-9]{6}$",
"type":"NUMERIC",
"encrypt":false
},
{
"id":"308",
"label":"Il vous reste {timeout} pour valider votre code.",
"regexp":".*",
"type":"TIMEOUT_MSG",
"encrypt":false,
"params":{
"timeout":"120"
}
},
{
"id":"307",
"label":"Valider",
"regexp":".*",
"type":"OK_CANCEL",
"encrypt":false
}
]
}
}

The new event contains an event with a type property: "ERROR_MSG". The event explains that there are still {remaining_attempts} attempts to the user. He must resubmit his code, but it must be correct this time.

Add a channel to an existing connection#

Adding a channel to an existing connection follows the same procedure as updating credentials for an EMPTY channel in a connection:

Example : Get a connection status:

# List channels in a connection
curl -s https://sandbox-api.oxlin.io/v2.1/v2.1/connectionconnections/18902 -H "authorization: Bearer {access_token}"
# Response
HTTP/1.1 200 OK
{
"id" : "18902",
"provider_id" : "87",
"name" : "Linxo Test Bank",
"status" : "SUCCESS",
"creation_date" : 1562233640,
"auto_sync" : true,
"logo_url" : "https://static.oxlin.io/common/pictures/providers_logos/87.png",
"channels" : [ {
"channel_definition_id" : "8701",
"mode" : "REDIRECT",
"expires" : 1569974400,
"status" : "NEVER",
"credentials" : [ ],
"account_types" : [ "CHECKINGS", "CREDIT_CARD" ]
}, {
"channel_definition_id" : "13400",
"mode" : "EMBEDDED",
"status" : "EMPTY",
"account_types" : [ "LOAN", "CREDIT_CARD" ]
} ],
"owner" : { }
}

In this example, updating credentials for channel_definition_id=13400 will add an embedded channel on this conection.

Update credentials for a channel#

Leaving a channel in a failure state leads to a break in the data sync. On a REDIRECT channel, credential expiration can be prevented by renewing the authentiction before the expiration date. This is made by using the expires property of the channel in the connection resource.

Example: Fetch status for a specific connection:

#
curl -s https://sandbox-api.oxlin.io/v2.1/v2.1/connections/18903 -H "authorization: Bearer {access_token}"
{
"id" : "18903",
"provider_id" : "182",
"name" : "Demo Bank",
"status" : "FAILED",
"creation_date" : 1562233721,
"auto_sync" : true,
"logo_url" : "https://static.oxlin.io/common/pictures/providers_logos/182.png",
"channels" : [ {
"channel_definition_id" : "17650",
"mode" : "EMBEDDED",
"status" : "AUTH_FAILED",
"last_success_date" : 1562233742,
"last_end_date" : 1562233742,
"credentials" : [ {
"id" : "10001",
"masked_value" : "**mo"
} ],
"account_types" : [ "CHECKINGS", "SAVINGS", "LOAN", "CREDIT_CARD" ]
} ],
"owner" : {
"name" : "Mr Test",
"address" : "address test",
"birth_date" : 1562233742,
"birth_place" : "birth place test",
"phone_number" : "06******1"
}
}

In this example, the connection is in FAILED status because its only channel is in AUTH_FAILED status. To fix the issue and restore the link with the bank, the user needs to update its password using an embedded form.

Updating credentials is made using a web redirection for REDIRECT channels and using a form for EMBEDDED channels in the same way we used to create the channel.

Update credentials for a redirect channel#

For a REDIRECT channel, we will generate a new authorize URL using our connection_id and channel_definition_id. After the authentication and the redirect is made, our system will update the credentials.

Example : Create an authorize URL on HSBC.

# Generate the authorize URL for HSBC on existing connection
curl -ski POST \
https://sandbox-api.oxlin.io/v2.1/providers/119/channel_definitions/100/authorize_url \
-H 'authorization: Bearer {access_token}'
-H 'Content-Type: application/json'
-d '{
"connection_id" : "123",
"channel_definition_id" : "redirect1",
"redirect_uri" : "https://<api-client-url>",
"widget_params" : {
"access_token" : "<access_token>",
"refresh_token" : "<refresh_token>",
"client_id" : "<client_id>",
"client_secret' : "<client_secret>",
"captive_mode" : false
}
}'
# Response
HTTP/1.1 201 OK
location: https://hsbc.fr/ca-paris/authorize?response_type=code&client_id=psdfr-bdf-16928&redirect_uri=https%3A%2F%2Fembed.oxlin.io%2Foauth2%2Faisp&scope=aisp%20extended_transaction_history&state={session_id}

Update credentials for an embedded channel#

For an embedded channel, you will have to buid the form like in the channel creation but the credentials will be updated using PUT /connections/{id}/channels/{channel_definition_id}.

Example: Update all credentials for a channel

curl -X PUT \
https://sandbox-api.oxlin.io/v2.1/connections/{id}/channels/{channel_id} \
-H 'authorization: Bearer {access_token}'
-d '{
"id": "{channel_definition_id}",
"key_id": "{key_id}",
"credentials": [
{ "id": "3500", "value": "0123456" },
{ "id": "3501", "value": "test" },
{ "id": "3502", "value": "-----BEGIN PGP MESSAGE-----\nVersion: BCPG v1.43\n\nhIwDwj9RkuzOAeMBA/9iuYDAGJ6QcZZaGLzF2hLNG+cdCIIZl3Yva9y+q12JpuIz\nhlsOuxdYaIQ/KVsQHj7tMULNkKrbLa6mzL2I/tzzbS1ZXXe43Xwcpk76zOPeqikx\ngjTT4ND0gz2NWu2rNAml9Fau/1MsMTNBWXQpBFHooa9QkamYB9MBux/BQKp0Mckl\nhERvVwT6pbXZu+aomrbksBonZYbFt66DQDq7lv3tpWOJht5m1Q==\n=a6mL\n-----END PGP MESSAGE-----\n" }
]
}'
HTTP/1.1 204 No Content

Alternatively, you can update a single credential using PUT /connections/{connection_id}/credentials/{credential_id} to avoid making the user input its identifier for instance. You can also display the masked_value of the identifier present in the connection resource.

PSD2 features with the widget#

The widget is available for any customer and customizable at different levels depending on your legal licence.

If you don't have or don't plan to use your own PSD2 licence, making a widget based integration will be mandatory.

Regulatory reminders: Depending on whether you have PSD2 (AIS/PIS) agreement or not, the use of widgets and features differ.

Here is a summary table:

PSD2 Agreement (AIS/PIS)No Agreement
Create user outside widgetsYESYES
End user has to validate OXLIN terms and conditionsNOYES
Can customize Widgets (color)YESNO
Removal of OXLIN logosYESNO
Do aggregation in full API modeYESNO
User can consent per accountsYESNO

Consent management#

You can use the default OXLIN behavior or you can have a different consent management. We propose a consent per account. You can use the query string parameter select_accounts. A webview then suggests to the user to choose the accounts he wants to aggregate and for which he consents. On this created connection, the new available accounts will not be added automatically. It will be necessary to use a dedicated widget in the future for this purpose.

When you have an agreement, you can also perform this behavior in full API. When the connection is saved, you will receive an event containing the account list for the client to display an account list selection screen and requisite consent. You will then need to call the consents endpoint /connections/{id}/consents using the word PUT to provide the list of account ids and the decision the user made. After this step, a synchronization is triggered to collect data on this account list.

To notify clients about new accounts that the user needs to give his/her consent to, we now list in the accounts resource the accounts with a status PENDING_CONSENT. An account will stay in this status until a consent decision is made on the endpoint /connections/{id}/consents.

Manage the accounts with the widget webview#

Display the select accounts widget (You need your own agreement to do this flow)

 captive_modeselect_accountsmultiple_channelsselect_channel
Valuetrue or falsetruefalsefalse
  • The user selects his provider from the list of available providers.
  • OXLIN prioritizes channel definitions (in this example, REDIRECT channel is available)
  • The user fills his credentials
  • We validate the credentials, create the connection
  • The list of available accounts is displayed and the user can choose which data he wants to aggregate.