Get Started
Welcome to Vezgo! As the crypto ecosystem grew exponentially, it became incredibly fragmented.
We created Vezgo to make it easy for app developers to integrate with cryptocurrency exchanges, wallets, and blockchains.
Our goal is to provide a single API to access and aggregate all of your customers' digital asset accounts, balances, NFTs, and transaction history.
Security is paramount to us. We're SOC 2 Type II certified and secure data with AES-256 encryption. We've been in the financial API business since 2015. To learn more about the security measures in place, visit our Security page.
This API Docs will show you how to get started and how to get the most out of the API.
To get started and to receive your Client ID and Client Secret, sign up on the portal.
Integrations
Vezgo's API provides integration with over 40 exchanges, 20 blockchains, and 250 wallets. See the full list of supported providers for the details on the coverage.
The API allows you to easily retrieve & aggregate your users' accounts, positions, balances, and historical transactions from exchanges (CEXes), wallets, and blockchains.
For integration, we offer Vezgo's out-of-the-box Connect Flow, which requires no development on your end.
Vezgo’s Ready-to-Use Connect Flow
We offer a ready-to-use Connect Flow through a JS SDK to speed up and simplify the implementation of Vezgo in your software.
Here’s an overview of Vezgo’s Connect Flow:

URLs
These are the base URLs for making API requests or composing a Vezgo Connect URL for users to connect their wallets.
API Base URL
https://api.vezgo.com/v1
Connect Base URL
https://connect.vezgo.com/connect
Sign-up for API Keys
Go to Vezgo’s Portal and create an account.

Following these steps will automatically create your app, and take you to your portal. You now have access to your unique Client ID and Client Secret. Important: Your Client Secret is only available at this time. Please make a note of it and keep it in a safe place.


The free version of the API includes 5 connections which includes accounts, balances and limited number of transactions. For access to webhooks and other features, you need a full access key, that can be activated in our portal. See our Pricing page for more details. Book a meeting if you need any help or have any questions: Book a meeting
Quickstart example
The best way to get started with Vezgo API is via the Vezgo JS SDKs (Frontend and Backend). You can take a look at the Vezgo Example project or follow the steps below:
1. Import the SDK
You can import the distribution build directly in your webpage:
<!-- Import the specific version -->
<script src="https://unpkg.com/vezgo-sdk-js@2.0.8/dist/vezgo.umd.js"></script>
<!-- Or import the latest version (may contain breaking changes) -->
<script src="https://unpkg.com/vezgo-sdk-js/dist/vezgo.umd.js"></script>
Or install and import it in a JS build system / NodeJS.
The same package is used for both frontend (web, mobile or desktop app) and backend (NodeJS).
npm install --save vezgo-sdk-js
// commonjs
const Vezgo = require('vezgo-sdk-js');
// esX
import Vezgo from 'vezgo-sdk-js';
// React Native
import Vezgo from 'vezgo-sdk-js/dist/vezgo.es';
2. Initialize the vezgo instance
A vezgo instance is initialized using your Client ID and Client Secret in the backend, or only Client ID in the frontend.
const vezgo = Vezgo.init({
clientId: 'YOUR_CLIENT_ID',
secret: 'YOUR_CLIENT_SECRET',
});
const vezgo = Vezgo.init({
clientId: 'YOUR_CLIENT_ID',
// optional parameters for authenticating the vezgo frontend instance with your server.
// See Authentication section for a more detailed explanation.
authEndpoint: '/vezgo/auth', // default value, need to accept POST
auth: {
params: { userId: 'LOGGED_IN_USER_ID' }, // custom params for authEndpoint
headers: {}, // custom headers for authEndpoint
},
});
Log in a user to create a user instance if you want to request user data.
const user = vezgo.login('USERNAME');
// No need to pass username because it's assumed that the `authEndpoint`
// implementation on your backend server should already know that from
// your authenticated user.
const user = vezgo.login();
3. Connect a user, or request for data.
Start the Connect process for users to connect their institutions, or call the SDK helper methods to request data.
// Get general Vezgo data
const providers = await vezgo.providers.getList();
// Get user data via the `user` instance
const account = await user.accounts.getOne('ACCOUNT_ID');
// Get general Vezgo data
const providers = await vezgo.providers.getList();
// Start the Connect process when user clicks the Connect button
document.getElementById('connect').addEventListener('click', () => {
user.connect().onConnection(async accountId => {
const account = await user.accounts.getOne(accountId);
displayAccount(account);
});
}, false);
Vezgo Connect Flow Schema

