Skip to content
Build

API overview

Authentication, the main endpoints, request and response shapes, and error handling.

Authentication

The API speaks JSON over HTTP. With server.secure: true, which the example config sets, every request except /health needs an X-LedgerForge-Key header. The master secret (server.secret_key) has deployment-wide administrator power: use it to provision scoped keys, and give applications and agents those instead. /metrics uses a separate bearer token. Keep the API behind TLS.

Request
$ curl -s -X POST http://127.0.0.1:5001/transactions \    -H "X-LedgerForge-Key: $LEDGERFORGE_KEY" \    -H 'Content-Type: application/json' \    -d @transfer.json

Main endpoints

EndpointPurpose
POST /ledgers, GET /ledgers/:idCreate and read ledgers.
POST /balances, GET /balances/:idCreate and read balances, including counters and inflight components.
GET /balances/:id/at, POST /balances-snapshotsRead a balance at a point in time and take snapshots.
POST /transactionsRecord a transfer, split transfer, inflight hold or preview.
POST /transactions/bulkSubmit a batch with explicit atomic or asynchronous behaviour.
GET /transactions/:id, GET /transactions/reference/:referenceRead a transaction by ID or by its business reference.
PUT /transactions/inflight/:txIDCommit (fully or partly) or void an inflight hold.
POST /refund-transaction/:idCreate a linked reversal of an APPLIED transaction.
POST /api-keys, GET /api-keys, DELETE /api-keys/:idCreate, list and revoke scoped API keys.
PUT /policies/:api_key_id, GET /policies/effectiveSet a key's spending policy and inspect effective rules and capacity.
GET /approvals, POST /approvals/:id/approve, POST /approvals/:id/rejectReview policy holds with an independent key.
POST /hooks, GET /hooksRegister and list transaction hooks.
POST /identities, POST /reconciliation/uploadIdentities, and reconciliation against external records.
GET /healthPublic health check.

The route list lives in api/api.go, which also covers filters, identity tokenisation, balance monitors, lineage, search and metadata updates.

Recording a transaction

POST /transactionsRequest
{  "source": "bln_e0f81b15-bd0a-43c5-9e84-7fd4394192e8",  "destination": "bln_df56a248-7d7e-4c0c-8fbf-a5215c87b603",  "currency": "USD",  "precise_amount": 1250,  "precision": 100,  "reference": "order-1042",  "description": "Order 1042",  "skip_queue": true,  "allow_overdraft": true,  "overdraft_limit": 12.5}
Response201 Created
{  "rate": 0,  "precise_amount": 1250,  "amount": 12.5,  "amount_string": "12.5",  "precision": 100,  "overdraft_limit": 12.5,  "transaction_id": "txn_c493a7e3-ac39-4456-90c3-ac43f00c5e0d",  "parent_transaction": "",  "source": "bln_e0f81b15-bd0a-43c5-9e84-7fd4394192e8",  "destination": "bln_df56a248-7d7e-4c0c-8fbf-a5215c87b603",  "reference": "order-1042",  "currency": "USD",  "description": "Order 1042",  "status": "APPLIED",  "hash": "9b7d7ad924e2e626055e3b33edcf194142e3fcd562de1807dd738d9afd7c1f87",  "allow_overdraft": true,  "inflight": false,  "skip_queue": true,  "atomic": false,  "created_at": "2026-10-10T05:54:45.691597464Z",  "effective_date": "2026-10-10T05:54:45.691597464Z",  "scheduled_for": "0001-01-01T00:00:00Z",  "inflight_expiry_date": "0001-01-01T00:00:00Z",  "inflight_commit_date": "0001-01-01T00:00:00Z",  "meta_data": {    "allow_overdraft": true  }}

Read status to learn the outcome. Without skip_queue the response is the accepted, QUEUED transaction; fetch it again later. A preview with dry_run: true returns 200 rather than 201 because nothing was created.

Numbers

Amounts and balance counters are JSON integers in units of the transaction's precision. JavaScript numbers can't represent every integer above 2^53 − 1, so use a JSON parser that preserves large integers and treat floating-point values as presentation only. Policy limits and MCP *_minor fields are integer strings.

Errors

Errors keep a compatible envelope with a stable code and a deliberate message. Internal failures return a generic message; SQL text, connection strings and driver errors are written to operator logs, not responses.

POST /transactions (same reference)409 Conflict
{  "error": "reference has already been used",  "error_detail": {    "code": "TXN_DUPLICATE_REFERENCE",    "message": "reference has already been used"  }}
Policy endpoints have their own error shape, for example {"error": "spending policy denied transaction"}. Don't assume every endpoint wraps a failure identically.