View as Markdown

Calculation runs

A calculation run recalculates holdings and returns for one scope from a day you choose to yesterday. Aleta recalculates by itself whenever data changes; a run is for when you want a scope recalculated now. The run writes staged figures, not the published ones you read; releasing staged figures is described under publish runs. Reading requires the platform:read scope and the operations module at read; starting a run requires platform:write, operations at read_write, and a grant on every cash and custody account the run recalculates. An admin sees every run; another user sees the runs they started.

Only a tenant that has moved to the platform recalculates here. Before the move, starting a run answers 409 legacy_managed.

The calculation run object

idstring

Stable identifier for the run. A UUID — treat it as opaque.

statusstring

pending (stored, not started yet), running, succeeded or failed. A run that has not started within an hour is failed with not_started. A run with any failed calculation is failed, also when the rest succeeded.

scopeobject

kind (cash_account, custody_account, asset, entity or client) and id.

custody_account_idstring or null

For an asset: the custody account it is recalculated in. null means every custody account holding it.

from_datestring

The first day recalculated, YYYY-MM-DD.

to_datestring

The last day: the day before the run was asked for (UTC).

requested_by_user_idstring or null

Who asked for the run.

created_atstring

When the run was asked for, ISO 8601 in UTC with microseconds.

finished_atstring or null

When the run ended: its last calculation, or when it was refused or gave up waiting. null while it is pending or running.

progressobject

total (the calculations the run is made of; 0 until it started, and when the scope had nothing to recalculate), succeeded and failed (calculations that failed or that the platform cannot run yet).

failed_countinteger

Every failure, also those past the 10 in failures.

failuresarray

At most 10, each { "message": … }: a calculation's error (newest first), a calculation the platform cannot run yet (the platform cannot run this calculation yet: …), or why the run did not start, such as empty_scope, not_found or not_started. A message longer than 500 characters is cut there and ends in ....

A run is kept for 30 days; an older one is no longer listed and is a 404.


POST/v1/calculation-runs

Start a run

Stores the run as pending and answers 202 Accepted with it and Location: /v1/calculation-runs/{id} (also on a replay, not on a dry run); the platform starts it within minutes. Takes ?dry_run=true and Idempotency-Key like every v1 write.

Body

scopeobject

Required. kind (cash_account, custody_account, asset, entity or client) and id (a UUID). Any other kind is a 400.

custody_account_idstring or null

Optional, and only with an asset: recalculate it in this custody account only. Sent with another kind, it is a 400.

from_datestring

Required. The first day, YYYY-MM-DD, on or before yesterday.

A field the body does not take, in the body or in scope, is a 400.

Refusals, in this order: 400 bad_request for the body (also a from_date that is not YYYY-MM-DD or is after yesterday), 404 not_found for a scope or custody_account_id you do not see, 403 forbidden for a scope with a cash or custody account your grants do not cover (for an asset: the custody account you name, or else every cash and custody account it is booked on), 409 legacy_managed before your tenant has moved. The 403 and 409 come also on a dry run and before a replay.

Request
POST/v1/calculation-runs
curl -X POST https://api.aleta.ai/v1/calculation-runs \
  -H "Authorization: Bearer {access_token}" \
  -H "Idempotency-Key: 6c1f7d0e" \
  -H "Content-Type: application/json" \
  -d '{"scope": {"kind": "cash_account", "id": "6b1d2f0a-3c4e-4f5a-8b7c-9d0e1f2a3b4c"}, "from_date": "2026-10-01"}'
Response
{
  "id": "0190f5f2-1b2c-7d3e-8f40-5a6b7c8d9e10",
  "status": "pending",
  "scope": { "kind": "cash_account", "id": "6b1d2f0a-3c4e-4f5a-8b7c-9d0e1f2a3b4c" },
  "custody_account_id": null,
  "from_date": "2026-10-01",
  "to_date": "2026-10-17",
  "requested_by_user_id": "0190f5e0-3333-7aaa-8bbb-0123456789ab",
  "created_at": "2026-10-18T09:00:00.000000Z",
  "finished_at": null,
  "progress": { "total": 0, "succeeded": 0, "failed": 0 },
  "failed_count": 0,
  "failures": []
}

GET/v1/calculation-runs

List runs

Returns runs newest first (created_at, then id) in the standard v1 list envelope: data, next_cursor (null on the last page) and limit. Runs older than 30 days are not listed.

Query parameters

limitinteger

Page size. Defaults to 100, capped at 1 000.

cursorstring

The next_cursor of the previous page. A cursor that cannot be read is a 400.

An unknown query parameter is a 400.

Request
GET/v1/calculation-runs
curl https://api.aleta.ai/v1/calculation-runs \
  -H "Authorization: Bearer {access_token}"
Response
{
  "data": [
    {
      "id": "0190f5f2-1b2c-7d3e-8f40-5a6b7c8d9e10",
      "status": "succeeded",
      "scope": { "kind": "cash_account", "id": "6b1d2f0a-3c4e-4f5a-8b7c-9d0e1f2a3b4c" },
      "custody_account_id": null,
      "from_date": "2026-10-01",
      "to_date": "2026-10-17",
      "requested_by_user_id": "0190f5e0-3333-7aaa-8bbb-0123456789ab",
      "created_at": "2026-10-18T09:00:00.000000Z",
      "finished_at": "2026-10-18T09:04:12.318204Z",
      "progress": { "total": 34, "succeeded": 34, "failed": 0 },
      "failed_count": 0,
      "failures": []
    }
  ],
  "next_cursor": null,
  "limit": 100
}

GET/v1/calculation-runs/{id}

Get a run

Returns one run. A run you do not see, older than 30 days, or an id that is not a UUID is a 404.

Request
GET/v1/calculation-runs/{id}
curl https://api.aleta.ai/v1/calculation-runs/0190f5f2-1b2c-7d3e-8f40-5a6b7c8d9e10 \
  -H "Authorization: Bearer {access_token}"