Integration Flow

Overview

This page walks through a complete sample integration flow from user creation through bank linking and aggregation jobs (balance checks, identity, transaction history)

Create a user and link a bank

Start an aggregation job

Sample Full Integration Flow

#ActionCallerKey InputKey Output
1Get Aeropay auth tokenBackendapi_key, api_secret, merchantId, scope: "merchant"Aeropay token (for /user only)
2Create AeroPass userBackendfirstName, lastName, phone, email + Aeropay tokenaeroPassUserUuid (persist in DB)
3Get Aerosync widget tokenBackendapiKey, apiSecret, aeroPassUserUuidWidget JWT token
4Pass token to frontendBackend → FrontendWidget JWT tokenToken available client-side
5Launch Aerosync widgetFrontendtoken, aeroPassUserUuid, configurationId, environmentWidget rendered in iframe
6User authenticates with bankEnd UserBank credentials (handled entirely by widget)Bank linked
7Capture onSuccess eventFrontendWidget postMessage eventconnectionId (persist in DB)
8Retrieve account detailsBackendconnectionId + Aerosync API tokenAccount/routing number
9Create aggregation jobBackendconnectionId + Aerosync API tokenjobId, status: Initiated
10Poll job statusBackendconnectionId, jobIdjobStatusCOMPLETED or MFA_REQUIRED
11Handle MFA if triggeredFrontendjobId, connectionId, handleMFA: true + new widget tokenUser completes MFA in widget
12Retrieve dataBackendconnectionId + Aerosync API tokenBalance / Transactions / Identity

Step 1–2: AeroPass User Creation

User creation is a server-side operation. Never expose your API credentials to the client. The aeroPassUserUuid returned must be persisted in your database — it is the permanent identifier for that user across all future widget launches.

Step 1: Get Aeropay Token (for /user only)

POST https://api.sandbox-pay.aero.inc/v2/token
{
  "scope": "merchant",
  "api_key": "<your_api_key>",
  "api_secret": "<your_api_secret>",
  "id": "<merchantId>"
}

Response:

{
  "TTL": 1800,
  "token": "<aeropay_token>"
}

Step 2: Create User

POST https://api.sandbox-pay.aero.inc/v2/user
Authorization: Bearer <aeropay_token>
{
  "firstName": "Jane",
  "lastName": "Doe",
  "phoneNumber": "+11234567890",
  "emailAddress": "[email protected]"
}

Response:

{
  "success": true,
  "user": {
    "aeroPassUserUuid": "0f2542a4-8e60-4a72-b3a1-064f2d6943e8",
    "userId": "1102575",
    "firstName": "Jane",
    "lastName": "Doe"
  }
}
⚠️

Warning: Store aeroPassUserUuid immediately on receipt. If the user already exists in your system, retrieve their stored UUID rather than creating a duplicate.


Step 3–4: Widget Token Generation

Widget tokens are short-lived (30 minutes) and tied to a specific user. Generate a fresh token on every widget launch — do not cache and reuse across sessions.

POST https://api.sandbox.aerosync.com/v2/token_widget
{
  "apiKey": "<aerosync_api_key>",
  "apiSecret": "<aerosync_api_secret>",
  "aeroPassUserUuid": "<uuid>"
}

Response:

{
  "status": "success",
  "statusCode": 200,
  "token": "<widget_jwt>",
  "userId": "<internal_id>"
}

Step 5–7: Widget Launch and Bank Linking

For this example, we will show the web SDK. Aerosync does support a breadth of SDKs depending on your tech stack. Those can be found here.

Install the NPM package:

npm i aerosync-web-sdk

Add the mount point to your HTML:

<button id="openBank" @click="openAerosyncWidget()">Connect Bank</button>
<div id="widget"></div>

Initialize and launch the widget. The onSuccess callback is your most important handler — it delivers the connectionId that gates all data retrieval.

import { openWidget } from 'aerosync-web-sdk';

