API Version

Introduction

The Teller API is organized around REST. Resources have predictable, self-describing URLs and contain links to related resources. Our API accepts form-encoded requests and returns JSON encoded responses. It uses standard HTTP status codes, authentication, and methods in their usual ways.

You can use the Teller API in sandbox mode, which is free, does not call out to any real banks, and does not affect your live data. The access token you use determines whether your request is handled in the live or sandbox enviroments.

Access tokens for the live environment are obtained using Teller Connect when a user successfully connects a bank account to your Teller application.

Take a look at the Teller Connect integration guide to learn how to integrate Teller Connect into your application.

https://api.teller.io/
test_token_ebomosvnklpwo
  • User ID: username
  • Password: password

Authentication

The Teller API uses TLS client certificates to authenticate your API requests. Secondly it uses access tokens to identify the user whose bank accounts your API call operates on. Access tokens are tied to your application, they are useless without your client certificate and its private key.

Client certificates are necessary for live production API calls. In the interests of getting up and running as quickly as possible they're not required in the sandbox environment. We recommend using client certificates as soon as possible in order to become familiarised with them.

It's of paramount importance that you keep your private key confidential, anyone that knows it is able to make API requests as you. If you suspect your private key has been compromised, immediately revoke it and request a new certificate from the certificate dashboard. Your new certificate will have a new private key.

Access tokens are encoded using HTTP Basic Auth. The access token is given as the username value, the password field should be left empty and is ignored by the API server. Teller uses access tokens to identify the resource owner and determine that your application has their consent to operate on their resources, i.e. their bank accounts.

http https://api.teller.io/accounts --auth test_token_ebomosvnklpwo:

Errors

Teller uses standard HTTP response status codes to indicate the success or failure of a request. Status codes in the 2xx denote a successful request. Status codes in the 4xx range denote a client error, e.g. not using a client certificate to make the request, a problem with the user access token, etc. Status codes in the 5xx range denote a problem on our end, e.g. a bank is unavailable and it's not possible or otherwise doesn't make sense to gracefully handle the exception.

error
object
An object describing the error condition.
error.code
string
The error condition.
error.message
string
A human readable string describing the error and how to resolve it.
OK A successful request.
Bad Request The request was unacceptable. This status is used when a request that must be made with a Teller client certificate is made without one.
Unauthorized A request was made without an access token where one was required.
Forbidden A request was made with an invalid or revoked access token.
Not Found Requested resource was not found.
Gone Indicates that the resource requested is no longer available and is unlikely to be available again, e.g. because the account was closed.
Unprocessable Entity A request was made with an invalid request body.
Bad Gateway A 500 level response was received when making a request to a financial institution where a graceful fallback is not possible, e.g. a payment instruction.

Versioning

Teller uses dated versions with the latest one being 2020-10-12. By default all API requests will use the version specified in the Teller Dashboard.

In order to test a new version, you can request it using the Teller-Version HTTP header. Once you are ready to upgrade to a new version permanently, you can do so from the dashboard. You will have 72 hours to rollback to the version you were previously using.

http https://api.teller.io/accounts Teller-Version: 2019-07-01

Sandbox

The sandbox environment can be used to simulate and test a variety of scenarios you can incur in while using Teller without having to use real bank accounts. You can use the environment by initialising Teller Connect with the environment property set to sandbox.

Enrollments

Specific enrollment flows are triggered using a combination of the login credentials entered in Teller Connect after selecting an institution. In the sandbox environment, all institutions will behave in the same way. The valid login password is always password so if you want to simulate a bad credentials flow, all you need to do is provide a different value.

username
Leads to an immediate successful enrollment.
otp
Will follow a two-factor authentication flow whereby the user will be asked to select a contact to send an OTP code and then will have to enter 0000 to complete it.
challenge
Makes the user answer a challenge question around its favorite color where the correct answer is blue.
disconnected
Results in a successful enrollment that will immediately disconnect upon the first request made to get data from it. Alternatively you can use any of the enrollment status reasons as usernames (e.g. account_locked).

Payees

Sandbox payee flows are determined by the name value that is used when issuing the create payee request. To immediately create a payee without any user intervention use any value that does not appear in the following list.

