This guide explains the integration tokens system within Mesh API, encompassing the processes of token generation, renewal, and storage. It can be used to better understand the system so that developers can follow best practices when integrating with Mesh APIs.

What is a “token”

AccessToken or AuthToken is a generated secret (sequence of symbols) that is returned by Mesh API upon successful account authentication and used in subsequent requests to access the user’s data.

Each token represents a connection between a particular user (typically, an end user of a client) and the user’s brokerage/exchange/wallet account.

The token grants access to the user’s data and enables the execution of read and (based on the integration) write operations. Therefore, it is essential to store the token with utmost security measures in place.

Types of tokens

There are 2 types of tokens provided by the Mesh API:

AccessToken

A token that allows performing read and write operations with the connected account.

Can be obtained in the following ways:

RefreshToken

A token that is returned by some integrations and needs to be used to refresh the access token before it expires.

Provided typically by OAuth and username password integrations, it is critical that it is handled correctly in order to ensure continued connections to accounts.

Can be obtained in the following ways:

— in a Link JS callback upon successful authentication managed authentication - recommended

General design

An important difference between Mesh and similar platforms is that Mesh does not store authentication tokens generated by integrations. Instead, Mesh API gets tokens from integrations, encrypts them and then returns back to API clients.

The main benefit of this approach is security. Even having access to Mesh’s system does not make it possible to manipulate customer accounts. Even Mesh employees are not able to use connections to integrations to perform operations with accounts.

Considering that Mesh supports critical write actions (trading and transfers) this approach makes the platform way safer compared to similar platforms that store tokens on their side.

However, there are also several disadvantages:

  • Clients have to manage tokens on their side - store and refresh them when necessary
  • Tokens are of different lengths
  • Different integrations return tokens in different formats - each with its own Time-To-Live (TTL), some of them require to be refreshed while other ones do not, and so on
  • There are some exceptions that have to be handled manually - for example, Delay authentication approach (Interactive Brokers) or the requirement to refresh the RefreshToken in some integrations

Data model

Successful Authenticate or RefreshToken call result (only relevant fields are shown):

FieldTypeDescriptionNote
AccessTokenstring (can be null)The access token that is used to access user’s dataProvided when the connection contains only one account
RefreshTokenstring (can be null)The refresh token that is used to refresh the access tokenOptional and depends on the specific integration. Some integrations do not provide refresh tokens
ExpiresInSecondsnumber (can be null)Specifies the duration, measured in seconds, after which the Access token expires and becomes unavailableOptional, depends on the integration. If not provided, it indicates that the token associated with that integration does not have an expiration time and can be used indefinitely
RefreshTokenExpiresInSecondsnumber (can be null)Specifies the duration, measured in seconds, after which the Refresh token expires and becomes unavailableOptional, depends on the integration. If not provided, it indicates that the Refresh token associated with that integration does not have an expiration time and can be used indefinitely
AccountTokensArray (can be null)Contains the list of account tokens if the integration provides multiple accountsOptional, used only if the integration provides multiple accounts or “subaccounts”. If provided, AccessToken and RefreshToken are not provided
AccountBrokerAccountGeneral information about the connected account (AccountId, AccountName and so on)

BrokerAccount model:

FieldTypeDescriptionNote
AccessTokenstring (can be null)The access token that is used to access user’s data
RefreshTokenstring (can be null)The refresh token that is used to refresh the access token
AccountBrokerAccountGeneral information about the connected accounteg. (AccountId, AccountName and so on)

Structure of token responses by integration types

API key integrations

Typically, API key integrations do not have an “access token” concept. Authentication is based on constant ApiKey and ApiSecret values that are obtained by users and provided to Mesh API during authentication. Mesh API does not store these values, instead, the API concatenates and encrypts them. The result is returned in the AccessToken field. Since the accessToken contains encrypted ApiKey and ApiSecret values and not actual integration’s tokens, there is no need to refresh them. That’s why the RefreshToken is typically not returned for such integrations.

