@afinx/bridge-sdk
v0.2.1
Published
JavaScript SDK for integrating Afinx Bridge — connect bank accounts and mobile money across Africa.
Downloads
66
Maintainers
Readme
@afinx/bridge-sdk
JavaScript SDK for integrating the Afinx Bridge widget — let your users securely connect their bank accounts and mobile money wallets across Africa.
npm install @afinx/bridge-sdkProduction (recommended): server-minted link token
Keep your secret key on your server. Mint a one-shot link token there, hand it to the browser, and pass it to the SDK — the key never ships in client code.
// 1. Your backend mints a link token with your secret key.
// POST https://api.afinx.co/bridge/link-token
// Authorization: Bearer afinx_test_sk_... → { linkToken: "link_..." }
// 2. Your frontend opens the widget with that token (no key in the browser):
import { createBridge } from '@afinx/bridge-sdk';
const bridge = createBridge(); // no apiKey needed when you pass a linkToken
bridge.open({
linkToken, // from your backend
onSuccess: async (publicToken) => {
// 3. Exchange the public token on YOUR BACKEND (never in the browser).
await fetch('/api/afinx/exchange-token', {
method: 'POST',
body: JSON.stringify({ publicToken }),
});
},
onExit: () => console.log('User closed the widget.'),
onError: (err) => console.error('Bridge error:', err),
});Sandbox quick start
For quick local testing you can let the SDK mint the link token in-browser with a sandbox key. Don't use this with a live key — the key would be exposed in client code.
const bridge = createBridge({ apiKey: 'afinx_test_sk_...', environment: 'sandbox' });
bridge.open({ userId: 'user_123', onSuccess: (publicToken) => {/* … */} });Either way, the widget handles institution selection, credential capture,
USSD/OTP MFA (and inbox/upload for the Statements product), and returns a
single-use publicToken your backend exchanges for an access token.
Configuration
createBridge(config)
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| apiKey | string | no* | — | afinx_test_sk_... / afinx_live_sk_.... *Required only if you DON'T pass a linkToken to open() (i.e. the sandbox in-browser-mint path). Omit it in production. |
| environment | 'sandbox' \| 'production' | no | 'production' | Selects the default apiUrl and bridgeUrl. |
| apiUrl | string | no | env-derived | Override the API host (self-hosted setups, custom subdomains). |
| bridgeUrl | string | no | env-derived | Override the widget host. |
Default URLs by environment
| Environment | apiUrl | bridgeUrl |
|---|---|---|
| sandbox | https://api.sandbox.afinx.co | https://bridge.afinx.co |
| production | https://api.afinx.co | https://bridge.afinx.co |
The widget URL is the same in both environments — the link token (minted by either the sandbox or live API) tells the widget which API to call back to. One widget deployment serves both.
Security: never put a live
apiKeyin client-side code. For browser integrations, mint a one-shot link token on your backend and pass it toopen({ linkToken })instead (see Production). Available since 0.2.0.
bridge.open(options)
| Field | Type | Description |
|---|---|---|
| linkToken | string | A one-shot link token your backend minted (POST /bridge/link-token). Pass this in production — the SDK then skips client-side minting, so apiKey isn't needed in the browser. |
| userId | string | Your stable identifier for the end user. |
| redirectUri | string | Where to send the user after the flow (redirect mode — coming in 0.2). |
| webhookUrl | string | Per-session webhook override. Falls back to your developer-level webhook. |
| metadata | object | Arbitrary JSON attached to the session and returned in webhooks. |
| onSuccess | (publicToken, metadata) => void | Fired with the single-use public token. |
| onExit | (error?) => void | Fired when the user closes the widget. error is set if the close was due to a problem. |
| onError | (error) => void | Fired on terminal failures (network, invalid session, etc.). |
bridge.close() / bridge.isOpen()
Programmatic control of the widget. Useful for cleanup on route change in SPAs.
useEffect(() => () => bridge.close(), []);Token exchange (server-side)
The publicToken returned to onSuccess is single-use and expires in
10 minutes. Exchange it on your backend immediately:
// pages/api/afinx/exchange-token.ts (Next.js example)
const r = await fetch('https://api.afinx.co/bridge/exchange-token', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.AFINX_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ publicToken: req.body.publicToken }),
});
const { accessToken, itemId, accounts } = await r.json();
// Persist accessToken keyed on your user. Use it to call /v1/transactions etc.TypeScript
Full types ship with the package — BridgeConfig, BridgeOpenOptions,
BridgeInstance are exported.
import type { BridgeConfig, BridgeOpenOptions } from '@afinx/bridge-sdk';Bundle size
- Tree-shakeable (
"sideEffects": false). - Zero runtime dependencies.
- ~3 kB gzipped.
Browser support
Modern evergreen browsers (Chrome/Edge/Firefox/Safari, last 2 versions). The
SDK uses window.postMessage, window.open, and fetch — no polyfills
included, no IE11.
Versioning
This package follows Semantic Versioning starting at
0.1.0. While the major version is 0.x, minor releases may include breaking
changes. Pin to a minor in production:
// package.json
"@afinx/bridge-sdk": "~0.2.0" // accepts 0.2.x, not 0.3.0Links
- Docs: https://afinx.co/docs
- Dashboard: https://afinx.co/dashboard
- Changelog: CHANGELOG.md
- Issues: https://github.com/afinx/bridge-sdk/issues
License
MIT — see LICENSE.