ending with "otp" (case-insensitive) e.g. "payee-otp"
The create payee response will contain a connect_token that you can use to initialise Teller Connect and perform a two-factor authentication flow whereby the user will be asked to select a contact to send an OTP code and then will have to enter 0000 to complete it.
ending with "otp_error" (case-insensitive) e.g. "payee-otp_error"
This behaves similarly to the otp value but it results in an error when the correct OTP code (0000) is provided in Teller Connect, simulating a failure from the bank. This failure can be observed with the onFailure hook.

Payments

Sandbox payment flows are determined by the memo value that is used when issuing the create payment request. To get a payment that is immediately successful and does not require user intervention use any value (or none at all) that does not appear in the following list.

otp
The create payment response will contain a connect_token that you can use to initialise Teller Connect and perform a two-factor authentication flow whereby the user will be asked to select a contact to send an OTP code and then will have to enter 0000 to complete it.
otp_error
This behaves similarly to the otp value but it results in an error when the correct OTP code (0000) is provided in Teller Connect, simulating a failure from the bank. This failure can be observed with the onFailure hook.

Enrollment status

When you try to request data that belongs to an enrollment, and that enrollment is disconnected, i.e. Teller is no longer able to fetch live data from the institution, Teller returns a 404 response with enrollment.disconnected error. You should parse the error as a dot-separated string where the first token is enrollment.disconnected and the subsequent tokens, if present, include the reason for disconnection.

When an enrollment gets disconnected, Teller can also send you a webhook event of type enrollment.disconnected.

enrollment.disconnected
When the enrollment is disconnected from the institution the user should reconnect with Teller Connect.
enrollment.disconnected.account_locked
Access to the account was blocked by the institution.
enrollment.disconnected.enrollment_inactive
Access to the enrollment is stale and needs to be updated.
enrollment.disconnected.credentials_invalid
The authentication credentials are not valid anymore.
enrollment.disconnected.user_action.captcha_required
The enrollment requires user interaction in the form of a captcha.
enrollment.disconnected.user_action.mfa_required
The enrollment requires user interaction in the form of an OTP code.
enrollment.disconnected.user_action.web_login_required
The enrollment requires the user to login on the institution's website.

DEPRECATED: teller-enrollment-status header

When you try to request data that belongs to an enrollment, Teller also sends teller-enrollment-status header with values:

  • healthy
  • disconnected
  • disconnected.account_locked
  • disconnected.enrollment_inactive
  • disconnected.credentials_invalid
  • disconnected.user_action.captcha_required
  • disconnected.user_action.mfa_required
  • disconnected.user_action.web_login_required
{
  "error": {
    "code": "enrollment.disconnected.user_action.mfa_required",
    "message": "Enrollment is not healthy"
  }
}
  • GET /accounts/:id
  • GET /accounts/:id/details
  • GET /accounts/:id/balances
  • GET /accounts/:id/transactions
  • GET /accounts/:id/transactions/:transaction_id
  • GET /accounts/:id/payments/zelle/payees
  • POST /accounts/:id/payments/zelle/payees
  • POST /accounts/:id/payments/zelle

Accounts

The Accounts resource represents a collection of the user's bank accounts. Once you no longer need access to an enrollment's accounts, you can revoke it issuing a DELETE on the collection or on the particular resource.

currency
string
The ISO 4217 currency code of the account.
enrollment_id
string
The id of the enrollment that the account belongs to.
id
string
The id of the account itself.
institution
object
An object containing information about the bank that holds the account
institution.id
string
The snake_cased name of the bank that holds the account.
institution.name
string
The Title Cased name of the bank that holds the account.
last_four
string
The last four digits of the account number.
links
object
An object containing links to related objects.
links.self
string
A self link to the account.
links.details
optional string
A link to the account's details, such as account number and routing numbers.
links.balances
string
A link to the account's balances.
links.transactions
string
A link to the account's ledger transactions.
name
string
The account's name.
type
string
The account's type e.g depository.
status
string
The account's status: open or closed.
subtype
string
The account's subtype e.g checking
  • GET /accounts
  • GET /accounts/:id
  • DELETE /accounts
  • DELETE /accounts/:id
{
  "currency": "USD",
  "enrollment_id": "enr_ogr36j4o580e1jug7e000",
  "id": "acc_ogr36j4j6c0c2n73cs000",
  "institution": {
    "id": "omni_ccu",
    "name": "OMNI Community Credit Union"
  },
  "last_four": "5995",
  "links": {
    "balances": "https://api.teller.io/accounts/acc_ogr36j4j6c0c2n73cs000/balances",
    "details": "https://api.teller.io/accounts/acc_ogr36j4j6c0c2n73cs000/details",
    "self": "https://api.teller.io/accounts/acc_ogr36j4j6c0c2n73cs000",
    "transactions": "https://api.teller.io/accounts/acc_ogr36j4j6c0c2n73cs000/transactions"
  },
  "name": "My Checking",
  "status": "open",
  "subtype": "checking",
  "type": "depository"
}
  • depository:
    checking, savings, money_market, certificate_of_deposit, treasury, sweep
  • credit:
    credit_card

