Accounts 🏦

This file will explain how to add and manage bank accounts in Yoli. If you are using the Yoli API without a connected bank account and want to update your users manually, jump to the Using Yoli without a Bank Account section.

Syncing

NOTE: This feature is currently not available, but will be implemented soon. Stay tuned!

By default the information and transactions of already added accounts will be synced every few hours. (Note that some banks only refresh their online banking data much less often than that, which unfortunately can't be worked around).

Multiple Accounts Behind One Login

Due to the nature of banks, there actually might be more than one bank account behind single set of online-banking credentials.

For example a user might have a normal account, a daily savings account and a partner account, all behind a single login.

This adds complexity, as we cannot simply remove a single account from being periodically synced without also removing the rest. (More info in the relevant section)

Adding a Bank Account

To add a bank account you need to be properly authenticated.

Initiating a Bank Login

To retrieve your bank account data and credentials, you can use our in-build bank search. Use the addBankLogin mutation with appropriate bank code and credentials to initiate a bank login. The required credentials and loginInterface can be queried by our bank search.

To test this functionality, you can use the provided test login below.

mutation {
  addBankLogin(
    bankCode: "10020000",
    loginInterface: "FINTS_SERVER", 
    credentials: [
      {
        "label": "username",
        "value": "fino01"
      },
      {
        "label": "password", 
        "value": "654321"
      }
    ]
  )
}
{
  "data": {
    "addBankLogin": "<task token>"
  }
}

Due to the complex nature of bank login systems, this process may take up to a few minutes and may take several steps in-between.

To handle this easier and eliminate the need for a constant internet connection, every started bank login will give you a "task token" which you can use to check on the state of the bank login and extract information on why it may have failed.

Note that each bank login automatically triggers the available modules for the user, such as contracts, documents, smartfeed, etc..

Before you can access your bank account data, every login requires a two-factor-authentication (2FA). In our context, this process is called a "challenge". To complete this challenge, multiple steps can be required depending on the chosen challenge of the user's bank: * Choosing the challenge type (optional) * Choosing a medium for the challenge (optional) * Sending the challenge input (TAN)

After initiating the bank login, you can check on the next required step via the Bank Login Status

Bank Login Status

You can check on the status of a bank login by using the bankLoginStatus. The status field describes the status of the complete bank login and will either be pending, success or failed. To get more information on why a login failed you use the message and providerError fields.

In addition, this query is used to retrieve information on the required 2FA challenge. Depending on the challenge process, the first steps will be either chooseChallengeType to choose the challenge type or chooseChallengeMedium to choose the challenge medium and finally challengeResponse or photoTanChallengeResponse for the challenge input. The different challenge options will be described here.

{
  bankLoginStatus(taskToken: "<task token>") {
    status
    step
    message
  }
}
{
  "data": {
    "bankLoginStatus": {
      "status": "pending",
      "step": "chooseChallengeType"
    }
  }
}

Choose challenge type

Attention: This step is optional and only is required, if the user has activated several challenge types for his bank account.

First, check the bankLoginStatus for the available challengeTypes:

{
    "data": {
        "bankLoginStatus": {
            "status": "pending",
            "step": "chooseChallengeType",
            "challengeTypes": [
                {
                    "id": "930",
                    "name": "Mobile TAN per SMS"
                },
                {
                    "id": "931",
                    "name": "Push-TAN per App"
                },
                {
                    "id": "932",
                    "name": "Photo-TAN"
                },
                {
                    "id": "910",
                    "name": "Chip-TAN manuell HHD 1.3.2"
                },
                {
                    "id": "920",
                    "name": "Chip-TAN manuell HHD 1.4"
                },
                {
                    "id": "911",
                    "name": "Chip-TAN optisch HHD 1.3.2"
                },
                {
                    "id": "921",
                    "name": "Chip-TAN optisch HHD 1.4"
                },
                {
                    "id": "913",
                    "name": "Chip-TAN QR"
                },
                {
                    "id": "912",
                    "name": "Chip-TAN USB"
                }
            ],
            "challengeMedia": null
        }
    }
}

Send the id of the chosen challenge type via the sendChallengeInput mutation.

mutation {
  sendChallengeInput(taskToken: "foo", "input": "930")
}
{
  "data": {
    "sendChallengeInput": "<task token>"
  }
}

Choose challenge medium

Attention: This step is optional and only is required, if the user has activated several challenge media for his bank account.

First, check the bankLoginStatus for the available challengeMedia:

{
    "data": {
        "bankLoginStatus": {
            "status": "pending",
            "step": "chooseChallengeMedium",
            "challengePhotoTanData": null,
            "challengeMedia": [
                {
                    "id": "Handy Eins",
                    "name": "********5679"
                },
                {
                    "id": "Handy Zwei",
                    "name": ""
                }
            ]
        }
    }
}

Send the id of the chosen challenge medium via the sendChallengeInput mutation.

mutation {
  sendChallengeInput(taskToken: "foo", "input": "Handy Eins")
}
{
  "data": {
    "sendChallengeInput": "<task token>"
  }
}

Send challenge input

First, check the bankLoginStatus for the specifc step. The step should either be challengeResponse or photoTanChallengeResponse. For the latter case, the challengePhotoTanData will contain a base64 encoded picture to display for the photo tan procedure. In addition, this step contains a challengeInputLabel which can be used as an advice for the user, how he can retrieve his TAN.

{
    "data": {
        "bankLoginStatus": {
            "status": "pending",
            "challengeInputLabel": "Bitte entnehmen sie die TAN der gesendeten SMS ihrer Bank und geben Sie sie hier ein.",
            "step": "challengeResponse"
        }
    }
}

or

{
    "data": {
        "bankLoginStatus": {
            "status": "pending",
            "challengeInputLabel": "Bitte entnehmen sie die TAN der gesendeten SMS ihrer Bank und geben Sie sie hier ein.",
            "step": "photoTanChallengeResponse",
            "challengePhotoTanData": "base64string",
        }
    }
}

Either way, the user should have received a transaction authentication number(TAN) via his chosen challenge type and medium. Send the TAN via the sendChallengeInput mutation.

mutation {
  sendChallengeInput(taskToken: "foo", "input": "123456")
}
{
  "data": {
    "sendChallengeInput": "<task token>"
  }
}

Select Account

The final step of the bank access process is to select the accounts behind a bank login.

You can add the availableAccounts array field to the bankLoginStatus query in order to retrieve the account Ids.

{
  bankLoginStatus(taskToken: "<task token>") {
    status
    message
    availableAccounts {
      accountId
    }
  }
}
{
  "data": {
    "bankLoginStatus": {
      "status": "success",
      "message": null,
      "availableAccounts": [
        {
          "accountId": "<account id>"
        }
      ]
    }
  }
}

Send the chosen account ids via the selectAccount query or do not specify any account ids to choose all accounts.

{
  #select all accounts
  selectAccount(taskToken: "<task token>")
}
{
  #select specific accounts
  selectAccount(taskToken: "<task token>", accountIds:["<account Id 1>", "<account Id 2>"])
}

The returning result contains a selectAccount field with true if the selection was successful.

{
  "data": {
    "selectAccount": true
  }
}

Managing Bank Accounts

Customizing a Bank Account

You may want to add new or modified information to existing bank accounts. This can be done via the customizeAccount mutation.

mutation {
  customizeAccount(accountId: "<account id>", customName: "foo bar")
}

Removing a Single Account

If you want to only remove a single account from the accounts behind a set of credentials, you can hide that account.

This will prevent it from being considered in any backend operation or frontend query except when explicitly asked.

To achieve this you can use the customizeAccount mutation and set the hidden field to true.

mutation {
  customizeAccount(accountId: "<account id>", hidden: true)
}

Removing a Bank Login

Removing a bank login (a set of credentials) will prevent these accounts from being synced and remove all their associated data.

Just use the removeBankLogin mutation to achieve this.

mutation {
  removeBankLogin(bankLoginId: "<bank login id")
}

Using Yoli without a Bank Account

Update Transactions

In order to update your contracts, smart alerts, etc. manually, simply use the updateTransactions mutation. Yoli will synchronize your user's data without saving the transactions to a bank account.

Attention: To guarantee seamless functionality of all features of the Yoli API, the accountId and transactionId fields should be unique and continous. Otherwise, you might experience faulty behaviour of the contracts feature i.e.

mutation {
  updateTransactions(defaultInterval: "MONTHLY", transactions: [
    {
      "paymentPartner": {
        "name": "Fino Digital"
      },
      "accountId": "ua4kUE8z1195",
      "amount": 210748,
      "dateOfTransaction": "2018-10-05T14:48:00.000Z",
      "purpose": "Gehalt Oktober 2018",
      "transactionCode": 999,
      "transactionId": "p4iBJ89M583afaF28rSuPJwF2"
    },
    {
      "paymentPartner": {
        "name": "Fino Digital"
      },
      "accountId": "ua4kUE8z1195",
      "amount": 210748,
      "dateOfTransaction": "2018-11-05T14:48:00.000Z",
      "purpose": "Gehalt November 2018",
      "transactionCode": 999,
      "transactionId": "eQzjT8mOWy6eEzsiajP431O7H"
    }
  ])
}