Ledger API v1

API Reference

Base URL: https://api.ledger.example.com/v1

The Ledger API is a JSON-over-HTTPS interface for reading and writing accounts, transactions, and invoices. All requests and responses use application/json unless noted otherwise. Timestamps are ISO 8601 in UTC. Monetary amounts are integers expressed in the smallest currency unit (for example, cents).

Test mode is available for every key prefixed with sk_test_. Test data never touches live balances.

Authentication

Authenticate every request with a secret key sent as a Bearer token in the Authorization header.

curl https://api.ledger.example.com/v1/accounts \
  -H "Authorization: Bearer sk_test_4eC39HqLyjWDarjtT1zdp7dc"

Keep secret keys on the server. Never embed them in browser or mobile code.

Errors

Errors return a non-2xx status code and a JSON body describing the problem.

{
  "error": {
    "type": "invalid_request",
    "code": "missing_field",
    "message": "The field 'currency' is required.",
    "param": "currency"
  }
}
StatusMeaning
400Malformed or invalid request body
401Missing or invalid API key
403Key lacks permission for this resource
404Resource not found
409Conflict, such as a duplicate idempotency key
429Rate limit exceeded; retry after the Retry-After interval
500Something went wrong on our side

Pagination

List endpoints are cursor-paginated. Pass limit (1 to 100, default 20) and the starting_after cursor from the previous page's last item.

{
  "data": [ ... ],
  "has_more": true,
  "next_cursor": "txn_01HZX8K3Q2"
}

Accounts

An account holds a balance in a single currency.

GET /accounts list

Returns a paginated list of accounts, newest first.

GET /accounts/{id} retrieve

Returns a single account by its identifier.

POST /accounts create
ParameterTypeDescription
namestringDisplay name for the account. Required.
currencystringISO 4217 code, such as USD. Required.
metadataobjectUp to 20 string key-value pairs.
{
  "id": "acc_01HZX7P4M9",
  "object": "account",
  "name": "Operating",
  "currency": "USD",
  "balance": 0,
  "created": "2024-05-14T09:21:07Z",
  "metadata": {}
}

Transactions

Transactions move funds between accounts. Amounts are positive integers; direction is set by type.

GET /transactions list

Supports filtering with account, type, and created_after query parameters.

POST /transactions create
ParameterTypeDescription
accountstringSource account ID. Required.
amountintegerAmount in the smallest currency unit. Required.
typestringcredit or debit. Required.
descriptionstringShown on statements. Optional.

Send an Idempotency-Key header on every create request so retries do not double-post.

Invoices

Invoices request payment from a customer. They move through draft, open, paid, and void.

GET /invoices/{id} retrieve
POST /invoices create
PATCH /invoices/{id} update

Only draft invoices can be edited. Send only the fields you want to change.

DELETE /invoices/{id} void

Voids an open invoice. The record is retained for auditing.

Webhooks

Register an HTTPS endpoint to receive events as they happen. Each delivery is signed in the Ledger-Signature header so you can verify it came from us.

import crypto from "node:crypto";

function verify(rawBody, header, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(rawBody, "utf8")
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(header)
  );
}
import hmac, hashlib

def verify(raw_body: bytes, header: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(), raw_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, header)