This document describes the HTTP API for the Stellar Portfolio Rebalancer backend. For full request/response schemas and try-it-now usage, use the OpenAPI 3.0 spec and Swagger UI.
| Resource | URL / action |
|---|---|
| Swagger UI (interactive docs) | http://localhost:3001/api-docs (when the backend is running on the default port) |
| OpenAPI 3.0 spec (JSON) | http://localhost:3001/api-docs.json or /api-docs/openapi.json (same document) — use for Postman (Import → Link) or other tools |
| Postman collection | Import the OpenAPI spec: see Postman collection below |
- Development:
http://localhost:3001(or the port set byPORTon the backend) - Production: Your deployed backend URL
All API routes below are relative to the base URL.
| Namespace | Purpose |
|---|---|
/api/v1/* |
Canonical portfolio/API surface. Prefer this for new clients; responses do not include deprecation headers. |
/api/* (same paths, no v1 segment) |
Legacy compatibility; the server may attach Deprecation, Sunset, and Link headers (RFC 8594). |
/api/auth/* |
JWT login, refresh, and logout — not under /api/v1 (see backend/src/http/mountApiRoutes.ts). |
The frontend defaults to /api/v1 for resource routes via VITE_API_VERSION and API_RESOURCE_ROOT in frontend/src/config/api.ts (see frontend/.env.example). Set VITE_USE_LEGACY_API=true only for emergency rollback to unversioned /api/*.
- Most endpoints are unauthenticated.
- Admin-only endpoints (e.g. auto-rebalancer start/stop, sync-onchain, auto-rebalancer history) require admin auth (e.g.
Authorizationheader or project-specific mechanism). See the OpenAPI spec and your deployment config for details.
Success responses use a common envelope:
{
"success": true,
"data": { ... },
"error": null,
"timestamp": "2025-01-01T00:00:00.000Z",
"meta": { ... }
}Error responses:
{
"success": false,
"data": null,
"error": {
"code": "VALIDATION_ERROR",
"message": "Human-readable message",
"details": { ... }
},
"timestamp": "2025-01-01T00:00:00.000Z"
}Select write endpoints support the Idempotency-Key request header. When provided, the server stores the response of the first successful call and returns the same response for any subsequent request that carries the same key, preventing duplicate side effects from client retries.
- Include an
Idempotency-Keyheader (1–255 characters, e.g. a UUID) on any supportedPOSTorPATCHrequest. - On the first call the server processes the request normally and caches the response (TTL: 24 hours).
- On any repeat call with the same key and identical body, the server returns the cached response with an
Idempotency-Replayed: trueheader — no side effects are triggered again. - If the same key is reused with a different body, the server returns
409 CONFLICT.
| Scenario | HTTP Status | Error code |
|---|---|---|
| Same key, same body (retry) | Cached status | — (Idempotency-Replayed: true) |
| Same key, different body | 409 |
CONFLICT |
| Key is empty or > 255 chars | 400 |
VALIDATION_ERROR |
| Method | Path | Notes |
|---|---|---|
POST |
/api/consent |
Consent recording — replay is safe; double-submit has no additional effect |
POST |
/api/portfolio |
Portfolio creation — prevents duplicate portfolios on retry |
POST |
/api/portfolio/:id/rebalance |
Rebalance execution — prevents double-execution on retry |
POST |
/api/rebalance/history |
Rebalance event recording — prevents duplicate history entries |
POST |
/api/notifications/subscribe |
Notification subscription — idempotent preference upsert |
POST |
/api/admin/assets |
Asset registry — prevents duplicate asset creation on retry |
PATCH |
/api/admin/assets/:symbol |
Asset enable/disable — safe to replay the same state change |
POST /api/consent
Content-Type: application/json
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
{
"userId": "GABC...",
"terms": true,
"privacy": true,
"cookies": true
}A retry with the same Idempotency-Key and body returns the cached 200 response immediately. A retry with a different body returns 409 CONFLICT.
Idempotency keys are automatically cleaned up to prevent unbounded table growth:
| Setting | Value |
|---|---|
| Key TTL | 24 hours from creation |
| Cleanup cadence | Every 60 minutes (via BullMQ scheduled job) |
| Startup cleanup | Runs once on server startup |
Expired keys (older than 24 hours) are permanently deleted during each cleanup cycle. The cleanup job logs the number of removed keys on every run for operational visibility. When Redis is unavailable, the cleanup job is not scheduled; expired keys are still filtered out at query time and will be purged once the scheduler resumes.
- GET / — API info, version, feature flags, and endpoint list.
- GET /health — Health check; includes auto-rebalancer status.
- GET /ready — Deep readiness probe covering database, Redis/queues, workers, indexer, and auto-rebalancer startup.
- POST /api/portfolio — Create portfolio (
userAddress,allocations,threshold, optionalslippageTolerance). Allocations must sum to 100%; threshold 1–50%. SupportsIdempotency-Key. - GET /api/portfolio/{id} — Get portfolio by ID.
- GET /api/user/{address}/portfolios — List portfolios for a Stellar address. When JWT auth is enabled, the token subject must match
:address(otherwise403). In demo mode, public-by-address listing is allowed only whenALLOW_PUBLIC_USER_PORTFOLIOS_IN_DEMOis enabled. - GET /api/portfolio/{id}/rebalance-plan — Get rebalance plan (total value, slippage, prices).
- POST /api/portfolio/{id}/rebalance — Execute rebalance (body optional:
{ options: { simulateOnly, ignoreSafetyChecks, slippageOverrides } }). SupportsIdempotency-Key. - GET /api/portfolio/{id}/analytics — Analytics time series (query:
days, default 30). - GET /api/portfolio/{id}/performance-summary — Performance summary.
- GET /api/rebalance/history — List rebalance events (query:
portfolioId,limit,source,startTimestamp,endTimestamp,syncOnChain). - POST /api/rebalance/history — Record a rebalance event. Supports
Idempotency-Key. - POST /api/rebalance/history/sync-onchain — Sync on-chain rebalance history (admin).
- GET /api/risk/metrics/{portfolioId} — Risk metrics and recommendations.
- GET /api/risk/check/{portfolioId} — Check if rebalance is allowed (risk check).
- GET /api/prices — Current asset prices (e.g. XLM, BTC, ETH, USDC).
- GET /api/prices/enhanced — Prices with risk/volatility info.
- GET /api/market/{asset}/details — Market details for one asset.
- GET /api/market/{asset}/chart — Price history for charting (query:
days, default 7).
- GET /api/auto-rebalancer/status — Status and statistics.
- POST /api/auto-rebalancer/start — Start (admin).
- POST /api/auto-rebalancer/stop — Stop (admin).
- POST /api/auto-rebalancer/force-check — Force check (admin).
- GET /api/auto-rebalancer/history — Auto-rebalance history (admin; query:
portfolioId,limit).
- GET /api/system/status — System status (portfolios, history, risk, auto-rebalancer, indexer, feature flags).
- GET /api/queue/health — BullMQ queue health and Redis connectivity.
- POST /api/notifications/subscribe — Subscribe (userId, email/webhook, events). Supports
Idempotency-Key. - GET /api/notifications/preferences — Get preferences (query:
userId). - DELETE /api/notifications/unsubscribe — Unsubscribe (query:
userId).
The API is described in full by an OpenAPI 3.0 specification:
- Served by backend: When the backend is running, the spec is available at:
- JSON:
GET /api-docs.json(alias:GET /api-docs/openapi.json)
- JSON:
- Swagger UI at
/api-docsuses this spec and provides:- All endpoints with descriptions
- Request and response schemas
- Examples and try-it-now (against the running server)
Third-party integration (e.g. code generation, API gateways, testing) can use the same OpenAPI spec.
You can use the OpenAPI spec as a Postman collection source:
-
Import from URL (recommended)
- In Postman: Import → Link.
- Enter:
http://localhost:3001/api-docs.json - Ensure the backend is running so the URL is reachable.
-
Import from file
- Export the spec to a file (see below), then in Postman: Import → Upload and select the JSON file.
Export spec to file (optional):
From the backend directory:
cd backend
npm run openapi:exportThis writes backend/openapi.json. In Postman: Import → Upload → select openapi.json.
The authoritative spec is backend/src/openapi/spec.ts (what Swagger and the server use). The checked-in backend/openapi.json is produced from it for Postman and CI diffing. See backend/docs/openapi.md.
To ensure that API.md, openapi.json, and spec.ts stay aligned, run the validation script below.
To validate sync:
cd backend
npm run api:validateTo refresh generated outputs (if you've changed the API code):
cd backend
npm run openapi:exportThe CI pipeline will fail if documents are out of sync.
POST /api/portfolio
Content-Type: application/json
{
"userAddress": "GABC...",
"allocations": { "XLM": 40, "BTC": 30, "USDC": 30 },
"threshold": 5,
"slippageTolerance": 1
}GET /api/pricesSuccessful data is { prices, feedMeta }: prices is a map of assets to quote fields (including optional source, servedFromCache, quoteAgeSeconds, dataTier), and feedMeta describes resolution path and whether the feed is degraded. The same feedMeta shape is included on GET /api/prices/enhanced and GET /api/portfolio/{id}/rebalance-plan as priceFeedMeta.
POST /api/portfolio/{id}/rebalance
Content-Type: application/json
{}or with options:
{
"options": {
"simulateOnly": false,
"ignoreSafetyChecks": false
}
}For more examples and exact request/response shapes, use Swagger UI at /api-docs or the OpenAPI spec at /api-docs/openapi.json.