Account status

An account can be open or closed. When the account is closed it means that it's closed from Teller's perspective, i.e. Teller can still access live enrollment data from the institution, but the account was physically closed, or Teller can no longer see that account, or the account transitioned to an insufficient-access state.

When you try to request an account or any of its subresources, and that account is closed, Teller returns a 410 response with account.closed error. You should parse the error as a dot-separated string where the first token is account.closed and the subsequent tokens, if present, include the reason for closing.

{
  "error": {
    "code": "account.closed",
    "message": "The requested account is closed"
  }
}

Account Details

The Account Details resource exposes the account's ACH routing number, if different, its wire routing number and its account number.

account_id
string
The id of the account the balance belongs to.
account_number
string
The account's account number.
links
object
An object containing links to related objects.
links.self
string
A self link to the account.
links.account
string
A link to the account that the details belongs to.
routing_numbers
object
An object containing the account's routing numbers.
routing_numbers.ach
optional string
The account's ACH routing number.
routing_numbers.wire
optional string
The account's wire routing number.
routing_numbers.bacs
optional string
The account's BACS sort code.
  • GET /accounts/:id/details
{
  "account_id": "acc_ogr36j4j6c0c2n73cs000",
  "account_number": "401832105995",
  "links": {
    "account": "https://api.teller.io/accounts/acc_ogr36j4j6c0c2n73cs000",
    "self": "https://api.teller.io/accounts/acc_ogr36j4j6c0c2n73cs000/details"
  },
  "routing_numbers": {
    "ach": "823358890"
  }
}

Account Balances

The Account Balances resource represents the account's real-time balances.

account_id
string
The id of the account the balances belong to.
ledger
string
The account's ledger balance. The ledger balance is the total amount of funds in the account.
available
string
The account's available balance. The available balance is the ledger balance less any outstanding holds or debits that have not yet posted to the account.
links.self
string
A self link to the account.
links.account
string
A link to the account that the balances belongs to.
  • GET /accounts/:id/balances
{
  "account_id": "acc_ogr36j4j6c0c2n73cs000",
  "available": "99047.00",
  "ledger": "99047.00",
  "links": {
    "account": "https://api.teller.io/accounts/acc_ogr36j4j6c0c2n73cs000",
    "self": "https://api.teller.io/accounts/acc_ogr36j4j6c0c2n73cs000/balances"
  }
}

Transactions

The Transactions resource represents the known transactions on the account. Each individual Transaction resource exposes an individual transaction on the account either posted or pending.

When you fetch transaction data for the very first time on a busy account we may take longer than usual to complete the request as we collect the full transaction history for the account. If we do timeout you will receive a 504 Gateway Timeout response, and you should try again with a few seconds backoff.

account_id
string
The id of the account that the transaction belongs to.
amount
string
The signed amount of the transaction as a string.
date
string
The ISO 8601 date of the transaction.
description
string
The unstructured transaction description as it appears on the bank statement.
details
object
An object containing additional information regarding the transaction.
details.processing_status
string
Whether the transaction details have been computed yet: complete or pending.
details.category
optional string
The category that the transaction belongs to.
details.counterparty
optional string
The name of the transaction counterparty, e.g. a merchant or person.
status
string
The transaction's status: posted or pending.
id
string
The id of the transaction itself.
links
object
An object containing links to related objects.
links.self
string
A self link to the account.
links.account
string
A link to the account that the transaction belongs to.
running_balance
optional string
The running balance of the account that the transaction belongs to. Running balance is only present on transactions with a posted status.
type
string
The type code transaction, e.g. card_payment.

