Developer documentation
AtlasForge Financial API
A complete reference for building on the AtlasForge platform — connectivity, payments, identity, and AI-powered insights.
Introduction
The AtlasForge Financial API is organized around REST. Our API has predictable resource-oriented URLs, returns JSON-encoded responses, and uses standard HTTP response codes, authentication, and verbs.
The base URL for all API requests is:
https://atlasforgefinancial.com/v1
Account linking is powered by Plaid: your frontend exchanges a Link token for a public token, then your backend calls /accounts/link on AtlasForge to exchange it and start syncing.
Authentication
Authenticate by including the API key we email you in theAuthorizationheader of every request. Use your test key (prefixafg_test_) against the Plaid sandbox; the live key (afg_live_) unlocks production once we've completed your onboarding.
Authorization: Bearer afg_test_…
All API requests must be made over HTTPS. Requests without a key return 401; live keys against an account that isn't production-enabled return402. You can probe configuration without auth at GET /api/v1/health.
Errors
AtlasForge Financial uses conventional HTTP response codes to indicate the success or failure of an API request:
- 200 — OK. Everything worked as expected.
- 400 — Bad Request. The request was unacceptable, often due to a missing parameter.
- 401 — Unauthorized. No valid API key provided.
- 404 — Not Found. The requested resource doesn't exist.
- 429 — Too Many Requests. Rate limit exceeded.
- 5xx — Server errors on our end (rare).
Plaid Link token
POST/v1/link/token/createYour frontend exchanges this token for a Plaid Link session. After the user authenticates with their bank, Plaid Link returns apublic_tokenthat your backend sends to /accounts/link.
Body parameters
| Field | Type | Description |
|---|---|---|
| user_id | string | Stable identifier for your end-user. Combined with your partner id to form Plaid's client_user_id. |
curl -X POST https://atlasforgefinancial.com/v1/link/token/create \
-H "Authorization: Bearer $ATLAS_KEY" \
-H "Content-Type: application/json" \
-d '{ "user_id": "usr_8M2nQ" }'Response · 200 OK
{
"link_token": "link-sandbox-9e2a...",
"expiration": "2026-06-16T16:21:00Z",
"request_id": "rq_4Lp9"
}Link accounts
POST/v1/accounts/linkExchange the Plaid public_token for a long-lived access token that AtlasForge stores against(partner_id, user_id, item_id). Returns the initial set of accounts with balances.
Body parameters
| Field | Type | Description |
|---|---|---|
| public_token | string | Token returned by Plaid Link's onSuccess callback. |
| user_id | string | Same end-user id you used for /link/token/create. |
| institution | object? | Optional { institution_id, name } from Plaid metadata for nicer reporting. |
curl -X POST https://atlasforgefinancial.com/v1/accounts/link \
-H "Authorization: Bearer $ATLAS_KEY" \
-H "Content-Type: application/json" \
-d '{
"public_token": "public-sandbox-9e2a...",
"user_id": "usr_8M2nQ",
"institution": { "institution_id": "ins_109508", "name": "Sandbox Bank" }
}'Response · 200 OK
{
"item_id": "item_a1B2c3D4",
"institution": { "institution_id": "ins_109508", "name": "Sandbox Bank" },
"accounts": [
{
"account_id": "acc_42",
"name": "Plaid Checking",
"mask": "0000",
"type": "depository",
"subtype": "checking",
"current_balance": 110.0,
"available_balance": 100.0,
"iso_currency_code": "USD"
}
]
}Every successful link also writes an account_link event into your partner-portal usage, so dashboards reflect activity automatically.
List accounts
GET/v1/accountsReturns current balances for every account linked to a given end-user across all of their Plaid items.
Query parameters
| Field | Type | Description |
|---|---|---|
| user_id | string | Required. Your end-user id. |
curl "https://atlasforgefinancial.com/v1/accounts?user_id=usr_8M2nQ" \ -H "Authorization: Bearer $ATLAS_KEY"
Response · 200 OK
{
"accounts": [
{
"account_id": "acc_42",
"name": "Plaid Checking",
"type": "depository",
"subtype": "checking",
"current_balance": 110.0,
"available_balance": 100.0,
"iso_currency_code": "USD"
}
]
}Transactions
GET/v1/transactionsReturns enriched transactions for an end-user using Plaid's /transactions/sync under the hood. We persist the cursor for you — call without one to resume from your last sync.
Query parameters
| Field | Type | Description |
|---|---|---|
| user_id | string | Required. Your end-user id. |
| since | YYYY-MM-DD | Optional. Only return transactions on or after this date. |
| limit | integer | Page size — defaults to 100, max 500. |
| cursor | string | Optional. Plaid sync cursor from a previous response. We store the latest one for you if omitted. |
curl "https://atlasforgefinancial.com/v1/transactions?user_id=usr_8M2nQ&limit=2" \ -H "Authorization: Bearer $ATLAS_KEY"
Response · 200 OK
{
"transactions": [
{
"transaction_id": "txn_7gK1",
"account_id": "acc_42",
"merchant_name": "Trader Joe's",
"amount": 42.18,
"iso_currency_code": "USD",
"date": "2026-06-13",
"pending": false,
"personal_finance_category_primary": "FOOD_AND_DRINK"
}
],
"next_cursor": "eyJsYXN0...",
"has_more": true
}Each transaction surfaced is also recorded as a transaction event in your partner portal — no separate ingest call needed.
Transfers
POST/v1/transfersInitiates a money movement between two linked accounts, or to a saved recipient.
curl -X POST https://atlasforgefinancial.com/v1/transfers \
-H "Authorization: Bearer $ATLAS_KEY" \
-H "Idempotency-Key: $UUID" \
-d '{
"from_account_id": "acc_a1B2c3D4",
"to_account_id": "acc_recipient_42",
"amount": 25.00,
"currency": "USD",
"memo": "Coffee"
}'Response · 200 OK
{
"id": "tfr_M0p9X",
"status": "submitted",
"rail": "rtp",
"estimated_settlement": "2026-06-14T19:23:11Z"
}Safe to Spend
POST/v1/calculate/safe-to-spendComputes a user's daily safe-to-spend allowance from their cash balance, upcoming bills, savings commitments and debt commitments. This is the headline calculation behind the AtlasForge platform.
curl -X POST https://atlasforgefinancial.com/api/v1/calculate/safe-to-spend \
-H "X-API-Key: $ATLAS_KEY" \
-d '{
"end_user_id": "usr_8M2nQ",
"cash_balance": 4218.50,
"upcoming_bills": [{ "amount": 1450, "due_in_days": 9 }],
"savings_commitments": 200,
"debt_commitments": 0,
"cash_buffer": 250
}'Response · 200 OK
{
"safe_to_spend_today": 78.45,
"daily_allowance": 78.45,
"monthly_safe_to_spend": 2353.50,
"breakdown": {
"cash_after_buffer": 3968.50,
"upcoming_bills_total": 1450,
"savings_commitments": 200,
"debt_commitments": 0,
"discretionary_pool": 2353.50,
"days_in_period": 30
},
"calculation_id": "calc_8M2nQ_2026-06-14",
"calculated_at": "2026-06-14T18:00:00Z",
"is_sandbox": false
}Use /api/v1/ingest instead if you want AtlasForge to cache the result and fire a calculation.updated webhook to your backend. The cached value powers your end-user dashboards and the partner portal.
AI Insights
GET/v1/insights/cashflowReturns a 30-day cash flow forecast for the authenticated user, including projected balances and identified shortfalls.
curl https://atlasforgefinancial.com/v1/insights/cashflow \ -H "Authorization: Bearer $ATLAS_KEY"
Response · 200 OK
{
"horizon_days": 30,
"projected_min_balance": 412.18,
"projected_min_date": "2026-06-21",
"confidence": 0.92,
"alerts": [
{ "type": "low_balance", "date": "2026-06-21" }
]
}Ingest API (Server-to-server)
The Ingest API lets your own backend stream partner usage events (signups, account links, transactions) into AtlasForge so they appear on the partner portal and admin dashboards in real time.
Endpoints are secret-protected — not partner-accessible. Pass X-Atlas-Ingest-Secret (or Authorization: Bearer …) on every request.
Endpoints
- POST /api/ingest/event — single event
- POST /api/ingest/events/batch — up to 500 events
- GET /api/ingest/ping — connectivity check
Event payload
| Field | Type | Description |
|---|---|---|
| partner_id | string | Required. AtlasForge partner id (visible in the admin dashboard URL / partners list). |
| event_type | enum | Required. One of: user_signup, account_link, transaction. |
| user_id | string | Your end-user id. Used for active-user de-dup. |
| amount_cents | integer | Only for transactions. USD cents (no decimals). |
| category | string | Only for transactions. e.g. groceries, rent, dining, subscription. |
| institution | string | Only for account_link. e.g. chase, vanguard. |
| occurred_at | ISO 8601 | Optional. Event timestamp. Defaults to now. |
Example — curl
# Single event
curl -X POST https://atlasforgefinancial.com/api/ingest/event \
-H "X-Atlas-Ingest-Secret: $ATLAS_INGEST_SECRET" \
-H "Content-Type: application/json" \
-d '{
"partner_id": "bb8eca7c-f0c3-4ee3-9a0a-c97bc66b0f2b",
"event_type": "transaction",
"user_id": "usr_42",
"amount_cents": 4218,
"category": "groceries"
}'Response · 200 OK
{
"accepted": 1,
"partners": ["bb8eca7c-f0c3-4ee3-9a0a-c97bc66b0f2b"]
}Example — batch (curl)
curl -X POST https://atlasforgefinancial.com/api/ingest/events/batch \
-H "X-Atlas-Ingest-Secret: $ATLAS_INGEST_SECRET" \
-H "Content-Type: application/json" \
-d '{
"events": [
{ "partner_id": "p_1", "event_type": "user_signup", "user_id": "usr_1" },
{ "partner_id": "p_1", "event_type": "account_link", "user_id": "usr_1",
"institution": "chase" },
{ "partner_id": "p_1", "event_type": "transaction", "user_id": "usr_1",
"amount_cents": 4218, "category": "groceries" }
]
}'Example — Node.js
import fetch from "node-fetch";
const ATLAS = "https://atlasforgefinancial.com";
const SECRET = process.env.ATLAS_INGEST_SECRET;
export async function atlasIngest(event) {
const res = await fetch(`${ATLAS}/api/ingest/event`, {
method: "POST",
headers: {
"X-Atlas-Ingest-Secret": SECRET,
"Content-Type": "application/json",
},
body: JSON.stringify(event),
});
if (!res.ok) {
const err = await res.text();
throw new Error(`Atlas ingest failed (${res.status}): ${err}`);
}
return res.json();
}
// Fire-and-forget after a real transaction in your app:
await atlasIngest({
partner_id: "bb8eca7c-...",
event_type: "transaction",
user_id: "usr_42",
amount_cents: 4218,
category: "groceries",
});Example — Python
import os
import requests
ATLAS = "https://atlasforgefinancial.com"
SECRET = os.environ["ATLAS_INGEST_SECRET"]
def atlas_ingest(event: dict) -> dict:
r = requests.post(
f"{ATLAS}/api/ingest/event",
headers={"X-Atlas-Ingest-Secret": SECRET},
json=event,
timeout=5,
)
r.raise_for_status()
return r.json()
# Batch up to 500 events in one call:
def atlas_ingest_batch(events: list[dict]) -> dict:
r = requests.post(
f"{ATLAS}/api/ingest/events/batch",
headers={"X-Atlas-Ingest-Secret": SECRET},
json={"events": events},
timeout=10,
)
r.raise_for_status()
return r.json()Responses
- 200 — Events accepted; returns count and resolved partner ids.
- 400 — One or more partner_ids don't exist. Response includes a missing array.
- 401 — Missing or wrong X-Atlas-Ingest-Secret.
- 503 — Ingest secret not configured on the server.
Tip: deliver events fire-and-forget from your other app so a slow AtlasForge response never blocks your hot path. Batching is significantly faster for high-volume events.
Webhooks
AtlasForge sends webhook events when relevant changes occur. Every delivery is signed with an HMAC-SHA256 signature in theAtlas-Signature header.
POST https://example.com/webhooks
Atlas-Signature: t=1718...,v1=8a3...
{
"id": "evt_5Aq",
"type": "transfer.settled",
"data": { "transfer_id": "tfr_M0p9X" }
}Rate limits
The default rate limit is 60 requests per second per API key. Enterprise plans receive higher limits and burst capacity. The current usage is returned in the X-RateLimit-* response headers.
