Connect a user
The Vezgo Connect process allows users to connect their exchange accounts or wallet addresses to your application. There are different ways to start the process and receive the connection result.
The easiest way to do that is using the Vezgo JS SDK to open the drop-in Vezgo Connect widget right inside your web application, and receive the result via JS callbacks.
Or you can use an oAuth process where you redirect users to the Vezgo Connect URL to connect their account, and have them redirected back to your application with the result. This oAuth process can be used in web, mobile or desktop applications.
Via drop-in Connect widget
document.getElementById('#connect').addEventListener('click', () => {
user.connect().onConnection(accountId => {
// Send the account id to your server
sendToServer('/some-route', accountId);
// Request account data to display in the page
const account = await user.accounts.getOne(accountId);
displayAccount(account);
});
}, false);
Start the connect process by calling user.connect()
from your web application. It will open the Vezgo Connect widget inside the page, and let the user select the exchange / wallet they want to connect.
For how to customize the widget, see /customize-connect-widget
Receive via callbacks
user.connect().onConnection(async accountId => {
console.log('account connection success:', accountId)
}).onError(error => {
console.error('account connection error:', error)
}).onEvent((name, data) => {
console.log('event:', name, data);
});
The drop-in Connect Widget sends data to the Vezgo Frontend SDK via 3 callbacks:
onConnection(accountId)
onError(error)
onEvent(name, data)
onConnection(accountId)
The onConnection()
callback listens for successful connection from the Connect Widget. After user successfully connects their account, the Widget is automatically closed and the callback is called with an accountId
that belongs to a newly-created Vezgo account. This Vezgo account is kept in sync with the user's connected exchange account / wallet.
It is recommended that you store this accountId
in your database for future updates.
onError(error)
The onError()
callback listens for errors from the Connect Widget. During the process, if there is an error or if user clicks the close (X) button, the Widget is closed and the callback is called with an error
object.
error object attributes
Attribute | Description |
---|---|
error_type | The error code, e.g. 400. |
message | The error message. |
account | The account ID if it has been created. |
onEvent(name, data)
The onEvent()
callback listens for events from the Connect Widget during the connection process. There are multiple events corresponding with the multiple steps of the connection process that keep your web application notified of the process inside the Widget so it can act accordingly.
Supported events
Event | Data | Description |
---|---|---|
APP_LOADED | {} | The Widget is ready (fully loaded and waiting for user actions). |
SCREEN_LOADED | { screen } | Screen successfully loaded. Screens: ACCEPT_TERMS_AND_POLICY , SELECT_PROVIDER , PROVIDE_CREDENTIALS , CONNECTION_SUCCESS , EXIT . |
TERMS_AND_POLICY_ACCEPTED | {} | User has accepted Vezgo terms and policy. |
ERROR | { type } | Error occurred. Available error types: PAGE_NOT_FOUND , OAUTH_ERROR , CLIENT_ID_REQUIRED , INVALID_ORIGIN , TOKEN_REQUIRED , WRONG_CLIENT_ID , WRONG_REDIRECT_URI , INVALID_ACCOUNT_ID , RECONNECT_CONFLICT |
CLOSE | {} | The Widget has been closed automatically. Directly followed by onConnection() or onError() . |
FORCE_CLOSE | {} | The Widget has been closed by the user. Directly followed by onError() . |
PROVIDER_SEARCH | { query } | User has searched for a provider in the provider selection screen.query contains the search string which the user typed in. |
PROVIDER_SELECTED | { provider } | User has selected a provider.provider is the Provider object. |
SUBMIT_CREDENTIALS | {} | User has submitted their credentials for the exchange/wallet. User credentials are secured and won't be available to the SDK. |
SUBMIT_ANSWER | {} | User has submitted their answer to security question. |
START_OAUTH | {} | User started oAuth flow. |
RETURNED_FROM_OAUTH | {} | User returned to Vezgo from third party oAuth service. |
OPEN_METAMASK | {} | User opened MetaMask. |
OPEN_WALLET_CONNECT | {} | User opened Wallet Connect. |
OPEN_LEDGER | {} | User opened Ledger. |
OPEN_TREZOR_CONNECT | {} | User opened Trezor Connect. |
Preselect a provider
document.getElementById('#connect_bitcoin').addEventListener('click', () => {
user.connect({ provider: 'bitcoin' }).onConnection(accountId => {
console.log('bitcoin connection success:', accountId)
});
}, false);
By default .connect()
will open the Vezgo providers list for user to select from. Alternately you can pass in a provider name to skip the selection step so that Vezgo Connect widget go straight to the provider credentials form.
NOTE: Pass in the provider name
that you get from the Provider object, not display_name
.
Reconnect an account
When an account's credentials expire or are revoked, Vezgo will no longer be able to synchronize its data from the provider. The account is put in a 'disconnected' state and will need to be reconnected.
To allow a user to reconnect a disconnected account, call user.reconnect(accountId)
from your web application. It will open the Vezgo Connect widget inside the page, and let the user reconnect the account. The process is almost identical to connecting a new account, except that at the end of the process it would update the existing account instead of creating a new one.
Connect Multiple Wallets
You can enable the ability to connect multiple wallets and multiple networks for the wallet address in one take inside one flow. This is useful for users who have multiple wallets or use multiple networks for the same wallet (e.g. Ethereum, BNB, Polygon...).
user.connect({
multiWallet: true,
// additional options
}).onConnection((result) => {
// overall status message
console.log(result.message);
// list of connected or failed accounts will be returned in multiWallet mode
result.accounts.map(async (acc, key) => {
console.log(acc.account, acc.message, acc.wallet, acc.network);
// send the account to your server one by one or handle the error if account is not provided
if(acc.account) {
await sendToServer('/some-route', acc);
} else {
console.error(acc.message);
}
});
});
Multi account connection example:
Via Connect URL
Another way to connect an user is via the Vezgo Connect URL directly. You can load the Vezgo Connect URL using your own method (redirect from the current page or open in a new window, or inside a mobile WebView...).
You can customize various aspects of the Vezgo Connect process via the url params. A standard Vezgo Connect URL looks something like this:
POST https://connect.vezgo.com/connect?client_id=YOUR_CLIENT_ID
Form Data (x-www-form-urlencoded):
token=USER_TOKEN
Or with preselected provider:
POST https://connect.vezgo.com/connect/bitcoin?client_id=YOUR_CLIENT_ID
Form Data (x-www-form-urlencoded):
token=USER_TOKEN
How to get correct provider name:
let slug = provider;
// Use the last alternate name as slug if available
if (provider.alternate_names) {
slug = provider.alternate_names[provider.alternate_names.length - 1];
}
You can use providers endpoint to see alternate_names
of a particular provider.
To reconnect account:
POST https://connect.vezgo.com/reconnect/ACCOUNT_ID?client_id=YOUR_CLIENT_ID
Form Data (x-www-form-urlencoded):
token=USER_TOKEN
The same Connect URL is loaded in the iframe when you connect via the drop-in Connect Widget using the JS SDK. The SDK just automatically encapsulates all handling logic in an easy-to-use interface to save you time.
Connect URL parameters
Parameter | Required | Description |
---|---|---|
client_id | Yes | Your team's Client ID. |
token | Yes | The user token. See Authentication to see how to get the token. |
redirect_uri | No | The URI to redirect to after the process is finished. This must match one of your team's registered Redirect URIs. More on this here. |
origin | No | The URL origin that triggers the process (e.g. https://some.domain ). This is used mostly for when the Vezgo Connect is loaded inside an iframe, so it can pass data back to the parent page at the specified origin. |
lang | No | The content language. Currently only en , es , fr and it are accepted. |
state | No | An opaque string containing your application state, to maintain application state between redirects (in the oAuth process). |
provider_categories | No | Show providers only from selected categories. Available options: exchanges,blockchains,wallets . |
providers | No | Limit the list of providers available for selection. Example: binance,coinbase,ethereum . |
theme | No | Vezgo Connect widget theme. Available options: light , dark . Default: light . |
providers_per_line | No | The number of providers displayed per line on the select provider screen. Available options: 1 , 2 .Default: 2 . |
sync_nfts | No | Show "Sync NFTs" checkbox. Available for proviers which support "sync_nft" feature and also if "sync_nft" feature enabled for the team (depends on plan). Boolean. Default: false . |
multi_wallet | No | Enable ability to connect multiple wallets and multiple networks for the wallet address in one take inside one flow. Boolean. Default: false . |
hide_wallet_connect_wallets | No | Hide all WalletConnect wallets from the list of all providers. Boolean. Default: false . |
Receive via URL params
The connection result is returned in the URL params at the end of the process. If you open the Connect URL inside an iframe, popup, or mobile WebView, you can expect the process to redirect to this URL when connection is successful:
https://connect.vezgo.com/connected?account=ACCOUNT_ID&code=ACCOUNT_ID
And to this URL when connection failed or is interrupted:
https://connect.vezgo.com/exit?error_type=400&message=ERROR_MESSAGE
Custom Redirect URI
You can pass in the Vezgo Connect URL a custom redirect_uri
so that it redirects to this uri at the end of the process.
For example, load the following Vezgo Connect URL:
POST https://connect.vezgo.com/connect?client_id=YOUR_CLIENT_ID&redirect_uri=https://myapp.test/crypto&state=APP_STATE_BEFORE_CONNECTION_IN_BASE64
Form Data (x-www-form-urlencoded):
token=USER_TOKEN
After user finishes connecting an account, it will redirect to:
https://myapp.test/crypto?account=ACCOUNT_ID&code=ACCOUNT_ID&state=APP_STATE_BEFORE_CONNECTION_IN_BASE64
Or if the connection failed or user closes the process, it will redirect to:
After user finishes connecting an account, it will redirect to:
https://myapp.test/crypto?error_type=400&message=Invalid+credentials&state=APP_STATE_BEFORE_CONNECTION_IN_BASE64
In both cases, your page at https://myapp.test/crypto
can look at the URL params and proceed accordingly. Optionally if you passed in a state
, you will receive it back here to restore your application to the same state before the connection.
All redirection URL params
Parameter | Type | On | Description |
---|---|---|---|
account | String | Both | The Vezgo account id corresponding to the newly-connected exchange account / wallet. Note that it's returned on Failure ONLY if it has been created before the error happens. |
code | String | Success | In default mode same as account for oAuth compliance. In multi_wallet it mode returns object with connection details. Ex. { message: 'MESSAGE', accounts: [{account: 'ACCOUNT_ID', wallet: 'WALLET_NAME', network: 'NETWORK_NAME'}] } |
error_type | Number | Failure | The connection error code. |
message | String | Failure | The connection error message. |
force | Boolean | Failure | true means the process was explicitly closed by user. |
state | String | Both | The application state passed to the Vezgo Connect URL at the start of the process (if available). |
Via Direct API (advanced)
It's possible to implement your own Connect UI using the Vezgo Accounts API. But the process is complicated and thus not recommended. Only supported in certain Vezgo subscription plans.
Connect new account
POST /accounts
with the credentials. If everything is valid, Vezgo should return202 Accepted
with the account object. A new interactive sync should start andaccount.status
should besyncing
at this point.GET /accounts/:id/poll?v={account.v}
to poll for an update and act according toaccount.status
syncing
: the account is still syncing but there has been some progress. Ifaccount.authorized
istrue
, this means the sync has gotten past the login/authentication process, and is fetching new data. Your application may choose to stop polling at this point if you don't need to show the balances to your user right away.ok
: The sync has been successful.error
: There's been an error with the authentication process, usually because of invalid credentials or a verification step. You should see"status": "error", "error.name": "LoginFailedError|SecurityQuestionError"
in the account object.LoginFailedError
: Invalid credentials. In this case you would need to update the credentials by callingPUT /accounts/:id
with the new credentials. Similar to 1), you would receive a202 Accepted
status and"status": "syncing"
. Then go back to polling.SecurityQuestionError
: Verification is required (mostly for exchanges with username/password authentication). There should be a security question or 2FA request insideaccount.error
. The question would be inaccount.error.message
, e.g."error.message": "Your one-time security code will be sent to number ending with XXX"
. Your application would send the answer (verification code in this case) by callingPUT /accounts/:id
with{ "security_answer": "thecode" }
in the request body. Then go back to polling. If you wait too long to send the answer, then the answer or the interactive session might expire and you will need to start over by triggering a new sync.
retry
: there has been an unexpected problem and the sync has been rescheduled in the next hour. It's advised to report to us the account id so we can investigate the problem. This usually happens when we are unable to connect to the exchange or the data service.- If a 404 is returned, it means the credentials is invalid and the account has been automatically deleted from the Vezgo side. This is equivalent to
LoginFailedError
but should happen only for new connections that have never synced, to minimize the number of abandoned connections. In this case, go back to 1) to create a new connection.
Each poll takes up to 30 seconds to return the latest account object if there is an update. Otherwise, it would return 304 and your application is expected to poll again until there is a new account.status
. It might take from seconds to a couple of minutes to return a new status depending on the provider and the account. If it takes too long to get a new account.status
, there's a chance there was an error, in which case the application should stop polling.
The polling process should be similar for when you connect a new account, sync or fix an existing connection
Reconnect/fix a connection
Sometimes a connection might fail to sync because of changed/revoked credentials or 2FA verifications. In that case, when you fetch the connection by doing GET /accounts/:id
, you should see "status": "error"
and the error type inside account.error.name
LoginFailedError
:
- Update the credentials by calling
PUT /accounts/:id
with the new credentials. A202 Accepted
should be returned and a new interactive sync should start. It's advised to have this triggered by the user, so they are around to handle any 2FA verification if needed. - Poll for update and act accordingly.
SecurityQuestionError
:
You might see the question under account.error.message
and be tempted to send an answer. However most of the time when you see this error, the 2FA session has already expired. You can confirm that by checking account.status_details.date
to see the date when the error was returned. If it's past 5 minutes since the error was set, then it's likely the session has expired. Some providers have even shorter 2FA sessions (under 60s).
Even if it looks like the session is still active, it might not be possible to send an answer because the sync was likely started in non-interactive mode (i.e. daily syncs). So most of the time you would need to first trigger a new interactive sync:
Call
POST /accounts/:id/sync
with{ force: true, interactive: true }
in the POST body.- Passing
interactive
enables interactive mode, which means the sync would accept 2FA answers. It's advised to have this triggered by the user, so they are around to handle any 2FA verification if needed. - Passing
force
allows starting a new sync even though an existing sync is already in progress.
- Passing
Poll for update and act accordingly. If you get a new
SecurityQuestionError
, send the answer and poll again.
Sync a connection
Trigger a sync by calling
POST /accounts/:id/sync
with{ interactive: true }
in the POST body.- Passing
interactive
enables interactive mode, which means the sync would accept 2FA answers. It's advised to have this triggered by the user, so they are around to handle any 2FA verification if needed.
- Passing
Poll for update and act accordingly.
Connecting with oAuth providers
OAuth providers (e.g. Coinbase) require a specific workflow to connect. Some important notes:
- The oAuth process must be opened as Vezgo (using the oAuth Client Id provided under
provider.client_id
). - At the end of the process, the oAuth result (
code
or errors) is always returned to the Vezgo OAuth Redirection service athttps://api.wealthica.com/oauth/redirect
. This redirection service will take care of returning the oAuth result back to your app. You will need to register your Redirect URI prior to the integration.
Detailed integration instructions
The process must be initiated for providers with "auth_type": "oauth"
(from the /providers
endpoint). The information needed to initiate the process is provided in the provider data, for example:
{
"name": "Coinbase",
"auth_type": "oauth",
"client_id": "theclientid",
"authorize_url": "https://www.coinbase.com/oauth/authorize?account=all",
"available_scopes": [{ "name": "wallet:user:read", ... }]
}
Upon registering your Redirect URI with Vezgo, you will be provided a sub
string corresponding with the Redirect URI. You can register multilple Redirect URIs (for dev, staging and production environments), each with its own sub
. For example:
- Sub: "myappdev", URI: "https://dev.myapp.com"
- Sub: "myappprod", URI: "https://prod.myapp.com"
- Sub: "myappios", URI: "com.myapp://oauth"
// STEP 1: Build oAuth URL
const OAUTH_REDIRECT_URI = "https://api.wealthica.com/oauth/redirect";
const OAUTH_SUB = 'myappdev';
// If you have different receiving paths for different flows (e.g. connect vs reconnect), you can
// provide a path. It will be concatenated with your registered URI.
// E.g. https://dev.myapp.com/connect, https://dev.myapp.com/reconnect/someaccountid
const OAUTH_PATH = '/connect';
// Prepare oAuth state.
// The Vezgo OAuth Redirection service will redirect based on your `sub` and (optional) `path`.
// You can also pass your client state info here.
const oAuthState = {
sub: OAUTH_SUB,
path: OAUTH_PATH,
...additionalStateProps,
};
const state = encodeBase64(oAuthState);
// Build the oAuth Authorize URL
const url = new URL(provider.authorize_url);
url.searchParams.append("client_id", provider.client_id);
url.searchParams.append("response_type", "code");
url.searchParams.append("state", state);
url.searchParams.append("redirect_uri", OAUTH_REDIRECT_URI);
if (provider.available_scopes) url.searchParams.append("scope", provider.available_scopes.map((scope) => scope.name).join(" "));
// STEP 2: Start the oAuth process
// User will be presented with the provider's oAuth page (Coinbase in this example).
// E.g. https://www.coinbase.com/oauth/authorize?account=all&client_id=theclientid...
openURL(url.href);
// STEP 3: Handling oAuth result
// After user completes the process, the oAuth result will be passed to your Redirect URI.
// E.g. https://dev.myapp.com/connect?code=somecode&state=thestate...
// Your app will then be responsible to send the code to the Vezgo API. Either:
// Send a POST to `/accounts` endpoint to create a new connection
createAccount({
name: "Coinbase",
provider: "coinbase",
credentials: {
code: 'somecode'
}
});
// Or send a PUT to `/accounts/:id` to update the existing connection
updateAccount({
id: 'existingconnectionid',
credentials: {
code: 'somecode'
}
})