Kraken API Example

{
    "content": {
        "status": "succeeded",
        "accessToken": "YHeBTUxgdIcLUZNCEOg3hA==......",
        "accountTokens": []
    },
    "status": "ok",
    "message": ""
}

Please note:

  • RefreshToken is not provided (there’s no need to refresh this token)
  • ExpiresInSeconds is not provided (the token never expires)
  • RefreshTokenExpiresInSeconds is not provided (there’s no refresh token)

Email-password integrations

Token data of username-password integrations may vary, based on specific implementations of underlying APIs of brokerage integrations. In most of the cases they follow a JWT token authentication approach and require to refresh the accessToken.

Robinhood Example:

{
    "content": {
        "status": "succeeded",
        "accessToken": "Wi13vA35s10si1i330Cy7w==.................",
        "refreshToken": "He536LzN3Pa11aP9GgusYg==.zwafsZqwIS8n0Pe4kqYu4lqdee4kO4uz3FzBBuopWztF0zkHyDN04ogGwGC5dY8UwnsxX2j7nQTyGdeFsgi32kfSqyMWYl........",
        "expiresInSeconds": 734000,
        "brokerAccountTokens": []
    },
    "status": "ok",
    "message": ""
}

Please note:

  • ExpiresInSeconds is provided, the value 734000 indicates that the access token will be expired after 734000 seconds (approximately 8 days)
  • RefreshToken is provided, and should be used to refresh the access token before access token expires
  • RefreshTokenExpiresInSeconds is not provided, it means that the refresh token never expires, and can be used even after the access token is expired

OAuth integrations

OAuth implementations may also vary. For example, Alpaca does not provide the RefreshToken. They provide the AccessToken only which means that the AccessToken never expires.

But generally, typical token response contains both Access and Refresh tokens and requires only the AccessToken to be refreshed.

Coinbase Example:

{
    "content": {
        "status": "succeeded",
        "accessToken": "6czWll8LTs5RLC2nm/K9HQ==.NcR3ErlhbwvnfEfLD8R/5uOeEOeOEqQ8pRkRE3esfSdj1htdeIduOKm8vCNhrL284YKfnYWcJ/nNcfomGo/DFf0vVJTxTp+6YjIraEKfP10yLu0tP+OPp+3juDybdm8Y1oksiJoFUgyVgViq4+3jMrnY.....",
        "refreshToken": "zE6Q+/Xltg0RbvtEi0VvJA==.UBMzYzt0eCVAF7F4p2tm12MU8uzW5efEhYpt7GLlflBdEXvZOL6gN2/btzLQiX+0zcleLSbU/DR3j1nlGCNur5rWLr7fiui/8W6rSe...",
        "expiresInSeconds": 7200,
        "brokerAccountTokens": []
    },
    "status": "ok",
    "message": ""
}
  • ExpiresInSeconds is provided, the value 7200indicates that the access token will be expired after 2 hours, so in this case it has to be refreshed oftenly
  • RefreshToken is provided, and should be used to refresh the access token before access token expires
  • RefreshTokenExpiresInSeconds is not provided, it means that the refresh token never expires, and can be used even after the access token is expired

Self Custody Wallets

Because of the nature of DeFi wallets, Mesh API never receives any sensitive information related to a wallet connection, like the private key or the seed/mnemonic phrase of the wallet. So technically, there’s no need to use secret tokens. But in order to follow the approach that is used with other integrations, Mesh generates the accessToken for DeFi wallet connections and uses the token to identify the connected chain type and connected address. Since there’s nothing to refresh, Mesh API returns the accessToken only:

{
    "content": {
        "status": "succeeded",
        "accessToken": "2ndqYOsU0vClpt3qmGccFQ==.Nlhbu3AuPdQnGTdWWYq527r0gitUSJPX2dSV0h2MChxg1sF8RAT/fOe1cjo4O3kb3N1s/kWrWi6xNKudNuj4bQjvAV7AJWFaPWE4wPQ6vzgiMUvczJGMVfTWicJStQVrzUsHb1/McM82Dp+9xpP/jB9oLBKR11EgMkkN....",
    },
    "status": "ok",
    "message": ""
}