const widgetRef = openWidget({
  id: 'widget',                         // must match your div id
  iframeTitle: 'Connect Bank',
  environment: 'sandbox',               // 'sandbox' | 'production'
  token: '<widget_jwt>',                // from Step 3
  aeroPassUserUuid: '<uuid>',           // from Step 2
  configurationId: '<configId>',        // optional, for custom branding
  style: {
    width: '375px',
    height: '688px',
    bgColor: '#000000',
    opacity: 0.7
  },
  handleMFA: false,                     // set true when re-launching for MFA
  onSuccess: (event) => {
    const { connectionId } = event;     // CRITICAL: save this to your DB
    console.log('Bank linked:', connectionId);
  },
  onError: (event) => { console.error(event); },
  onClose: () => { /* handle user exit */ },
  onEvent: (event, type) => { /* optional page-level analytics */ }
});

widgetRef.launch();
📘

Note: For mobile apps, set widgetLaunchType: "systemBrowser" and provide a deeplink URL so OAuth bank flows can redirect back to your app after external authentication.


Step 8: Retrieve Account Details

Generate an Aerosync API token. Please note this is separate from the widget token:

POST https://api.sandbox.aerosync.com/v2/token
Content-Type: application/json

{
  "apiKey": "<aerosync_api_key>",
  "apiSecret": "<aerosync_api_secret>"
}

Then retrieve account details:

GET https://api.sandbox.aerosync.com/v2/accounts/{connectionId}
Authorization: Bearer <aerosync_api_token>

Key response fields:

FieldDescription
accountNumberFull account number (may be tokenized)
routingNumber9-digit ABA routing number
accountNumberType"Tokenized" or "Non-tokenized"
connectionStatus"CONNECTED" or "failure"
connectionExpiryISO 8601 timestamp — monitor for reconnection flows
canFetchBalanceBoolean — check before triggering balance jobs

Steps 9–12: Aggregation Jobs

All three job types follow the same four-step pattern: (1) Create job → (2) Poll status → (3) Handle MFA if triggered → (4) Retrieve data. The only differences are endpoint paths and response schemas.

Job TypeCreate JobRetrieve DataNotes
Balance RefreshPOST /v2/accounts/{connectionId}/refreshGET /v2/accounts/{connectionId}/balancecanFetchBalance must be true. Not available for manually linked users.
Transaction HistoryPOST /v2/accounts/{connectionId}/transactionsGET /v2/accounts/{connectionId}/transactionsReturns recent transactions with date, amount, description. Not available for manually linked users.
Account IdentityPOST /v2/accounts/{connectionId}/identityGET /v2/accounts/{connectionId}/identityReturns account holder name, address, email. Only one job can run at a time — AC-112 if conflict.

Polling Pattern & Webhook

After creating a job, you can either poll the status endpoint until a terminal status is returned or listen to the job.<job_status> webhook.

GET https://api.sandbox.aerosync.com/v2/accounts/{connectionId}/job/{jobId}
Authorization: Bearer <aerosync_api_token>
{
  "status": "success",
  "statusCode": 200,
  "jobStatus": "COMPLETED",
  "mfaTriggered": false,
  "jobId": "REF-c869a583d3274a6aa2cbc45ef106f805",
  "connectionId": "68c13bc0855a4f85853f6aafaa43e461"
}
Job StatusmfaTriggeredMeaningAction
InitiatedfalseJob queued, not yet runningContinue polling
IN_PROGRESSfalseActively fetching from FIContinue polling
COMPLETEDfalseData readyCall data retrieval endpoint
MFA_REQUIREDtrueFI issued MFA challengeRe-launch widget with handleMFA: true
FAILEDfalseFI or connection errorCheck AC error code. Retry once. If persistent, surface reconnect widget.

Recommended polling interval: every 2–3 seconds. Set a maximum of 30 attempts, then surface an error to the user.

MFA Handling — Re-launching the Widget

When mfaTriggered: true is detected, generate a fresh widget token and re-launch the widget in MFA mode:

const widgetRef = openWidget({
  token: '<new_widget_jwt>',            // fresh token required
  aeroPassUserUuid: '<uuid>',
  handleMFA: true,                      // required
  jobId: '<jobId>',                     // required when handleMFA is true
  connectionId: '<connectionId>',       // required when handleMFA is true
  environment: 'sandbox',
  id: 'widget',
  iframeTitle: 'Verify Identity',
  onSuccess: (event) => { /* resume polling */ },
  onError: (event) => { console.error(event); },
  onClose: () => { /* handle close */ }
});

widgetRef.launch();

Tip: After the user completes MFA in the widget, resume polling the job status endpoint. The job should advance to COMPLETED within a few seconds.