Pagination

The Transactions endpoint returns all transactions for the given account. Usually this does not represent a large amount of data transfer, but if your application has specific requirements of minimising the amount of data going over the wire the transactions list endpoint supports pagination controls.

Pagination controls are given as query params on the request URL.

GET /accounts/:account_id/transactions?count=5&from_id=txn_test

count
integer
The maximum number of transactions to return in the API response.
from_id
string
The transaction from where to start the page. The first transaction in the API response will be the one immediately before the transaction in the ledger with this id.
  • GET /accounts/:id/transactions
  • GET /accounts/:id/transactions/:transaction_id
{
  "account_id": "acc_ogr36j4j6c0c2n73cs000",
  "amount": "-10.39",
  "date": "2023-05-30",
  "description": "American Express Platinum Card",
  "details": {
    "category": "service",
    "counterparty": {
      "name": "AMERICAN EXPRESS",
      "type": "organization"
    },
    "processing_status": "complete"
  },
  "id": "txn_ogr36j6h6c0c2n73cs00a",
  "links": {
    "account": "https://api.teller.io/accounts/acc_ogr36j4j6c0c2n73cs000",
    "self": "https://api.teller.io/accounts/acc_ogr36j4j6c0c2n73cs000/transactions/txn_ogr36j6h6c0c2n73cs00a"
  },
  "running_balance": null,
  "status": "pending",
  "type": "bill_payment"
}
  • accommodation, advertising, bar, charity, clothing, dining, education, electronics, entertainment, fuel, general, groceries, health, home, income, insurance, investment, loan, office, phone, service, shopping, software, sport, tax, transport, transportation, utilities

Zelle Payments Beta

The Zelle payment network allows the transfer of funds from a checking account to a saved payee's account. Teller enables listing and creating payees, and making payments programmatically from an authorised account.

The following functionality is currently supported for Bank of America, CapitalOne, Chase and Citibank.

Payees

The Payees resource represents the known Zelle payees on the account.

Some institutions may require the account owner to go through a multi-factor authentication (MFA) process in order to create a new payee. If this happens, the response to create a payee request will return a connect_token. The token can then be used as an initialisation parameter in Teller Connect which will prompt the user with the step(s) to complete the MFA process to create a new payee.

account_id
string
The id of the account the payee belongs to.
address
object
An object containing the payee's address information.
address.type
string
The address' type, e.g. email or mobile.
address.value
string
The address' value.
id
string
The id of the payee itself.
links
object
An object containing links to related objects.
links.self
string
A self link to the payee.
links.account
string
A link to the account that the payee belongs to.
name
string
The payee's name.
type
string
The payee's type, e.g. person, business or unknown.
  • GET /accounts/:id/payments/zelle/payees
  • POST /accounts/:id/payments/zelle/payees
{
  "address": {
    "type": "email",
    "value": "ella.baker@teller.io"
  },
  "name": "Ella Baker",
  "type": "person"
}
{
  "account_id": "acc_ogr36j4j6c0c2n73cs000",
  "address": {
    "type": "email",
    "value": "ella.baker@teller.io"
  },
  "id": "zpe_ogr36j7imc0c2n73cs000",
  "links": {
    "account": "https://api.teller.io/accounts/acc_ogr36j4j6c0c2n73cs000",
    "self": "https://api.teller.io/accounts/acc_ogr36j4j6c0c2n73cs000/payments/zelle/payees/zpe_ogr36j7imc0c2n73cs000"
  },
  "name": "Ella Baker",
  "state": "created",
  "type": "person"
}

Payments

You must either specify a payee_id or a payee.

Depending on the kind of payment (beneficiary, amount, etc), some institutions may require the account owner to go through a multi-factor authentication (MFA) process in order to complete the payment. If this happens, the response to create a payment request will return a connect_token. The token can then be used as an initialisation parameter in Teller Connect which will prompt the user with the step(s) to complete the MFA process to confirm the payment.