The Vezgo Connect Schema outlines the process for integrating with the Vezgo API to manage user connections, financial institutions, and account data. This step-by-step guide explains how to:
- Obtain API keys.
- Create users with a unique
loginName. - Connect to financial accounts.
- Retrieve and sync account data.
- Use the data in your application.
It also emphasizes the importance of the loginName as a unique identifier linking users between your app and Vezgo.
Step 1: Obtain API Keys
- What: Request API keys from the Vezgo Team to authenticate API requests.
- How:
- Create new API keys via the Vezgo Portal.
- Keys can be generated for different environments (e.g., local, staging, pre-production, production).
- Why: API keys are required to initiate all interactions with the Vezgo API.
Example:
- API Key for Staging Env: Used for testing.
- API Key for Production Env: Used for live deployment.
- API Key for Other Envs: For additional environments like local or pre-production.
Step 2: Create Users
- What: Create users on Vezgo using an API key and a unique
loginName. - How:
- Send a
POST /auth/tokenrequest with theloginNamein the header. - Vezgo issues a bearer token for that user to start the Vezgo Connect Widget or make further API requests.
- Send a
- Why: The bearer token authorizes actions on behalf of the user, ensuring secure access to their data.
Example:
- API Key for Production Env creates:
- User 1 with
loginName: user_12345→ Gets bearer token. - User 2 with
loginName: user_67890→ Gets bearer token. - User 3 with
loginName: user_98765→ Gets bearer token.
- User 1 with
Step 3: Establish Account Connections
- What: Connect users to their crypto, blockchain, CEX, and wallet accounts using the Vezgo Connect Widget.
- How:
- Users provide their account credentials (e.g., exchange login, wallet address) through the widget.
- Each user can connect to multiple accounts.
- Why: Connections enable Vezgo to access and sync financial data from the accounts.
- Details:
- Once a connection is established, data syncs daily.
- Manual sync can be triggered if immediate updates are needed.
Example:
- User 1 → Connected Account 1 (e.g., Coinbase - a centralized exchange (CEX)).
- User 2 → Connected Account 2 (e.g., Bitcoin wallet - a blockchain/wallet).
- User 3 → Connected Account 3 (e.g., Ethereum account via MetaMask - a wallet/blockchain).
Step 4: Retrieve and Sync Data
- What: Fetch financial data from connected accounts using API requests.
- How:
- Use endpoints like:
GET /accounts/{account_id}to retrieve data for a specific account.GET /accountsto get a list of all accounts.GET /transactions/{account_id}to fetch transaction history (ensuring transactions are synced).
- Use endpoints like:
- Why: Provides access to balances, transactions, and historical data for financial tracking.
- Details:
- Data syncs daily, with manual sync available as needed.
Example:
- Connected Account 1 → Account Data 1 (e.g., balances, transactions, history from Coinbase).
- Connected Account 2 → Account Data 2 (e.g., balances, transactions, history from Bitcoin wallet).
- Connected Account 3 → Account Data 3 (e.g., balances, transactions, history from Ethereum via MetaMask).
Step 5: Utilize Data
- What: Integrate the retrieved data into your application.
- How: Use the data for features like financial tracking, reporting, or analysis.
- Why: Enhances your app with real-time financial insights.
Example:
- User 1 uses Account Data 1 to monitor their Coinbase exchange balance.
Why loginName Matters
The loginName is a unique identifier you assign to each user when creating them in Vezgo. It ensures accurate mapping between your app and Vezgo.
Key Reasons
- Uniqueness:
- Prevents data overlap (e.g., two users with the same
loginNamewould share data).
- Prevents data overlap (e.g., two users with the same
- Linking:
- Maps your app’s user IDs to Vezgo (e.g., User ID
12345→loginName: user_12345).
- Maps your app’s user IDs to Vezgo (e.g., User ID
- Data Access:
- Ensures API requests fetch the correct user’s data.
- Security:
- Bearer tokens are tied to
loginName, restricting access to authorized users.
- Bearer tokens are tied to
- Scalability:
- Supports growth without confusion as more users are added.
How It Works
- Creation:
- Send
POST /auth/tokenwith your API key andloginName→ Get a bearer token.
- Send
- Consistency:
- Use the same
loginNamefor all requests for that user to avoid creating duplicate profiles.
- Use the same
- Retrieval:
- Use the bearer token to access that user’s data via
GET /accounts/{account_id}.
- Use the bearer token to access that user’s data via
Best Practices
- Make it unique: Ensure no duplicates within your app.
- Keep it consistent: Use the same
loginNamefor all calls per user. - Keep it simple: Align with your app’s user IDs (e.g.,
user_12345). - Avoid sensitive info: Don’t use personal data like emails unless necessary.
Summary
- Get API Keys: Create API keys via the Vezgo Portal for your environments.
- Create Users: Use
POST /auth/tokenwith a uniqueloginNameto generate bearer tokens. - Connect Accounts: Link users to crypto, blockchain, CEX, and wallet accounts via the Vezgo Connect Widget.
- Retrieve Data: Use API calls like
GET /accounts/{account_id}to fetch data. - Use Data: Integrate into your app for financial features.
The loginName ensures a secure, seamless connection between your app and Vezgo.
Prompt for Your AI Agent
Quickly integrate Vezgo Connect by copying a ready-made prompt into your AI coding agent. Each prompt contains the full specification — your agent will scaffold the backend and frontend in one go.
Compatible with: Perplexity, Claude Code, ChatGPT, Cursor, Windsurf, GitHub Copilot, Gemini Code Assist, or any AI coding assistant.
There are two ways to integrate Vezgo Connect into your application. Choose the one that fits your architecture:
- Drop-in Connect Widget Flow — Best for web apps. Opens the Vezgo Connect widget inside your page using the JS SDK. The user connects their account without leaving your app, and you receive the result via JS callbacks.
- Connect URL / Redirect Flow — Best for mobile apps, desktop apps, or when you need full control over the UX. Loads the Vezgo Connect URL (with the user token) and redirects back to your app with the result.
Quick Start: Drop-in Connect Widget Flow
Do not paste your Client Secret into the prompt. The prompts below reference it via an environment variable (VEZGO_CLIENT_SECRET). Store the secret in your .env file or secrets manager — your AI agent will read it from there when generating backend code.
Copy the prompt below, replace the [REPLACE: ...] placeholders, and paste it into your AI coding agent:
# Task
Integrate Vezgo Connect into my app using the **Drop-in Connect Widget flow** (iframe-based). Build all files needed to:
1. Authenticate with the Vezgo API from my backend
2. Open the Vezgo Connect Widget in the frontend
3. Handle the connection result and fetch account data
Read the specification below carefully before writing any code. Do not skip any step.
---
# My app context
- **Stack**: [REPLACE: e.g. "Next.js frontend + Express backend" or "React SPA + Django API"]
- **Vezgo Client ID**: [REPLACE: your Client ID]
- **Vezgo Client Secret**: stored in environment variable `VEZGO_CLIENT_SECRET` (do NOT paste the actual secret here)
- **How I identify logged-in users**: [REPLACE: e.g. "JWT with userId in payload" or "session.user.id"]
---
# Specification
## Step 1 — Backend auth endpoint
Create a `POST /vezgo/auth` endpoint on my backend server.
This endpoint is called by the Vezgo JS SDK from the browser. It must:
1. Identify the currently logged-in user (using my app's own auth — see "My app context" above).
2. Request a Vezgo user token by calling:
```
POST https://api.vezgo.com/v1/auth/token
Headers:
loginName: <my_user_identifier>
Content-Type: application/json
Body:
{ "clientId": "<CLIENT_ID>", "secret": process.env.VEZGO_CLIENT_SECRET }
```
3. Return the response as-is: `{ "token": "..." }`
**Security rule**: The Client Secret must only exist on the backend. Never send it to the browser.
**Note on `loginName`**: Use a stable, unique identifier from your system (internal user ID, UUID, etc.). Avoid PII like emails or usernames. The same `loginName` must be used across all calls for the same user — it is what links your user to their Vezgo accounts.
## Step 2 — Frontend: install SDK and open the Connect Widget
Install the SDK:
```bash
npm install vezgo-sdk-js
```
Or load via CDN:
```html
<script src="https://unpkg.com/vezgo-sdk-js/dist/vezgo.umd.js"></script>
```
Then initialize and wire up a "Connect Account" button:
```js
import Vezgo from 'vezgo-sdk-js';
// Initialize — the SDK will call /vezgo/auth automatically to get a token
const vezgo = Vezgo.init({
clientId: '<CLIENT_ID>',
authEndpoint: '/vezgo/auth',
});
const user = vezgo.login();
// Open the Connect Widget when the user clicks "Connect Account"
function openConnect() {
user.connect()
.onConnection(async (accountId) => {
// SUCCESS — the user connected an account.
// `accountId` is the Vezgo account ID. Send it to your backend and store it.
await fetch('/vezgo/accounts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ accountId }),
});
})
.onError((error) => {
// FAILURE — the user closed the widget, or there was a connection error.
// `error` has: error_type (number), message (string), account (string|undefined)
})
.onEvent((name, data) => {
// OPTIONAL — lifecycle events during the process:
// APP_LOADED, SCREEN_LOADED, PROVIDER_SELECTED, SUBMIT_CREDENTIALS,
// SUBMIT_ANSWER, ERROR, CLOSE, FORCE_CLOSE, etc.
});
}
```
## Step 3 — Backend: fetch account data
After the frontend sends the `accountId` to the backend, fetch the full account object:
```
GET https://api.vezgo.com/v1/accounts/<accountId>
Headers:
Authorization: Bearer <user_token>
```
Store the `accountId` in your database. Vezgo automatically syncs account data daily — you can retrieve updated data at any time with the same GET request, or trigger a manual sync with `POST /accounts/<accountId>/sync`. Step 5 covers how to know when a sync has finished.
## Step 4 — Reconnect (implement later, but wire the handler now)
When an account's credentials expire or are revoked, the account moves to a `disconnected`/`error` state and stops syncing. To let the user reconnect it without creating a new account:
```js
user.reconnect(accountId)
.onConnection((accountId) => { /* updated existing account */ })
.onError((error) => { /* handle */ });
```
## Step 5 — Webhooks: know when data is ready, and when an account breaks
Vezgo syncs every connected account once a day, and on demand when you call `POST /accounts/<accountId>/sync`. Syncs run in the background — the first starts right after the user connects, and a long transaction history can take a while. Instead of polling, create a webhook endpoint and let Vezgo notify you.
Create a `POST /vezgo/webhooks` endpoint on my backend. Vezgo sends one of two JSON bodies:
```
{ "hook": "SyncAccount", "user": { "id": "...", "loginName": "..." }, "account": { "id": "...", "provider": "..." } }
{ "hook": "SyncError", "user": { "id": "...", "loginName": "..." }, "account": { "id": "...", "provider": "..." }, "error": "<ErrorName>" }
```
- `SyncAccount` — the account synced successfully. Re-fetch the account (Step 3) and pull its new transactions.
- `SyncError` — the account failed to sync. Branch on `error`:
- `LoginFailedError`, `SecurityQuestionError` — the user must reconnect, and the account will not sync again until they do. Mark it as needing attention and show a "Reconnect" action that runs the reconnect flow from Step 4.
- `TemporaryFailureError` — Vezgo retries automatically. Log it; no action needed.
- `UnhandledConnectorError`, `UnknownConnectorError` — log it with the account id. If it keeps happening, contact Vezgo support with that id.
Handler rules:
1. **Acknowledge immediately.** Return `200` right away and do the work asynchronously (a queue or background job). A non-2xx response, or no response within a few seconds, counts as a failed delivery and Vezgo retries it — so the same event can arrive more than once. Make the handler idempotent.
2. **Treat the webhook as a signal, not as data.** It carries only ids. Always read current state from the Vezgo API, with a fresh user token requested using `loginName: user.loginName`.
3. **Map the user back.** `user.loginName` is the identifier my backend sends when requesting tokens — use it to find the user in my database.
4. **Verify the sender.** Check that the `X-Client-Id` request header equals my Client ID. If my infrastructure filters inbound traffic, allowlist Vezgo's static source IPs, listed at https://vezgo.com/docs/webhooks. Vezgo does not sign webhook payloads — do not implement HMAC signature verification.
5. **Don't rely on webhooks alone.** An endpoint that stays down long enough can miss events. As a safety net, re-read the status of each stored account on a schedule (e.g. daily).
The endpoint must be served over HTTPS at a public URL. Register it in the Vezgo Client Portal under API Keys → Webhook URL.
## Step 6 — Map the data into your app
After accounts connect, fetch each account and map its data into your app's domain model — providers, accounts, transactions, positions/holdings, and securities/assets. Follow the step-by-step Data mapping guide, which lists the exact Vezgo fields and recommended target columns for each area:
https://vezgo.com/docs/get-started#data-mapping
For the exact fields, transaction types and quirks of a provider you integrate, also fetch its per-connector reference (available for the major chains and exchanges): `https://vezgo.com/status/<provider>-api/` (replace `<provider>` with `account.provider.name`, e.g. `ethereum`, `coinbase`, `binance`).
---
# Constraints
- Token lifetime: 10 minutes by default — always request a fresh token right before opening the widget.
- For testing, use the **Vezgo Demo** provider: username `vezgo`, password `vezgo` for a successful connection. See the [Vezgo Demo institution](/connecting-a-user#the-vezgo-demo-institution) docs for other test scenarios (2FA, errors, MFA).
- Vezgo also supports `multi_wallet` mode (connect multiple wallets/networks in one flow) and `sync_nfts` — see "Connect URL parameters" in the Connecting a User docs.
- Webhooks require a premium Vezgo plan. Without one, skip Step 5 and re-read each stored account's `status` on a schedule instead.
# References (fetch these docs for the latest API details)
- JS SDK source & examples: https://github.com/wealthica/vezgo-sdk-js
- Connect a user (full reference): https://vezgo.com/docs/connecting-a-user
- Authentication: https://vezgo.com/docs/authentication
- Webhooks: https://vezgo.com/docs/webhooks
- API reference: https://vezgo.com/docs/api
Quick Start: Connect URL / Redirect Flow
Copy the prompt below, replace the [REPLACE: ...] placeholders, and paste it into your AI coding agent:
# Task
Integrate Vezgo Connect into my app using the **Connect URL / Redirect flow**. Build all files needed to:
1. Generate a Vezgo user token on the backend
2. Send the user to the Vezgo Connect URL
3. Handle the redirect callback with the connection result
Read the specification below carefully before writing any code. Do not skip any step.
---
# My app context
- **Stack**: [REPLACE: e.g. "Express.js backend serving a React SPA" or "Rails API + React Native mobile"]
- **Vezgo Client ID**: [REPLACE: your Client ID]
- **Vezgo Client Secret**: stored in environment variable `VEZGO_CLIENT_SECRET` (do NOT paste the actual secret here)
- **Redirect URI**: [REPLACE: e.g. "https://myapp.com/vezgo/callback" — must be registered with Vezgo]
- **How I identify logged-in users**: [REPLACE: e.g. "JWT with userId in payload" or "session.user.id"]
- **Platform**: [REPLACE: "web", "iOS", "Android", or "desktop"]
---
# Specification
## Step 1 — Backend: generate a Vezgo user token
Create a backend function that generates a Vezgo user token for a given user:
```
POST https://api.vezgo.com/v1/auth/token
Headers:
loginName: <my_user_identifier>
Content-Type: application/json
Body:
{ "clientId": "<CLIENT_ID>", "secret": process.env.VEZGO_CLIENT_SECRET }
Response:
{ "token": "eyJ..." }
```
**Security rule**: The Client Secret must only exist on the backend. Never send it to the browser or mobile app.
## Step 2 — Send the user to Vezgo Connect
The Connect URL must be loaded as a **POST request** with the token in form data. The simplest approach is to render a server-side page with an auto-submitting HTML form:
```html
<form method="POST" action="https://connect.vezgo.com/connect?client_id=<CLIENT_ID>&redirect_uri=<REDIRECT_URI>&state=<STATE>">
<input type="hidden" name="token" value="<USER_TOKEN>" />
<noscript><button type="submit">Continue to Vezgo</button></noscript>
</form>
<script>document.forms[0].submit();</script>
```
Create a backend endpoint (e.g. `GET /vezgo/connect`) that:
1. Gets a fresh Vezgo user token (Step 1)
2. Encodes any app state into the `state` parameter (base64) so you can restore context after redirect
3. Renders the auto-submitting form above
### Connect URL parameters
| Parameter | Required | Description |
|-----------|----------|-------------|
| `client_id` | Yes | Your Vezgo Client ID |
| `redirect_uri` | Recommended | Where Vezgo redirects after completion. Must be pre-registered. |
| `state` | Recommended | Opaque string (e.g. base64) to restore your app state after redirect |
| `lang` | No | UI language: `en`, `es`, `fr`, `it` |
| `provider_categories` | No | Limit to categories: `exchanges`, `blockchains`, `wallets` |
| `providers` | No | Comma-separated allow-list of providers, e.g. `binance,coinbase,ethereum` |
| `theme` | No | `light` (default) or `dark` |
| `providers_per_line` | No | Providers per line on the select screen: `1` or `2` (default `2`) |
| `sync_nfts` | No | Show "Sync NFTs" checkbox (boolean, default `false`) |
| `multi_wallet` | No | Allow connecting multiple wallets/networks in one flow (boolean, default `false`) |
| `hide_wallet_connect_wallets` | No | Hide WalletConnect wallets from the list (boolean, default `false`) |
### URL variants for other flows
New connection (skip provider selection by appending the provider name):
`https://connect.vezgo.com/connect/<PROVIDER_NAME>?client_id=...&redirect_uri=...`
Reconnect (update credentials for an existing account):
`https://connect.vezgo.com/reconnect/<ACCOUNT_ID>?client_id=...&redirect_uri=...`
## Step 3 — Handle the redirect callback
Create a route at your redirect URI (e.g. `GET /vezgo/callback`). Vezgo redirects the user back with query parameters:
**On success:**
```
<REDIRECT_URI>?account=<ACCOUNT_ID>&code=<ACCOUNT_ID>&state=<STATE>
```
**On failure:**
```
<REDIRECT_URI>?error_type=<CODE>&message=<MSG>&state=<STATE>&force=<true|undefined>&account=<ID>
```
Your callback handler must:
1. Check if `error_type` is present — if so, show an error to the user using `message`. If `force=true`, the user explicitly closed the widget.
2. If `account` is present, store the account ID in your database linked to the user.
3. Decode and restore app state from the `state` parameter.
4. Fetch the full account data from the Vezgo API:
```
GET https://api.vezgo.com/v1/accounts/<accountId>
Headers:
Authorization: Bearer <user_token>
```
### Callback query parameters reference
| Parameter | Present on | Description |
|-----------|-----------|-------------|
| `account` | Both (on failure: only if created before the error) | The Vezgo account ID |
| `code` | Success | Same as `account` (for OAuth compliance). In `multi_wallet` mode, an object with connection details. |
| `error_type` | Failure | Numeric error code |
| `message` | Failure | Human-readable error message |
| `force` | Failure | `true` if the user explicitly closed the widget |
| `state` | Both | Your app state passed in Step 2 |
## Step 4 — Webhooks: know when data is ready, and when an account breaks
Vezgo syncs every connected account once a day, and on demand when you call `POST /accounts/<accountId>/sync`. Syncs run in the background — the first starts right after the user connects, and a long transaction history can take a while. Instead of polling, create a webhook endpoint and let Vezgo notify you.
Create a `POST /vezgo/webhooks` endpoint on my backend. Vezgo sends one of two JSON bodies:
```
{ "hook": "SyncAccount", "user": { "id": "...", "loginName": "..." }, "account": { "id": "...", "provider": "..." } }
{ "hook": "SyncError", "user": { "id": "...", "loginName": "..." }, "account": { "id": "...", "provider": "..." }, "error": "<ErrorName>" }
```
- `SyncAccount` — the account synced successfully. Re-fetch the account as in Step 3 and pull its new transactions.
- `SyncError` — the account failed to sync. Branch on `error`:
- `LoginFailedError`, `SecurityQuestionError` — the user must reconnect, and the account will not sync again until they do. Mark it as needing attention and show a "Reconnect" action that sends the user to the reconnect URL (`https://connect.vezgo.com/reconnect/<ACCOUNT_ID>?client_id=...&redirect_uri=...`, see "URL variants" in Step 2).
- `TemporaryFailureError` — Vezgo retries automatically. Log it; no action needed.
- `UnhandledConnectorError`, `UnknownConnectorError` — log it with the account id. If it keeps happening, contact Vezgo support with that id.
Handler rules:
1. **Acknowledge immediately.** Return `200` right away and do the work asynchronously (a queue or background job). A non-2xx response, or no response within a few seconds, counts as a failed delivery and Vezgo retries it — so the same event can arrive more than once. Make the handler idempotent.
2. **Treat the webhook as a signal, not as data.** It carries only ids. Always read current state from the Vezgo API, with a fresh user token requested using `loginName: user.loginName`.
3. **Map the user back.** `user.loginName` is the identifier my backend sends when requesting tokens — use it to find the user in my database.
4. **Verify the sender.** Check that the `X-Client-Id` request header equals my Client ID. If my infrastructure filters inbound traffic, allowlist Vezgo's static source IPs, listed at https://vezgo.com/docs/webhooks. Vezgo does not sign webhook payloads — do not implement HMAC signature verification.
5. **Don't rely on webhooks alone.** An endpoint that stays down long enough can miss events. As a safety net, re-read the status of each stored account on a schedule (e.g. daily).
The endpoint must be served over HTTPS at a public URL. Register it in the Vezgo Client Portal under API Keys → Webhook URL.
## Step 5 — Map the data into your app
After accounts connect, fetch each account and map its data into your app's domain model — providers, accounts, transactions, positions/holdings, and securities/assets. Follow the step-by-step Data mapping guide, which lists the exact Vezgo fields and recommended target columns for each area:
https://vezgo.com/docs/get-started#data-mapping
For the exact fields, transaction types and quirks of a provider you integrate, also fetch its per-connector reference (available for the major chains and exchanges): `https://vezgo.com/status/<provider>-api/` (replace `<provider>` with `account.provider.name`, e.g. `ethereum`, `coinbase`, `binance`).
---
# Constraints
- Token lifetime: 10 minutes by default — always generate a fresh token right before redirecting.
- The redirect URI **must be pre-registered** with Vezgo. Contact Vezgo to register URIs for dev, staging, and production, or manage them in the Vezgo Portal.
- Vezgo syncs account data daily automatically. You can also trigger a manual sync via `POST /accounts/:id/sync`. The `SyncAccount` webhook (Step 4) tells you when either finishes.
- Webhooks require a premium Vezgo plan. Without one, skip Step 4 and re-read each stored account's `status` on a schedule instead.
- For testing, use the **Vezgo Demo** provider: username `vezgo`, password `vezgo` for a successful connection. See the [Vezgo Demo institution](/connecting-a-user#the-vezgo-demo-institution) docs for other test scenarios.
- For mobile apps, register a custom scheme redirect URI (e.g. `com.myapp://vezgo/callback`) and handle it in your deep link handler. See the [React Native](/sdk/react-native), [iOS](/sdk/ios) and [Android](/sdk/android) guides.
# References (fetch these docs for the latest API details)
- Connect a user (full reference): https://vezgo.com/docs/connecting-a-user
- URLs: https://vezgo.com/docs/get-started#urls
- Webhooks: https://vezgo.com/docs/webhooks
- API reference: https://vezgo.com/docs/api
- Mobile apps: https://vezgo.com/docs/sdk/react-native, https://vezgo.com/docs/sdk/ios, https://vezgo.com/docs/sdk/android
Data mapping
The prompts above generate the connection flow. Once an account is connected, you still need to map the Vezgo data into your app's domain model — accounts, wallets, transactions, positions/holdings, and securities/assets. This is the step-by-step guide both prompts link to; it applies equally to the Drop-in Widget and Connect URL / Redirect flows.
This guide is the cross-connector model — the fields and rules that apply to every provider. For the exact fields, transaction types, asset_type values and provider-specific quirks of a given connector, read its per-connector data-mapping reference on the provider's status page.
URL pattern: https://vezgo.com/status/<provider>-api/ — where <provider> is account.provider.name (e.g. ethereum → vezgo.com/status/ethereum-api). Detailed per-connector references are available for:
- EVM chains — Ethereum, Arbitrum, Avalanche, Base, BSC, Cronos, Optimism, Polygon
- Bitcoin-family (UTXO) — Bitcoin, Bitcoin Cash, Dash, Dogecoin, Litecoin
- Other chains — Solana, XRP, Hedera, Algorand, Stellar, Hyperliquid, Sui, TRON, Toncoin
- Exchanges — Coinbase, Kraken, Uphold, Binance, Binance US, Gemini, Bybit, OKX, Crypto.com, Bitstamp, MEXC, KuCoin, Coinbase Prime
Integrating a provider that isn't listed above? Browse all supported integrations at vezgo.com/status — every provider has a status page (/status/<provider>-api/), and detailed per-connector data-mapping references are added there over time.
Global rules (apply to every area below):
- Every
amountandfiat_valueis a string — parse with a decimal/bignumber type, never a float. - Every timestamp (
created_at,updated_at,initiated_at,confirmed_at, …) is milliseconds since epoch. - Account-level
fiat_tickeris alwaysUSD(per-balancefiat_tickermay differ — store it per balance). - Confirm exact field names against the object reference before writing your schema.
1. Providers
The provider is the institution holding an account. Read the catalog from GET /providers (no user token needed); read the per-account provider from account.provider.
- Capture provider identity:
account.provider.name(stable id, e.g.bitcoin,ethereum,coinbase,binance) andaccount.provider.display_name(for UI). - Classify it:
provider.is_blockchain === true→ blockchain / self-custody wallet; otherwise → exchange. (The widget also groups providers via theprovider_categoriesoption:blockchains,wallets,exchanges.) - Read
provider.auth_type(wallet= address/WalletConnect,oauth= e.g. Coinbase,token= API key + secret,password= username + password) — this determines how reconnection works.
Blockchains (e.g. Ethereum, Bitcoin, Solana)
- Each connection exposes one or more
account.wallets[]. Persistwallet.id(stable, e.g.bitcoin:cash:usd) andwallet.address(on-chain address or xpub) as the account identity. - For EVM / multi-chain providers, read the chain id from
provider.misc.chain_id(chainlist.org id) and the list inprovider.supported_networks. Store chain id next to the address so the same address on different chains stays distinct. - Key wallets on
wallet.id, not onwallet.addressalone — withmulti_walletenabled, one connection can hold the same address across multiple networks.
Suggested host fields: provider_name ← provider.name, is_blockchain ← provider.is_blockchain, chain_id ← provider.misc.chain_id, address ← wallet.address.
Exchanges (e.g. Coinbase, Binance)
- Provider identity is the same
provider.name/display_name. - Exchange sub-accounts (spot / margin / futures) surface as separate
account.wallets[]entries. Persist eachwallet.idandwallet.name;wallet.addressis usuallynullfor exchanges — key onwallet.id. - Exchange wallets have no on-chain address — do not require an
addressfor them.
Reconnection without duplicates (both types)
A reconnect (user.reconnect(accountId) or connect.vezgo.com/reconnect/<ACCOUNT_ID>) refreshes credentials on the same account.id. Match on account.id and update in place — never insert a new row on reconnect.
2. Accounts
- Read from
GET /accounts(list) orGET /accounts/:id(one). SDK:user.accounts.getList()/user.accounts.getOne(id). - Persist at minimum:
id(Vezgo account id — your stable key),provider.name,provider.is_blockchain,fiat_value(string, USD),fiat_ticker,status,created_at,updated_at. - Deduplicate on
account.idonly — never on the user-visible name (names are not unique and can change). Put a unique constraint on the stored Vezgoaccount.id. - Relate each account to its owner via the stable
loginNameyou used to create the Vezgo user: storeyour_user_id↔account.id. The sameloginNamealways returns the same set of accounts. - Track
status(ok/error/syncing/retry). Onerror, readaccount.error.name(e.g.LoginFailedError,SecurityQuestionError) and surface a "Reconnect" action.
3. Transactions
- Endpoint:
GET /accounts/:id/transactions. SDK:user.transactions.getList({ accountId, from, last, limit }). - Sync strategy — paginate with
lastfor incremental sync:- First import: pass an old
from(e.g.2009-01-01) and page withlimit(10–1000) +last(the previous page's lastid) until a page comes back empty. Keep the samefromacross all pages. - Incremental sync: store the newest imported
transaction.id; next run, pass it as?last=to fetch only newer transactions. Start each run from theSyncAccountwebhook rather than a timer.
- First import: pass an old
- Persist per transaction:
id,account,wallet,transaction_type,transaction_subtype,transaction_hash,initiated_at,confirmed_at,fiat_calculated_at,misc.origin_id. Each transaction has 1+parts[]and 0+fees[]. (statusis currently alwaysnull— do not rely on it.) - Store each
partin a child table keyed by transactionid:direction(sent/received),ticker,ticker_address,amount(string),fiat_value(string),fiat_ticker,from_address,to_address. Storefees[]:ticker,amount,fiat_value. - Type mapping — translate Vezgo
transaction_typeinto your taxonomy:
Vezgo transaction_type | Direction(s) | Suggested host type |
|---|---|---|
deposit | received | deposit / transfer-in |
withdrawal | sent | withdrawal / transfer-out |
trade | sent + received | trade (buy/sell — see transaction_subtype) |
staking | sent + received | stake |
unstaking | sent + received | unstake |
reward | received | reward / income |
bonus | received | bonus / airdrop |
other | varies | other (inspect transaction_subtype) |
For finer detail (buy vs sell, fiat vs crypto, interest, airdrop…) read transaction_subtype (e.g. buy, sell, staking_reward, distribution). Full list: Connecting a user.
- Internal transfers (avoid double-counting): a
withdrawalfrom one tracked account and adepositinto another tracked account are the same movement. Detect by matchingtransaction_hash(ormisc.origin_id) plus thefrom_address/to_addresspair across your accounts, and collapse them into one internal-transfer record. On EVM,misc.internal === true/misc.category === 'internal'also flag internal movements. - Idempotency / natural key: use Vezgo
transaction.idas the primary key (stable across re-imports);misc.origin_id(provider's original id or blockchain hash) is the secondary cross-reference. Note: Vezgo reconciles a buy + sell (+ fee) into a singletradetransaction, so do not assume a 1:1 mapping to raw exchange rows.
4. Positions / Holdings
- Positions are not a separate endpoint — they are the
account.balances[]array on the account object (GET /accounts/:id). Refresh by re-reading the account after a sync (POST /accounts/:id/sync, or the daily auto-sync). - Persist per balance:
account,wallet,ticker,ticker_address,amount(string quantity),decimals,fiat_value(string),fiat_ticker, andupdated_at(use as youras_of). - Snapshot, not computed: balances are the provider's authoritative current holdings — replace the position set for each
(account, wallet)on every sync. Use transactions (area 3) only for movement/cost-basis, not to recompute current quantity. - Cost basis: Vezgo does not return a cost-basis field. If you need it, compute it from the transaction
parts[].fiat_valueatinitiated_at. - NFTs vs fungible tokens: with
sync_nftsenabled, NFTs appear as balances withasset_type === 'nft'andmisc.metadata(contract_address,token_id,contract_type,media_url). Fungible tokens haveasset_typeofnull/staked/cash. Route NFTs to a separate table keyed bycontract_address+token_id.
5. Securities / Assets
- There is no securities endpoint — Vezgo identifies each asset inline on balances and transaction parts. Build your instrument table from the assets you encounter.
- Identifiers Vezgo provides:
ticker— normalized symbol (e.g.BTC,ETH).provider_ticker— the provider's own symbol (may differ;.stakedsuffix for staked assets).ticker_address— token contract address (ERC-20 / SPL / …);nullfor native coins.- chain context — from the account's
provider.misc.chain_id/provider.supported_networks. decimals,name, andlogo(https://data.wealthica.com/api/securities/CRYPTO:<TICKER>/logo— theCRYPTO:<TICKER>string is Vezgo's security key).- NFTs:
misc.metadata.contract_address+token_id+contract_type. - Note: Vezgo does not return a CoinGecko/CMC id. If your instrument table keys on one, resolve it yourself from
ticker+ticker_address+ chain.
- Dedupe key:
- Native coins → key on
ticker. - Tokens → key on
ticker_address+ chain id (nottickeralone — the same ticker exists on multiple chains, e.g. USDC on Ethereum vs Polygon). - NFTs → key on
contract_address+token_id.
- Native coins → key on
- Collisions: when two assets share a
tickerbut differ inticker_address/chain, store them as distinct instruments (use the address + chain key). - Unknown / unsupported assets (fall-through): if an asset is not in your instrument table, auto-create a minimal record from (
ticker,ticker_address, chain,decimals,name) instead of dropping the balance/transaction; flag it for later enrichment. - Price & currency: each balance/part already carries
fiat_value+fiat_ticker(account-level fiat isUSD). Use these directly; for another display currency, convert from USD with your own FX source.