API documentation.
Authenticate with OAuth or personal access tokens, then read and write trades, accounts, connections, imports, and metrics through the Deltalytix Public API v1.
What you can do
Each row opens the matching guide. Include that scope on your token.
| You can | Scope |
|---|---|
| Read the authenticated user profile | profile:read |
| List trades | trades:read |
| Create trades | trades:write |
| List accounts and related metrics | accounts:read |
| List broker connections | connections:read |
| Create connections and trigger sync | connections:write |
| Upload import files | imports:write |
| Read summary, equity, and account metrics | metrics:read |
Authentication
Get a token
You can generate or paste a token from any try panel when you need one.
Deltalytix is its own OAuth 2.0 authorization server. Humans still sign in with Supabase; API tokens are minted and validated by Deltalytix.
Every /api/v1/* request requires a Bearer token:
Authorization: Bearer dltx_at_…REST endpoints are served from https://www.deltalytix.app under API v1 (/api/v1/*). List endpoints accept limit (default 100, max 500) and cursor. Timestamps are ISO 8601. Machine-readable discovery: /openapi.json, /.well-known/openid-configuration, /.well-known/oauth-protected-resource.
Token formats
Tokens are opaque strings. Deltalytix stores only SHA-256 hashes.
| Kind | Prefix | Lifetime |
|---|---|---|
| Access token | dltx_at_<48 hex chars> | 3600 seconds |
| Refresh token | dltx_rt_<48 hex chars> | 30 days (rotated on use) |
| Personal access token (PAT) | dltx_pat_<48 hex chars> | No expiry until revoked |
| Client ID | dltx_app_<24 hex chars> | — |
| Client secret | dltx_secret_<48 hex chars> | Shown once at creation |
Scopes
Request only the scopes your integration needs. Space-separate them in OAuth scope parameters. The full list is in what you can do.
Personal access tokens
PATs are ideal for scripts and private tools. Create and revoke them in Settings → Connections, choosing the scopes you need. The token value is shown once.
curl https://www.deltalytix.app/api/v1/me \
-H "Authorization: Bearer dltx_pat_YOUR_TOKEN"Demo token
The documentation playground can issue a short-lived, read-only demo token without signing in. It authenticates as a shared sample account (DEMO-001) so you can try GET routes. Write scopes require your own docs token or PAT. Signed-in docs playground tokens expire after 24 hours.
OAuth 2.0
Register your app on the Developers page to get a client_id and a client_secret.
1. Authorize
Send the user to the consent page (HTML). Unauthenticated users are redirected to /authentication?next=….
GET /oauth/authorize
?client_id=dltx_app_…
&redirect_uri=https%3A%2F%2Fyour-app.example%2Fcallback
&response_type=code
&scope=profile%3Aread%20trades%3Aread
&state=csrf-token
&code_challenge=BASE64URL_SHA256_OF_VERIFIER
&code_challenge_method=S256Try this route
This authorization endpoint requires a browser redirect and user consent, so it can’t be sent as an API request here.
| Query | Required | Notes |
|---|---|---|
client_id | Yes | Registered OAuth app |
redirect_uri | Yes | Must exactly match a registered URI |
response_type | Yes | Must be code |
scope | Yes | Space-separated scopes |
state | Recommended | CSRF protection; echoed on redirect |
code_challenge | Recommended | PKCE S256 challenge |
code_challenge_method | With challenge | Must be S256 |
On approve, Deltalytix issues a single-use authorization code (10 minute TTL) and redirects. Re-approving the same app for the same user revokes that pair’s prior access and refresh tokens and invalidates unused authorization codes so only the new grant remains usable:
https://your-app.example/callback?code=…&state=…On deny:
https://your-app.example/callback?error=access_denied&state=…2. Exchange the code for tokens
POST /api/oauth/token accepts form-urlencoded or JSON.
curl -X POST https://www.deltalytix.app/api/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "code=AUTHORIZATION_CODE" \
-d "redirect_uri=https://your-app.example/callback" \
-d "client_id=dltx_app_…" \
-d "client_secret=dltx_secret_…" \
-d "code_verifier=PKCE_VERIFIER"Every OAuth app is a confidential client. Send client_secret in the body (client_secret_post) or as Authorization: Basic (client_secret_basic) on the code exchange, on refresh, and on revoke. A missing or wrong secret returns 401 invalid_client. PKCE is still recommended: a code issued with a code_challenge also needs the matching code_verifier.
Success
{
"access_token": "dltx_at_…",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "dltx_rt_…",
"scope": "profile:read trades:read"
}OAuth errors follow RFC 6749:
Error
{
"error": "invalid_grant",
"error_description": "Invalid or expired authorization code"
}3. Call the API
curl https://www.deltalytix.app/api/v1/trades?limit=10 \
-H "Authorization: Bearer dltx_at_…"const res = await fetch("https://www.deltalytix.app/api/v1/trades?limit=10", {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});4. Refresh the access token
curl -X POST https://www.deltalytix.app/api/oauth/token \
-H "Content-Type: application/json" \
-d '{
"grant_type": "refresh_token",
"refresh_token": "dltx_rt_…",
"client_id": "dltx_app_…",
"client_secret": "dltx_secret_…"
}'Refresh tokens rotate on use. Store the new refresh_token from each successful response.
5. Revoke a token
curl -X POST https://www.deltalytix.app/api/oauth/revoke \
-H "Content-Type: application/json" \
-d '{
"token": "dltx_at_…",
"client_id": "dltx_app_…",
"client_secret": "dltx_secret_…"
}'Revocation always returns 200, whether or not the token was found.
Resource-server failures
Missing or invalid Bearer tokens return 401:
Error
{
"error": "unauthorized",
"message": "…"
}with a WWW-Authenticate header pointing at protected-resource metadata.
Valid tokens without the required scope return 403:
Error
{
"error": "insufficient_scope",
"message": "…"
}Managing apps and tokens
On the Developers page you can:
- Create OAuth apps (name, redirect URIs, allowed scopes) —
client_idis always visible;client_secretis shown once. Redirect URIs must be absolutehttpsURLs (plainhttponly onlocalhost) without a fragment. - Edit an app, regenerate its secret, or delete it
In Settings → Connections you can:
- Create and revoke personal access tokens with chosen scopes — the PAT value is shown once
- Revoke apps you authorized on your own account
Trades
List and create trades with filters, pagination, and the shared import/dedupe pipeline.
Manage the authenticated user’s trade history.
List trades
GET /api/v1/tradesScope: trades:read
Query parameters
| Parameter | Description |
|---|---|
accountNumber | Filter by account number |
instrument | Filter by instrument symbol |
side | Filter by side |
from | Inclusive lower bound on entryDate (ISO 8601) |
to | Inclusive upper bound on entryDate (ISO 8601) |
cursor | Opaque pagination cursor |
limit | Page size (default 100, max 500) |
Example
curl "https://www.deltalytix.app/api/v1/trades?accountNumber=SIM-001&limit=50" \
-H "Authorization: Bearer dltx_at_…"const params = new URLSearchParams({
accountNumber: "SIM-001",
from: "2026-01-01T00:00:00.000Z",
limit: "50",
});
const res = await fetch(`https://www.deltalytix.app/api/v1/trades?${params}`, {
headers: { Authorization: `Bearer ${accessToken}` },
});
const page = await res.json();Response 200
Success
{
"data": [
{
"id": "trade_01HZX…",
"accountNumber": "SIM-001",
"instrument": "ES",
"side": "long",
"quantity": 2,
"entryPrice": 5120.25,
"closePrice": 5128.5,
"entryDate": "2026-03-15T14:30:00.000Z",
"closeDate": "2026-03-15T15:10:00.000Z",
"pnl": 825,
"commission": 8.64,
"timeInPosition": 2400,
"tags": ["breakout"],
"comment": "Morning continuation",
"createdAt": "2026-03-15T15:12:01.000Z"
}
],
"nextCursor": null
}Create trades
POST /api/v1/tradesScope: trades:write
Creates one or more trades using the same dedupe pipeline as the dashboard (UUID v5 identity + createMany with skipDuplicates). Duplicate payloads are counted, not treated as hard failures.
Request body
{
"trades": [
{
"accountNumber": "SIM-001",
"instrument": "ES",
"quantity": 2,
"entryPrice": 5120.25,
"closePrice": 5128.5,
"entryDate": "2026-03-15T14:30:00.000Z",
"closeDate": "2026-03-15T15:10:00.000Z",
"pnl": 825,
"side": "long",
"commission": 8.64,
"entryId": "optional-broker-entry-id",
"closeId": "optional-broker-close-id",
"timeInPosition": 2400,
"tags": ["breakout"],
"comment": "Morning continuation"
}
]
}| Field | Required | Notes |
|---|---|---|
accountNumber | Yes | Target account |
instrument | Yes | Symbol / contract |
quantity | Yes | Size |
entryPrice | Yes | Entry price |
closePrice | Yes | Exit price |
entryDate | Yes | ISO 8601 |
closeDate | Yes | ISO 8601 |
pnl | Yes | Realized P&L |
side | No | e.g. long / short |
commission | No | Fees |
entryId | No | Broker entry identifier |
closeId | No | Broker exit identifier |
timeInPosition | No | Duration in seconds |
tags | No | String array |
comment | No | Free text |
Example
curl -X POST https://www.deltalytix.app/api/v1/trades \
-H "Authorization: Bearer dltx_at_…" \
-H "Content-Type: application/json" \
-d '{
"trades": [
{
"accountNumber": "SIM-001",
"instrument": "ES",
"quantity": 2,
"entryPrice": 5120.25,
"closePrice": 5128.5,
"entryDate": "2026-03-15T14:30:00.000Z",
"closeDate": "2026-03-15T15:10:00.000Z",
"pnl": 825,
"side": "long",
"commission": 8.64
}
]
}'Response 201
Success
{
"imported": 1,
"duplicates": 0,
"total": 1
}If every row is a duplicate, the response still returns success with counts (for example "imported": 0, "duplicates": 3, "total": 3), not an error status.
Accounts
List trading accounts, payouts, and optional per-account performance metrics.
Read the authenticated user’s accounts and related payout information.
List accounts
GET /api/v1/accountsScope: accounts:read
Query parameters
| Parameter | Description |
|---|---|
includeMetrics | When true, attaches per-account metrics from computeMetricsForAccounts |
Example
curl "https://www.deltalytix.app/api/v1/accounts?includeMetrics=true" \
-H "Authorization: Bearer dltx_at_…"const res = await fetch(
"https://www.deltalytix.app/api/v1/accounts?includeMetrics=true",
{
headers: { Authorization: `Bearer ${accessToken}` },
},
);
const payload = await res.json();Response 200
Success
{
"data": [
{
"id": "acc_01HZX…",
"number": "SIM-001",
"propfirm": "Topstep",
"startingBalance": 50000,
"createdAt": "2026-01-15T09:00:00.000Z",
"hidden": false,
"group": { "id": "grp_01HZX…", "name": "Evaluations" },
"balance": 52480.12,
"payouts": [
{
"id": "pay_01HZX…",
"amount": 2500,
"date": "2026-02-01T00:00:00.000Z",
"status": "PAID"
}
],
"metrics": { "…": "…" }
}
]
}Hidden accounts
Every account is returned, including ones hidden in the dashboard. Hiding an account moves it into the Hidden Accounts group, so hidden is true for those accounts and group.name is "Hidden Accounts". Skip accounts where hidden is true to match what the dashboard shows. group is null for accounts that are not in any group.
metrics is present only when includeMetrics=true. Metric fields include balance, drawdown, consistency, and progress toward account targets.
For portfolio-level analytics across all accounts, prefer the Metrics endpoints.
Connections
List broker connections, create server-syncable connections, and trigger sync.
Broker connections sync trades into Deltalytix. Raw broker tokens are never returned by the API.
Server-syncable services in v1:
service | Create | Sync | Notes |
|---|---|---|---|
ibkr | token, queryId | yes | IBKR Flex Web Service |
dxfeed | login, password, propFirmId | yes | Volumetrica / DxFeed prop firms |
rithmic-protocol | username, password, systemName, historyStartDate, optional gatewayId | yes | Rithmic Protocol gateway |
tradovate | dashboard OAuth only | yes | Connect in the Deltalytix dashboard, then list and sync via the API |
Classic rithmic, thor, and etp may appear in GET listings but are not creatable or syncable through this API.
List connections
GET /api/v1/connectionsScope: connections:read
Example
curl https://www.deltalytix.app/api/v1/connections \
-H "Authorization: Bearer dltx_at_…"Response 200
Success
{
"data": [
{
"id": "conn_01HZX…",
"service": "ibkr",
"externalId": "flex-query-123",
"lastSyncedAt": "2026-03-15T16:00:00.000Z",
"environment": "live",
"accountNumbers": ["U1234567"]
}
],
"nextCursor": null
}| Field | Description |
|---|---|
id | Connection identifier |
service | Provider key (ibkr, tradovate, dxfeed, rithmic-protocol, …) |
externalId | Provider-side identifier (never a secret token) |
lastSyncedAt | Last successful sync timestamp, or null |
environment | e.g. live / demo when applicable |
accountNumbers | Linked Deltalytix account numbers |
Create a connection
POST /api/v1/connectionsScope: connections:write
Body is discriminated by service. On success the API stores credentials (encrypted), links accounts when available, attempts an initial sync, and returns the connection without secrets.
IBKR Flex
{
"service": "ibkr",
"token": "<flex token>",
"queryId": "<flex query id>"
}curl -X POST https://www.deltalytix.app/api/v1/connections \
-H "Authorization: Bearer dltx_at_…" \
-H "Content-Type: application/json" \
-d '{
"service": "ibkr",
"token": "YOUR_FLEX_TOKEN",
"queryId": "YOUR_FLEX_QUERY_ID"
}'DxFeed
{
"service": "dxfeed",
"login": "trader@example.com",
"password": "…",
"propFirmId": "miltraders"
}propFirmId must be an enabled firm id (for example miltraders, myfundedfutures, phoenixtraderfunding).
Rithmic Protocol
{
"service": "rithmic-protocol",
"username": "your-username",
"password": "…",
"systemName": "Rithmic Paper Trading",
"historyStartDate": "2026-01-01",
"gatewayId": "core"
}historyStartDate is YYYY-MM-DD. gatewayId is optional (e.g. core, nyc, test).
Response 201
Success
{
"id": "conn_01HZX…",
"service": "ibkr",
"externalId": "flex-query-123",
"accountNumbers": ["U1234567"],
"imported": 42,
"duplicates": 3
}If the connection is stored but the initial sync fails, the response still returns 201 with imported: 0 and a warning message.
Unsupported service → 422
Error
{
"error": "unsupported_service",
"message": "Only credential-based connection services can be created in v1",
"details": {
"supported": ["ibkr", "dxfeed", "rithmic-protocol"]
}
}Trigger a sync
POST /api/v1/connections/{id}/syncScope: connections:write
Works for ibkr, dxfeed, rithmic-protocol, and tradovate connections that were already linked in the dashboard.
Example
curl -X POST https://www.deltalytix.app/api/v1/connections/conn_01HZX…/sync \
-H "Authorization: Bearer dltx_at_…"Response 200
Success
{
"status": "completed",
"imported": 42,
"duplicates": 3
}Imports
Upload CSV or XLSX trade files with AI mapping or platform-specific parsers.
Import trades from files without going through the dashboard UI.
Upload a file
POST /api/v1/importsScope: imports:write
Content-Type: multipart/form-data
Form fields
| Field | Required | Description |
|---|---|---|
file | Yes | .csv or .xlsx file |
type | Yes | "ai" or a platform name |
accountNumber | Yes | Destination account number |
AI import (type=ai)
The server parses the file (Papa Parse for CSV, read-excel-file for XLSX), runs the same AI mapping and formatting pipeline used by the dashboard, then saves trades through the shared trades-save core.
curl -X POST https://www.deltalytix.app/api/v1/imports \
-H "Authorization: Bearer dltx_at_…" \
-F "file=@./trades.csv" \
-F "type=ai" \
-F "accountNumber=SIM-001"const form = new FormData();
form.append("file", fileInput.files[0]);
form.append("type", "ai");
form.append("accountNumber", "SIM-001");
const res = await fetch("https://www.deltalytix.app/api/v1/imports", {
method: "POST",
headers: { Authorization: `Bearer ${accessToken}` },
body: form,
});
const result = await res.json();Platform import (type=<platform>)
When type is a platform key, the server uses a registered parser extracted from the dashboard import flow. Supported platforms depend on which parsers are available as pure functions (candidates include tradezella, tradovate, quantower, topstep, ftmo, atas, and others).
curl -X POST https://www.deltalytix.app/api/v1/imports \
-H "Authorization: Bearer dltx_at_…" \
-F "file=@./export.xlsx" \
-F "type=tradovate" \
-F "accountNumber=SIM-001"Success response
Success
{
"imported": 128,
"duplicates": 4,
"total": 132,
"accountNumber": "SIM-001"
}Unsupported platform → 422
Error
{
"error": "unsupported_service",
"message": "Unknown import type",
"details": {
"supported": ["ai", "tradovate", "tradezella", "quantower"]
}
}The exact details.supported list reflects the live parser registry.
Metrics
Summary statistics, equity curves, and per-account performance metrics.
Compute performance analytics from the authenticated user’s trades.
All metrics endpoints require the metrics:read scope.
Summary
GET /api/v1/metrics/summaryUses the same filters as GET /api/v1/trades. Statistics come from calculateStatistics plus the dashboard profit-factor formula (gross wins and losses net of commission).
Query parameters
| Parameter | Description |
|---|---|
accountNumber | Filter by account |
instrument | Filter by instrument |
side | Filter by side |
from / to | Bounds on entryDate |
cursor / limit | Accepted for consistency with trades filters where applicable |
Example
curl "https://www.deltalytix.app/api/v1/metrics/summary?from=2026-01-01T00:00:00.000Z" \
-H "Authorization: Bearer dltx_at_…"Response 200
Success
{
"totalPnl": 12450.5,
"totalCommission": 312.4,
"tradeCount": 186,
"winCount": 102,
"lossCount": 78,
"breakevenCount": 6,
"winRate": 0.5484,
"profitFactor": 1.62,
"averageWin": 215.3,
"averageLoss": -142.1,
"longCount": 110,
"shortCount": 76,
"tradingDays": 48
}Equity curve
GET /api/v1/metrics/equityBuilds daily equity points via computeEquityChartData.
Query parameters
| Parameter | Description |
|---|---|
from | Start date (ISO 8601) |
to | End date (ISO 8601) |
accountNumbers | Comma-separated account numbers; when provided, also returns per-account series |
Example
curl "https://www.deltalytix.app/api/v1/metrics/equity?accountNumbers=SIM-001,SIM-002&from=2026-01-01T00:00:00.000Z" \
-H "Authorization: Bearer dltx_at_…"const params = new URLSearchParams({
accountNumbers: "SIM-001,SIM-002",
from: "2026-01-01T00:00:00.000Z",
});
const res = await fetch(
`https://www.deltalytix.app/api/v1/metrics/equity?${params}`,
{ headers: { Authorization: `Bearer ${accessToken}` } },
);
const equity = await res.json();Response 200
Success
{
"points": [
{
"date": "2026-01-02",
"equity": 50120.5
},
{
"date": "2026-01-03",
"equity": 50385.25
}
],
"accounts": {
"SIM-001": [
{ "date": "2026-01-02", "equity": 25050.0 },
{ "date": "2026-01-03", "equity": 25210.75 }
],
"SIM-002": [
{ "date": "2026-01-02", "equity": 25070.5 },
{ "date": "2026-01-03", "equity": 25174.5 }
]
}
}The accounts object is included when accountNumbers is requested.
Account metrics
GET /api/v1/metrics/accountsRuns computeMetricsForAccounts for all of the user’s accounts.
Example
curl https://www.deltalytix.app/api/v1/metrics/accounts \
-H "Authorization: Bearer dltx_at_…"Response 200
Success
{
"data": [
{
"accountNumber": "SIM-001",
"balance": 52480.12,
"drawdown": 3.4,
"consistency": 0.82,
"progress": 0.61
}
]
}Errors
REST error envelope, HTTP status codes, and OAuth error responses.
REST error envelope
API v1 errors use a consistent JSON body:
Error
{
"error": "machine_code",
"message": "Human-readable explanation",
"details": {}
}| Field | Description |
|---|---|
error | Stable machine-readable code |
message | Human-readable explanation |
details | Optional structured context (validation issues, supported values, …) |
HTTP status codes
| Status | Typical cause |
|---|---|
400 | Validation error (malformed query/body) |
401 | Missing, expired, revoked, or invalid Bearer token |
403 | Authenticated but missing required scope |
404 | Resource not found |
422 | Semantically invalid request (unsupported service/type) |
500 | Unexpected server error |
Unauthorized (401)
Error
{
"error": "unauthorized",
"message": "Missing or invalid access token"
}Responses include:
WWW-Authenticate: Bearer resource_metadata="https://www.deltalytix.app/.well-known/oauth-protected-resource"Insufficient scope (403)
Error
{
"error": "insufficient_scope",
"message": "Token is missing required scope trades:write"
}Unsupported service (422)
Error
{
"error": "unsupported_service",
"message": "Only credential-based connection services can be created in v1",
"details": {
"supported": ["ibkr", "dxfeed", "rithmic-protocol"]
}
}OAuth errors
Token endpoint failures use the RFC 6749 shape (not the REST envelope):
Error
{
"error": "invalid_grant",
"error_description": "Authorization code is invalid or expired"
}Common OAuth error values include invalid_request, invalid_client, invalid_grant, unauthorized_client, unsupported_grant_type, and invalid_scope.
Authorization endpoint denials redirect to the registered redirect_uri with error=access_denied (and the original state when provided).
Non-error counts
Some write endpoints report duplicate or zero-import outcomes with HTTP success and count fields instead of an error body. For example, POST /api/v1/trades returns 201 with "imported": 0 when every row already exists.
Handling tips
- Branch first on HTTP status, then on
error. - Surface
messageto developers; useerrorfor programmatic handling. - Treat
details.supportedas the authoritative allow-list when present. - On
401, refresh the OAuth access token or prompt for a new PAT; do not retry indefinitely.
OpenAPI reference
Paths, methods, parameters, and response codes from the live OpenAPI document.
Download openapi.jsonLoading OpenAPI document…