The creation endpoint supports idempotency for safely retrying payment requests without accidentally performing the same operation twice. To do so, just provide an additional Idempotency-Key header containing a unique value. We store the key and keep the behaviour associated to it for 72 hours.

Any request resulting in a status code of 500 should be treated as indeterminate. There is nothing a client can do to recover it or retry it automatically.

amount
string
The payment amount in dollars and cents (optional) as a string, e.g. "13.37", "10.00", "5"
memo
optional string
A short description of the payment.
payee_id
optional string
The id of the payee where funds will be transfered to.
payee
optional object
An object containing a Payee.
  • POST /accounts/:id/payments/zelle
{
  "amount": "70.23",
  "memo": "Breakfast",
  "payee_id": "zpe_ogr36j7imc0c2n73cs000"
}
{
  "id": "zpay_ogr36j9c6c0c2n73cs000",
  "payee_id": "zpe_ogr36j7imc0c2n73cs000"
}

Webhooks

Teller can send you webhook events related to your application and its accounts, e.g. when an enrollment gets diconnected and the user should reconnect using Teller Connect, Teller will send you a webhook event of type enrollment.disconnected and your application can take appropriate action.

How to listen to webhook events

To start listening, go to the Teller dashboard, and configure your Webhook URL.

Teller sends a webhook event by making a POST request to your configured URL with JSON encoded attributes. Your application should respond with a 2xx status code. If Teller doesn't receive a 2xx status code it will retry sending the webhook event with exponential backoff.

As a best practice, your application shouldn't perform any heavy operations before responding with a 2xx in order to avoid a timeout and a retry.

id
string
The id of the webhook event.
payload
object
Event type specific data.
timestamp
string
The ISO 8601 timestamp of the event.
type
string
The type of the event, e.g enrollment.disconnected.

How to test a webhook

Go to the Teller dashboard, and press the Test button next to your Webhook URL. When you press it, Teller will send you a test webhook event of type webhook.test, and display a message about its delivery status.

How to verify that events were created by Teller

Teller signs every webhook event with all non-expired signing secrets, that only you and Teller know. You can get your signing secrets from the Teller dashboard.

Teller sends a signature in the Teller-Signature HTTP header:

Teller-Signature: t=signature_timestamp,v1=signature_1,v1=signature_2,v1=...

Most of the time there will be only one non-expired signing secret, so the signature header will look like this:

Teller-Signature: t=signature_timestamp,v1=signature

To verify that the payload was created by Teller, you have to calculate the signature and it must be equal to the signature extracted from the signature header.

To calculate the signature:

  1. Create signed_message by joining signature_timestamp and the request's JSON body with a . character
  2. Compute HMAC with SHA-256 using the non-expired signing secret as the key and signed_message as the message

To prevent replay attacks you should reject webhook events with a signature_timestamp (Unix time) older than 3 minutes.

How to expire a signing secret

When you have a policy to periodically roll secrets, Teller allows you to do it without a gap in signature verification.

To expire the current signing secret, go to the Teller dashboard and select when the secret should expire, e.g. in 2 hours. When you press Save, Teller will create a new non-expired secret, and from that moment, Teller will sign all webhook events with both secrets until the old secret expires:

Teller-Signature: t=signature_timestamp,v1=signature_with_new_secret,v1=signature_with_old_secret

This gives you time to update your verification code with the new secret.

Event types

enrollment.disconnected

Sent when an enrollment gets disconnected and the user should reconnect using Teller Connect.

payload.enrollment_id
string
The id of the enrollment.
payload.reason
string
The reason the enrollment was disconnected. Possible values:
  • disconnected
  • disconnected.account_locked
  • disconnected.enrollment_inactive
  • disconnected.credentials_invalid
  • disconnected.user_action.captcha_required
  • disconnected.user_action.mfa_required
  • disconnected.user_action.web_login_required
See Enrollment status for more details.
{
  "id": "wh_ogrhugpol80e1jug7e000",
  "payload": {
    "enrollment_id": "enr_ogrhugpol80e1jug7e001",
    "reason": "disconnected.account_locked"
  },
  "timestamp": "2023-05-30T19:59:31Z",
  "type": "enrollment.disconnected"
}