Analytics
Close analytics: period flux and unusual entries.
| Operation | Summary | Scope | Idempotency-Key |
|---|---|---|---|
GET /v1/entities/{entity}/analytics/breakdowns |
Top customers, vendors, currencies and expenses | read |
|
GET /v1/entities/{entity}/analytics/flux |
Flux analysis of a period | read |
|
GET /v1/entities/{entity}/analytics/insights |
What moved, in words | read |
|
GET /v1/entities/{entity}/analytics/kpis |
Key figures with comparison | read |
|
GET /v1/entities/{entity}/analytics/series |
A metric over time | read |
|
GET /v1/entities/{entity}/analytics/unusual-entries |
Unusual entries of a period | read |
GET /v1/entities/{entity}/analytics/breakdowns
Top customers, vendors, currencies and expenses
What a range is made of. customers: invoiced revenue per customer; vendors: billed spend per vendor; currency: invoiced revenue per document currency. All three are posted documents by document date, invoices less credit notes, at their net total in the functional currency (the document list filtered by the same party or currency, dates and posted status has the same documents). expense_mix: the expense accounts of the ledger by report line. The largest limit rows, the rest as one row other.
| Required scope | read (admin implies every scope) |
| Operation id | breakdowns_v1_entities__entity__analytics_breakdowns_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
| query | kind |
"currency" | "customers" | "expense_mix" | "vendors" |
yes | What to break down. |
| query | start |
string (date) | yes | First day of the range, inclusive (ISO 8601). |
| query | end |
string (date) | yes | Last day of the range, inclusive (ISO 8601). |
| query | limit |
integer | Most rows before the rest is grouped. (min 1; max 50; default 10) |
Example request
curl "$ERP_URL/v1/entities/CH01/analytics/breakdowns?kind=value&start=2026-01-01&end=2026-09-30" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | Breakdown |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"entity": "string",
"kind": "currency",
"currency": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"total": "string",
"rows": [
{
"key": "C-DE",
"label": "string",
"amount": "string",
"share_pct": 1.5,
"documents": 1
}
]
}
GET /v1/entities/{entity}/analytics/flux
Flux analysis of a period
Flux analysis: each account's P&L movement or balance-sheet balance against the previous regular period. A row is flagged when the variance reaches the materiality and 10% of the comparison; flagged rows come first. Amounts are in the entity's functional currency. Profit-and-loss accounts compare the period's movement, balance-sheet accounts the balance at the period's end. Without an earlier period the comparison is empty. An unknown year or period answers 404.
| Required scope | read (admin implies every scope) |
| Operation id | flux_v1_entities__entity__analytics_flux_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
| query | fiscal_year |
integer | yes | The fiscal year, e.g. 2026. (min 2000; max 2100) |
| query | period |
integer | yes | Period number within the year, from 1; adjustment periods follow the regular ones. (min 1) |
| query | materiality |
decimal string | Threshold in the entity's functional currency (decimal, e.g. 1000.00) above which a difference or amount is flagged; default 1000. (min 0; default "1000") |
Example request
curl "$ERP_URL/v1/entities/CH01/analytics/flux?fiscal_year=2026&period=9" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | Flux |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"period": "string",
"comparison": "string",
"materiality": "string",
"rows": [
{
"account": "string",
"name": "string",
"type": "string",
"current": "string",
"comparison": "string",
"variance": "string",
"variance_pct": 1.5,
"flagged": true
}
]
}
GET /v1/entities/{entity}/analytics/insights
What moved, in words
The accounts whose movement (profit and loss) or balance (balance sheet) differs from the comparison range by at least the materiality and 10%, largest first, each with one sentence made from its numbers by a fixed template; and the entries of the range that carry a risk flag. The same rules as /flux and /unusual-entries, for any range.
| Required scope | read (admin implies every scope) |
| Operation id | insights_v1_entities__entity__analytics_insights_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
| query | start |
string (date) | yes | First day of the range, inclusive (ISO 8601). |
| query | end |
string (date) | yes | Last day of the range, inclusive (ISO 8601). |
| query | compare |
"previous" | "year_ago" |
The range figures are compared with: previous is the same number of whole months (or days, if the range is not whole months) right before the range, year_ago the same dates a year earlier. (default "previous") |
|
| query | materiality |
decimal string | Threshold in the entity's functional currency (decimal, e.g. 1000.00) above which a difference or amount is flagged; default 1000. (min 0; default "1000") |
|
| query | limit |
integer | Most flagged accounts and entries listed. (min 1; max 100; default 10) |
Example request
curl "$ERP_URL/v1/entities/CH01/analytics/insights?start=2026-01-01&end=2026-09-30" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | Insights |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"entity": "string",
"currency": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"comparison": {
"mode": "previous",
"start": "2026-09-21",
"end": "2026-09-21"
},
"materiality": "string",
"flagged_total": 1,
"flux": [
{
"account": "string",
"name": "string",
"type": "string",
"current": "string",
"comparison": "string",
"variance": "string",
"variance_pct": 1.5,
"change": "cleared",
"summary": "string"
}
],
"unusual_total": 1,
"unusual": [
{
"id": "string",
"number": "string",
"date": "2026-09-21",
"description": "string",
"source": "string",
"amount": "string",
"created_by": "string",
"created_by_type": "string",
"reverses": "string",
"flags": [
"string"
]
}
]
}
GET /v1/entities/{entity}/analytics/kpis
Key figures with comparison
Ten figures for a range, each against the same figure for a comparison range, each with a small trend: revenue, gross margin %, operating expenses, net income, cash, receivables, payables, DSO, DPO and working capital. Computed on the server from the posted ledger by report line (the definitions are the metric list of GET .../analytics/series); a flow is the range's total, a stock the balance on its last day. delta is the difference, favourable says whether it is good news.
| Required scope | read (admin implies every scope) |
| Operation id | kpis_v1_entities__entity__analytics_kpis_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
| query | start |
string (date) | yes | First day of the range, inclusive (ISO 8601). |
| query | end |
string (date) | yes | Last day of the range, inclusive (ISO 8601). |
| query | compare |
"previous" | "year_ago" |
The range figures are compared with: previous is the same number of whole months (or days, if the range is not whole months) right before the range, year_ago the same dates a year earlier. (default "previous") |
Example request
curl "$ERP_URL/v1/entities/CH01/analytics/kpis?start=2026-01-01&end=2026-09-30" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | Kpis |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"entity": "string",
"currency": "string",
"start": "2026-09-21",
"end": "2026-09-21",
"comparison": {
"mode": "previous",
"start": "2026-09-21",
"end": "2026-09-21"
},
"granularity": "month",
"periods": [
{
"key": "2026-09",
"start": "2026-09-21",
"end": "2026-09-21"
}
],
"kpis": [
{
"key": "cash",
"unit": "amount",
"value": "string",
"comparison": "string",
"delta": "string",
"delta_pct": 1.5,
"direction": "down",
"favourable": true,
"spark": [
null
]
}
]
}
GET /v1/entities/{entity}/analytics/series
A metric over time
One metric per calendar month, quarter or year over a range, from the posted ledger in the entity's functional currency. A flow (revenue, expenses, ...) is what moved in the period; a stock (cash, receivables, ...) and an aging are as of the period's end; a ratio is computed from the period's own figures. The first and last period are clamped to the range, so the sum of a flow over the periods is the report total for the range (revenue over a range is revenue of the profit-and-loss report for it), and a stock at the last period is the trial balance as of end. by splits a flow or stock into one line per report line or account; its totals are the sums. One aggregate query reads the range (an aging reads the open documents once). More than 60 periods answers 422: ask for a coarser granularity.
The metrics (metric):
revenue(amount): Credit less debit of the accounts of type revenue: the P&L's total revenue.expenses(amount): Debit less credit of the accounts of type expense: the P&L's total expenses.net_income(amount): Revenue less expenses: the P&L's net income.cost_of_sales(amount): Debit less credit of the report linepl.cost_of_sales.operating_expenses(amount): Debit less credit of the report linespl.opex.*andpl.depreciation.gross_margin_pct(percent): (Revenue onpl.revenueless cost of sales) / revenue onpl.revenuex 100; empty when there is no such revenue.cash(amount): Balance at the period's end of the report lines startingbs.assets.current.cash(the Cash figure of the Role Center).receivables(amount): Balance at the period's end of the report linebs.assets.current.receivables(the receivables control account in the ledger).payables(amount): Balance at the period's end of the report linebs.liabilities.current.payables(the payables control account in the ledger).unearned_revenue(amount): Balance at the period's end of the report linebs.liabilities.current.contract_liabilities: what customers paid or were billed for goods and services not yet delivered (contract liabilities, deferred revenue).working_capital(amount): Report linesbs.assets.current.*lessbs.liabilities.current.*at the period's end.dso(days): Open receivables without VAT at the period's end (from the documents: the carrying amount of each open customer invoice and credit note times its net total over its total) / revenue of the period x days in the period.dpo(days): Open payables without VAT at the period's end (from the vendor bills, as the receivables for DSO) / purchases of the period x days in the period; purchases are cost of sales plus the operating expenses that are bought on account (pl.opex.*exceptpl.opex.personnel). Capital purchases on account are not in purchases.receivables_aging(amount): Open customer invoices less credit notes as of the period's end, in the functional currency at the booking rate, in the buckets of the aging report (current, 1-30, 31-60, 61-90, 90+ days past due). Equalstotals_functionalofGET /v1/entities/{entity}/reports/agingas of that day.payables_aging(amount): Open vendor bills less credit notes as of the period's end, as the receivables aging.
| Required scope | read (admin implies every scope) |
| Operation id | series_v1_entities__entity__analytics_series_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
| query | metric |
"cash" | "cost_of_sales" | "dpo" | "dso" | "expenses" | "gross_margin_pct" | "net_income" | "operating_expenses" | "payables" | "payables_aging" | "receivables" | "receivables_aging" | "revenue" | "unearned_revenue" | "working_capital" |
yes | Which metric; see the list in the description. |
| query | start |
string (date) | yes | First day of the range, inclusive (ISO 8601). |
| query | end |
string (date) | yes | Last day of the range, inclusive (ISO 8601). |
| query | granularity |
"month" | "quarter" | "year" |
The size of the periods: month, quarter or year (calendar). (default "month") |
|
| query | by |
"account" | "none" | "report_line" |
none: one line; report_line or account: one line each, for the metrics that can be split (revenue, expenses, cost_of_sales, operating_expenses, cash, receivables, payables). Others answer 422. (default "none") |
Example request
curl "$ERP_URL/v1/entities/CH01/analytics/series?metric=value&start=2026-01-01&end=2026-09-30" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | Series |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
{
"entity": "string",
"metric": "cash",
"unit": "amount",
"currency": "string",
"granularity": "month",
"start": "2026-09-21",
"end": "2026-09-21",
"by": "account",
"periods": [
{
"key": "2026-09",
"start": "2026-09-21",
"end": "2026-09-21"
}
],
"lines": [
{
"key": "pl.opex.other",
"label": "string",
"values": [
null
]
}
],
"totals": [
"string"
]
}
GET /v1/entities/{entity}/analytics/unusual-entries
Unusual entries of a period
Posted entries of the period with risk flags: manual, created by an agent, round or
large amount (10x materiality), weekend date, reversal. Each entry lists its flags
and its total debits in the entity's functional currency. An unknown year or period answers
404.
| Required scope | read (admin implies every scope) |
| Operation id | unusual_entries_v1_entities__entity__analytics_unusual_entries_get |
Parameters
| In | Name | Type | Required | Description |
|---|---|---|---|---|
| path | entity |
string | yes | Entity code, e.g. CH01 (GET /v1/entities). |
| query | fiscal_year |
integer | yes | The fiscal year, e.g. 2026. (min 2000; max 2100) |
| query | period |
integer | yes | Period number within the year, from 1; adjustment periods follow the regular ones. (min 1) |
| query | materiality |
decimal string | Threshold in the entity's functional currency (decimal, e.g. 1000.00) above which a difference or amount is flagged; default 1000. (min 0; default "1000") |
Example request
curl "$ERP_URL/v1/entities/CH01/analytics/unusual-entries?fiscal_year=2026&period=9" \
-H "Authorization: Bearer $ERP_KEY"
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 | OK | array of UnusualEntry |
| 401 | Missing or invalid API key (unauthorized). |
ErrorEnvelope |
| 403 | The caller lacks the required scope (missing_right, with scope), the key belongs to no person where a person is needed (forbidden), or the approval policy denies the action (policy_denied, with action and rule_id). |
ErrorEnvelope |
| 404 | The resource does not exist, or belongs to another organization (not_found). |
ErrorEnvelope |
| 422 | Validation failed: the error envelope (validation_failed). For a malformed body or parameter its message is in sentences ("Line 2: the quantity must be above zero") and fields lists each error with pydantic's loc, type and msg beside its sentence (message) and its parts, to word it in another language: items (name and number from 1: line 2), field (its name as sent) and the rule it breaks with its limit, count and choices (none for a validator's own sentence); FastAPI's own detail list stays beside the envelope. |
HTTPValidationError |
Example response (shape)
[
{
"id": "string",
"number": "string",
"date": "2026-09-21",
"description": "string",
"source": "string",
"amount": "string",
"created_by": "string",
"created_by_type": "string",
"reverses": "string",
"flags": [
"string"
]
}
]
Schemas
Breakdown
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Code of the entity. |
kind |
"currency" | "customers" | "expense_mix" | "vendors" |
yes | What is broken down. |
currency |
string | yes | The entity's functional currency; amounts are in it. |
start |
string (date) | yes | First day of the range (ISO 8601). |
end |
string (date) | yes | Last day of the range (ISO 8601). |
total |
string | yes | The sum over all rows, including other. |
rows |
array of BreakdownRow |
yes | The largest first, at most limit; what is left is one row other. |
BreakdownRow
| Field | Type | Required | Description |
|---|---|---|---|
key |
string | yes | The customer or vendor code, the currency, the report line, or other. |
label |
string | yes | The customer or vendor name, the currency, or the report line. |
amount |
string | yes | The amount in the functional currency. |
share_pct |
number or null | yes | Its share of total, one decimal; null when the total is zero. |
documents |
integer or null | yes | How many posted documents make it up (null for the expense mix, which is read from the ledger); the filtered document list shows the same count. |
ComparisonRange
| Field | Type | Required | Description |
|---|---|---|---|
mode |
"previous" | "year_ago" |
yes | previous or year_ago, as requested. |
start |
string (date) | yes | First day of the comparison range (ISO 8601). |
end |
string (date) | yes | Last day of the comparison range (ISO 8601). |
Flux
| Field | Type | Required | Description |
|---|---|---|---|
period |
string | yes | The period analysed, <year>-<number>, e.g. 2026-09. |
comparison |
string | yes | The previous regular period it is compared with, or none (first period). |
materiality |
string | yes | The threshold used, functional currency. |
rows |
array of FluxRow |
yes | One row per account, flagged rows first, then by size of the variance. |
FluxInsight
| Field | Type | Required | Description |
|---|---|---|---|
account |
string | yes | Account code. |
name |
string | yes | Account name. |
type |
string | yes | asset, liability, equity, revenue or expense. |
current |
string | yes | Profit-and-loss accounts: the range's movement; others: the balance at the range's end. Functional currency, the account's normal direction. |
comparison |
string | yes | The same measure for the comparison range. |
variance |
string | yes | current less comparison. |
variance_pct |
number or null | yes | The variance as a percentage of the comparison, one decimal; null when the comparison is zero. |
change |
"cleared" | "decrease" | "increase" | "new" |
yes | new: nothing in the comparison; cleared: nothing now. |
summary |
string | yes | One plain sentence in English built from the numbers by a fixed template (no language model). |
FluxRow
| Field | Type | Required | Description |
|---|---|---|---|
account |
string | yes | Account code. |
name |
string | yes | Account name. |
type |
string | yes | asset, liability, equity, revenue or expense. |
current |
string | yes | Profit-and-loss accounts: the period's movement; others: the balance at the period's end. Functional currency, account's normal direction. |
comparison |
string | yes | The same measure for the comparison period. |
variance |
string | yes | current minus comparison. |
variance_pct |
number or null | yes | The variance as a percentage of the comparison, one decimal; null when the comparison is zero. |
flagged |
boolean | yes | True when the variance reaches the materiality and, if there is a comparison, 10% of it. |
Insights
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Code of the entity. |
currency |
string | yes | The entity's functional currency; amounts are in it. |
start |
string (date) | yes | First day of the range (ISO 8601). |
end |
string (date) | yes | Last day of the range (ISO 8601). |
comparison |
ComparisonRange |
yes | The range the figures are compared with. |
materiality |
string | yes | The threshold used, functional currency. |
flagged_total |
integer | yes | How many accounts are flagged; flux holds the largest limit. |
flux |
array of FluxInsight |
yes | The flagged accounts, the largest variance first, each with a sentence. |
unusual_total |
integer | yes | How many entries of the range carry a risk flag; unusual holds the largest limit. |
unusual |
array of UnusualEntry |
yes | Entries with risk flags (manual, agent, round, large, weekend, reversal), largest amount first. |
Kpi
| Field | Type | Required | Description |
|---|---|---|---|
key |
"cash" | "cost_of_sales" | "dpo" | "dso" | "expenses" | "gross_margin_pct" | "net_income" | "operating_expenses" | "payables" | "payables_aging" | "receivables" | "receivables_aging" | "revenue" | "unearned_revenue" | "working_capital" |
yes | The metric (GET .../analytics/series?metric= takes it). |
unit |
"amount" | "days" | "percent" |
yes | amount, percent or days. |
value |
string or null | yes | A flow: the total of the range; a stock: the balance on its last day; a ratio: computed over the range. Null where a ratio has no denominator. |
comparison |
string or null | yes | The same figure for the comparison range. |
delta |
string or null | yes | value less comparison (percentage points for a percent, days for days); null if either is missing. |
delta_pct |
number or null | yes | delta as a percentage of the absolute comparison, one decimal; null for percent and days, and when the comparison is zero. |
direction |
"down" | "flat" | "up" or null |
yes | The sign of delta; null if there is none. |
favourable |
boolean or null | yes | Whether the change is good news (revenue up, expenses down); null for a flat change and for metrics where neither direction is better. |
spark |
array of string or null | yes | The metric per period over the range, in the order of periods. |
Kpis
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Code of the entity. |
currency |
string | yes | The entity's functional currency; amounts are in it. |
start |
string (date) | yes | First day of the range (ISO 8601). |
end |
string (date) | yes | Last day of the range (ISO 8601). |
comparison |
ComparisonRange |
yes | The range the figures are compared with. |
granularity |
"month" | "quarter" | "year" |
yes | The size of the periods of spark. |
periods |
array of SeriesPeriod |
yes | The periods of every spark. |
kpis |
array of Kpi |
yes | Revenue, gross margin, operating expenses, net income, cash, receivables, payables, DSO, DPO and working capital, in that order. |
Series
| Field | Type | Required | Description |
|---|---|---|---|
entity |
string | yes | Code of the entity. |
metric |
"cash" | "cost_of_sales" | "dpo" | "dso" | "expenses" | "gross_margin_pct" | "net_income" | "operating_expenses" | "payables" | "payables_aging" | "receivables" | "receivables_aging" | "revenue" | "unearned_revenue" | "working_capital" |
yes | The metric, as requested. |
unit |
"amount" | "days" | "percent" |
yes | amount (the functional currency), percent or days; ratios have one decimal. |
currency |
string | yes | The entity's functional currency; amounts are in it. |
granularity |
"month" | "quarter" | "year" |
yes | The size of the periods. |
start |
string (date) | yes | First day of the range (ISO 8601). |
end |
string (date) | yes | Last day of the range (ISO 8601). |
by |
"account" | "none" | "report_line" |
yes | How the metric is split into lines. |
periods |
array of SeriesPeriod |
yes | The calendar months, quarters or years of the range, the first and last clamped to it. |
lines |
array of SeriesLine |
yes | One line, or one per report line, account or aging bucket. |
totals |
array of string or null | yes | Per period, the sum of the lines (the line itself for a ratio). For a flow, the sum over all periods is the report total for the range. |
SeriesLine
| Field | Type | Required | Description |
|---|---|---|---|
key |
string | yes | Which line: the metric's own key when not split, else the report line (pl.opex.other), the account code (6500) or, for an aging, the bucket (current, 1-30, 31-60, 61-90, 90+). |
label |
string | yes | A readable name: the metric's English title, the report line, or the account code and name. |
values |
array of string or null | yes | One value per period, in the order of periods; null where a ratio has no denominator (no revenue in the period). |
SeriesPeriod
| Field | Type | Required | Description |
|---|---|---|---|
key |
string | yes | 2026-09 for a month, 2026-Q3 for a quarter, 2026 for a year. |
start |
string (date) | yes | First day the period covers inside the requested range (ISO 8601). |
end |
string (date) | yes | Last day the period covers inside the requested range (ISO 8601). A flow is the movement from start to end; a stock or an aging is as of end. |
UnusualEntry
| Field | Type | Required | Description |
|---|---|---|---|
id |
string or null | Id (uuid) of the entry, as text. | |
number |
string or null | yes | The entry number. |
date |
string (date) | yes | The posting date (ISO 8601). |
description |
string | yes | What the entry is for. |
source |
string | yes | manual or the subledger that created it. |
amount |
string | yes | Total debits in functional currency. |
created_by |
string or null | yes | Name of the actor that created the entry. |
created_by_type |
string or null | yes | human, agent or system. |
reverses |
string or null | yes | Id of the entry this one reverses. |
flags |
array of string | yes | manual entry, created by an agent, round amount, large amount, weekend date, reversal. |