Read the ledger of money movements

Read the journal of the money a project held, gave back and spent, entry by entry.

The ledger is the journal of the money movements of a project. Every hold, every release and every capture adds an entry, and an entry is never changed afterwards. The operation of this page reads the journal, and the console shows it on the Ledger tab of Operations.

What an entry says

type says what moved. amount is signed from the side of the budget: a positive amount is taken from the budget, and a negative one goes back to it.

typeWritten whenamount
reservation_holdA hold is takenThe amount held
reservation_releaseA hold is releasedThe amount given back, negative
usage_captureA call is capturedWhat the call cost
overrunA call cost more than its holdThe part of the cost above the hold

The contract names three more types: manual_adjustment, budget_credit and refund. No operation of the API writes them.

reservation_id names the hold an entry belongs to. metadata carries what the entry was written with: the model of a hold, the token counts of a capture.

Do not add the entries up to get the spend: a hold and its capture are both positive, and an overrun is a part of the usage_capture of the same call, not money on top of it. To read what a project spent, use the spend report on the Analytics page.

The window and the pages

The list is read inside a window of time, newest entry first.

  • Without from and to the window is the last seven days, today included.

  • from and to are sent together, and the window between them is at most seven days.

  • A value with an offset is the moment it names. A value without an offset is read as UTC.

  • One of the two without the other, a to earlier than from or a wider window answers 422 with the code invalid_date_range.

Inside the window the list is paged: send meta.next_cursor of one page as cursor of the next request. To read a longer period, move the window.

The list can also be read by the number of a page. Send page instead of cursor, and meta carries page, limit, total and anchor instead of next_cursor; the pages are counted inside the same window. Send the anchor of the first page back with every other page: only the entries at or below it are counted, so an entry written in the meantime does not move the rows from one page to the next. page together with cursor, and anchor without page, are answered with 422 — Search and numbered pages says how total is counted.

Operations

Append-only money movements

GET /v1/ledger-entries

  • Scope: read

Parameters

ParameterDescription
cursor optional

querystring

The meta.next_cursor of the previous page.

limit optional

queryinteger

Page size (default 30).

  • Minimum: 1
  • Maximum: 100
page optional

queryinteger

The page number, from 1. Turns the list into its numbered form: the answer's meta is a PageMeta (with total) instead of a CursorMeta. Together with cursor it is a 422; without page the answer is the cursor page, unchanged. A page past the last one is an empty data with the same total.

  • Minimum: 1
  • Maximum: 10000
anchor optional

querystring

The meta.anchor of the first numbered page: only rows with an id at or below it are counted and paged, so rows written after the first page do not shift pages 2..N. Without it the newest matching row is the anchor, and the answer names it. Only with page (422 otherwise).

  • Pattern: ^\d{1,20}$
from optional

querystring

Window start, ISO-8601 — a value with an offset is the moment it names, one without is read as UTC. The rule of the three lists that take this parameter (/v1/operations, /v1/authorizations, /v1/ledger-entries): from and to are given together or not at all, to must not precede from, and the window must not exceed 7 days. Naming neither end selects the last 7 days rather than the whole table — an unbounded range is the one query shape that cannot use the (project_id, created_at, id) index. Anything else answers 422 invalid_date_range. The analytics read their window by another rule (WindowFrom).

  • Format: date-time
to optional

querystring

Window end, ISO-8601, inclusive. See from for the window rule of the three lists: both ends together, to >= from, at most 7 days, otherwise 422 invalid_date_range.

  • Format: date-time

Responses

200

One page of ledger entries — a cursor page, or a numbered one with page

application/json
{
  "data": [
    {
      "entry_id": "74438200973594624",
      "reservation_id": "74438200973594624",
      "type": "reservation_hold",
      "amount": "0.02700000",
      "currency": "USD",
      "metadata": {},
      "created_at": "2026-10-02T09:30:00Z"
    }
  ],
  "meta": {
    "limit": 1,
    "next_cursor": "string"
  }
}

Errors

StatusCodesWhen
401

No credential, or one that is not valid

403

Authenticated, but not allowed: scope (insufficient_scope), policy or tenant (not_a_member); a blocked end customer (customer_blocked); an act only a signed-in person may take (session_required); the role rule on members and invitations (member_forbidden, invite_forbidden); a write to the organization's own rows, or a read of the audit journal, by a caller that does not reach every project (project_access_restricted)

422

Validation failure or a semantically impossible request. Field-level validation from the framework answers {"message", "errors"}; domain refusals answer the standard error envelope. An Idempotency-Key longer than 190 characters is invalid_request in that envelope.

Headers

  • X-Request-Id

The body of every error is the error envelope.

Examples

Read the latest entries

The answer holds the two entries of one call, newest first: its capture, then its hold. meta.next_cursor is the cursor of the next page.

Terminal
curl "https://api.slen.ai/api/v1/ledger-entries?limit=2" \
  -H "Authorization: Bearer $SLEN_API_KEY"
200 OK
{
  "data": [
    {
      "entry_id": "74442131708452864",
      "reservation_id": "74442097331195904",
      "type": "usage_capture",
      "amount": "0.00014100",
      "currency": "USD",
      "metadata": {
        "input_tokens": 420,
        "output_tokens": 130,
        "is_partial": false,
        "estimated_cost": "0.00018300",
        "reserved_amount": "0.00021960"
      },
      "created_at": "2026-10-02T08:40:15+00:00"
    },
    {
      "entry_id": "74442097335390208",
      "reservation_id": "74442097331195904",
      "type": "reservation_hold",
      "amount": "0.00021960",
      "currency": "USD",
      "metadata": {
        "model_code": "gpt-4o-mini",
        "estimation_strategy": "maximum",
        "safety_multiplier": "1.200"
      },
      "created_at": "2026-10-02T08:40:12+00:00"
    }
  ],
  "meta": {
    "limit": 2,
    "next_cursor": "74442097335390208"
  }
}

Where to go next

Built from openapi b070e1f, sdk contract 5cc7055, php ae4a53b, node 84bc62d