Rangler Connect
Embed read-only account and portfolio linking with Rangler Connect.
Rangler Connect is the hosted Link flow for read-only account and portfolio aggregation. v1 is available to design partners and does not support trade execution, order routing, payments, or money movement.
Security model
Keep rgl_test_ and rgl_live_ API keys on your backend only. Never put them in browser code, mobile apps, or client-side environment variables.
Connect uses short-lived browser tokens:
- your backend creates a
link_tokenwithX-API-Key - your browser opens the hosted modal with that
link_token - the hosted modal returns a one-use
public_token - your backend exchanges the
public_tokenfor the persistentconnection_id
The JavaScript SDK supports fetchLinkToken so your button can open Connect without exposing your API key. fetchLinkToken should call your own backend endpoint, and that endpoint should call Rangler with X-API-Key.
Flow
- Your backend creates a short-lived link token with
X-API-Key. - Your frontend opens the hosted Connect modal with
@rangler/connect-js. - The hosted modal links the customer to an enabled institution and returns a one-use
public_token. - Your backend exchanges the public token for a persistent
connection_id. - Your backend reads accounts, positions, and transactions through the Connect API.
Create a link-token endpoint
Create this endpoint in your own backend. It authenticates your current user, maps that user to a stable client_user_id, and calls Rangler with your server-side API key.
app.post('/api/rangler/link-token', async (req, res) => {
const response = await fetch('https://sandbox-api.rangler.co/v1/connect/link-tokens', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': process.env.RANGLER_API_KEY,
},
body: JSON.stringify({
client_user_id: req.user.id,
client_name: req.user.name,
products: ['accounts', 'positions', 'transactions'],
allowed_origins: ['https://app.example.com'],
}),
});
res.status(response.status).json(await response.json());
});Direct HTTP equivalent:
curl -s https://sandbox-api.rangler.co/v1/connect/link-tokens \
-H "X-API-Key: rgl_test_your_key_here" \
-H "Idempotency-Key: 7b830fc3-848d-4c21-92d6-4f5243c1f45f" \
-H "Content-Type: application/json" \
-d '{
"client_user_id": "customer_123",
"client_name": "Ada Okafor",
"products": ["accounts", "positions", "transactions"],
"allowed_origins": ["https://app.example.com"]
}'The response contains link_token, link_token_id, request_id, and expiration.
Generate a new Idempotency-Key for each logical link-token request. Reuse it only when retrying the same body after a timeout or transport failure. Rangler returns the same unused, unexpired token for a valid replay. Reusing the key with a different body, or after that token is used or expired, returns 409 Conflict.
Open the hosted modal
Use the npm package when your app has a build system.
import { Connect } from '@rangler/connect-js';
const connect = new Connect({
fetchLinkToken: async () => {
const response = await fetch('/api/rangler/link-token', { method: 'POST' });
const data = await response.json();
return data.link_token;
},
environment: 'sandbox',
onSuccess: async ({ public_token }) => {
await fetch('/api/rangler/exchange-token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ public_token }),
});
},
onClose: ({ status }) => {
console.log(status);
},
});
connect.setup().open();You can also pass a pre-created linkToken directly if your app already fetched one before rendering the button.
For plain HTML or apps that do not use npm, load the hosted script tag. This uses the same token model: the script calls your backend for a link token and sends the public token back to your backend for exchange.
Define window.RanglerConnect.onLoad before the async script loads; otherwise the SDK may load before your callback is registered.
<button id="connect">Connect portfolio</button>
<script>
window.RanglerConnect = window.RanglerConnect || {};
window.RanglerConnect.onLoad = function () {
var connect = window.RanglerConnect.init({
environment: 'sandbox',
fetchLinkToken: async function () {
var response = await fetch('/api/rangler/link-token', { method: 'POST' });
var data = await response.json();
return data.link_token;
},
onSuccess: async function ({ public_token }) {
await fetch('/api/rangler/exchange-token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ public_token }),
});
},
});
document.getElementById('connect').onclick = function () {
connect.open();
};
};
</script>
<script src="https://connect.rangler.co/connect.js" async></script>Create an exchange endpoint
Create this endpoint in your own backend. It receives the one-use public_token from the browser and exchanges it with your server-side API key.
app.post('/api/rangler/exchange-token', async (req, res) => {
const response = await fetch('https://sandbox-api.rangler.co/v1/connect/token/exchange', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': process.env.RANGLER_API_KEY,
},
body: JSON.stringify({ public_token: req.body.public_token }),
});
res.status(response.status).json(await response.json());
});Direct HTTP equivalent:
curl -s https://sandbox-api.rangler.co/v1/connect/token/exchange \
-H "X-API-Key: rgl_test_your_key_here" \
-H "Content-Type: application/json" \
-d '{ "public_token": "rgl_public_..." }'The public token is one-use and short-lived. Store the returned connection_id, not the public token.
Read linked data
curl -s https://sandbox-api.rangler.co/v1/connect/connections/{connection_id}/positions \
-H "X-API-Key: rgl_test_your_key_here"Available resources:
/v1/connect/connections/v1/connect/connections/{connection_id}/accounts/v1/connect/connections/{connection_id}/positions/v1/connect/connections/{connection_id}/transactions
Webhooks
Subscribe to Connect events from the portal:
connect.connection.createdconnect.connection.updatedconnect.connection.revokedconnect.sync.completedconnect.sync.failedconnect.item.login_required
Connect webhook payloads use the same delivery and signature model as other Rangler events.
Provider availability
Sandbox institutions are available for integration testing. Real providers move into beta only after approved access is in place for that provider. Restricted providers are not shown to external customers.