Subaccounts and multiple accounts

Integrations have different structure of accounts. For example:

  • Robinhood and Coinbase allow to have one account only
  • Kraken allows multiple accounts, but each should be registered using a different email, so every account is completely separate
  • Binance account contains several subaccounts by default - “Spot” account and “Margin” account
  • Some exchanges have a concept of “Funding” account (that is used to deposit and withdraw fiat and crypto) and “Trading” account (that is used for trading only).

Because of a completely different nature of account handling by integrations, Mesh API does not have a concept of subaccounts. Instead, for integrations that support multiple accounts, Mesh API returns them on the same level, as completely equal accounts.

Binance Example:

{
    "content": {
        "status": "succeeded",
        "accountTokens": [
            {
                "account": {
                    "accountId": "953c03fb6f562805600de4f386495b2fe4199049:Spot",
                    "accountName": "Binance International Spot Account",
                },
                "accessToken": "H4LYskKAOpF8FImBCxAa4g==.aKqJKG5/LL0r9dyStTeZLM2P8V647J1gf3FdwcGKtB3Pjl2/zciT/HfhmmYbrIVUvg0QSEI78Dc4AJeyVGKXkT8Eeu4sppMfxmd0Tv9VnxMIzS/W9ma1JabJOVNGyPEJBqs0j03mgrXz31LzCBvaMOW......=="
            },
            {
                "account": {
                    "accountId": "953c03fb6f562805600de4f386495b2fe4199049:Margin",
                    "accountName": "Binance International Margin Account",
                },
                "accessToken": "1BFmQaFUkvzdulWM6PxtWQ==.LgKuTfz89WQY0kISemLeVXx3Mhxy7pgEA9HFbqMaEr32hGk+ds9ylDmpvXActgar2PfrxFLDOobdBtMmUh3c0WmsmeBU2p1+u90Q01F4Cl4vYMPQcFvRyhOJA/wR1X8kZFsxZeQjwmnPv+V6sKRQ8pZ4TspPAr1f....=="
            }
        ]
    },
    "status": "ok",
    "message": ""
}

Note that:

  • top level AccessToken and RefreshToken fields are empty, and both pairs of AccessToken and RefreshToken are provided in accountTokens field.

Refresh Token Implementation

Best Practices

Suggested logic on how to implement the flow to refresh tokens to cover all possible scenarios properly:

  • Check if expiresInSeconds value is present
  • If expiresInSeconds is present, it means that the accessToken should be refreshed before it is expired
  • Create a logic that registers the timestamp when the accessToken is obtained, records the expiresInSeconds and uses the refreshToken to refresh the token before timestamp + expiresInSeconds comes
  • If refreshTokenExpiresInSeconds is also provided, create the same logic for the refreshToken - it should be refreshed using /api/v1/token/refresh call with CreateNewRefreshToken: true parameter before timestamp +refreshTokenExpiresInSeconds time comes
  • Check the documentation for the exceptions and implement them for the required integrations.

Bad Practices

It’s a bad practice to rely on 401 responses and update Access and Refresh tokens after they have expired. Integrations may track this behavior and block clients that do not refresh tokens properly.

The proper approach is to handle expiresInSeconds and refreshTokenExpiresInSeconds values properly and refresh token/tokens before tokens expire.

Exceptions

There are some exception related to token flows related to limitations of specific integrations.

They are mostly related to the refreshToken and described in the documentation.

Interactive Broker: Delayed Authentication

Interactive Brokers API has a limitation. The problem is that when user is authenticated against the API for the first time before 16:55 EST, the IBKR API does not return information about user’s accounts (number of available accounts, their IDs etc.). This information becomes available only the next day (after the “nightly” refresh of the API).

This happens with accounts that were never connected to the API before.