jaz-clio 5.73.3 → 5.74.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -5
- package/assets/skills/api/SKILL.md +6 -4
- package/assets/skills/api/references/endpoints.md +82 -1
- package/assets/skills/api/references/errors.md +24 -0
- package/assets/skills/api/references/field-map.md +12 -0
- package/assets/skills/api/references/full-api-surface.md +11 -2
- package/assets/skills/api/references/report-templates.md +305 -0
- package/assets/skills/api/references/search-enums.md +1 -1
- package/assets/skills/api/references/search-reference.md +1 -1
- package/assets/skills/cli/SKILL.md +5 -4
- package/assets/skills/cli/references/command-catalog.md +33 -15
- package/assets/skills/conversion/SKILL.md +1 -1
- package/assets/skills/jaz-kit/SKILL.md +1 -1
- package/assets/skills/jaz-pseudo-sql/SKILL.md +1 -1
- package/assets/skills/jobs/SKILL.md +1 -1
- package/assets/skills/jobs/references/bank-recon.md +4 -4
- package/assets/skills/transaction-recipes/SKILL.md +1 -1
- package/assets/templates/platform-rules/jaz-agent-rules.md +3 -3
- package/cli.mjs +559 -557
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
<p align="center">
|
|
4
4
|
<a href="https://www.npmjs.com/package/jaz-clio"><img src="https://img.shields.io/npm/v/jaz-clio?style=for-the-badge&logo=npm" alt="npm"></a>
|
|
5
5
|
<a href="https://www.npmjs.com/package/jaz-clio"><img src="https://img.shields.io/npm/dm/jaz-clio?style=for-the-badge&label=downloads" alt="npm downloads"></a>
|
|
6
|
-
<img src="https://img.shields.io/badge/tools-
|
|
6
|
+
<img src="https://img.shields.io/badge/tools-386-blue?style=for-the-badge" alt="386 tools">
|
|
7
7
|
<a href="https://github.com/teamtinvio/jaz-ai/blob/main/LICENSE"><img src="https://img.shields.io/github/license/teamtinvio/jaz-ai?style=for-the-badge&color=green" alt="License"></a>
|
|
8
8
|
</p>
|
|
9
9
|
|
|
10
10
|
<p align="center"><b>Jaz accounting on the command line, and inside any AI agent.</b></p>
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
386 tools · 76 command groups · 7 skills · 13 calculators · 12 close playbooks · 160 field-tested API rules.
|
|
13
13
|
|
|
14
14
|
```bash
|
|
15
15
|
npm install -g jaz-clio
|
|
@@ -64,7 +64,8 @@ clio invoices search --query 'status:unpaid AND $500+' # structured per-entity s
|
|
|
64
64
|
clio ledger-find-fix preview --level TRANSACTIONS --input recode.json # find & fix (recode) across record types
|
|
65
65
|
clio approvals approve <resourceId> --entity invoices # approve a document waiting on you
|
|
66
66
|
clio sql preview "SELECT invoice_number, balance FROM invoices WHERE balance > 0 LIMIT 10" # read-only pseudo-SQL
|
|
67
|
-
clio report-templates
|
|
67
|
+
clio report-templates update "Board P&L" --set colorTheme=calm # customize a report layout
|
|
68
|
+
clio exports download --type profit-and-loss --template "Board P&L" --format PDF --start-date 2026-01-01 --end-date 2026-06-30
|
|
68
69
|
clio modules list # which features are switched on
|
|
69
70
|
clio navigate reports.profit-and-loss # deep link into the Jaz dashboard (offline)
|
|
70
71
|
```
|
|
@@ -107,7 +108,7 @@ that ends up in a journal.
|
|
|
107
108
|
|
|
108
109
|
## MCP server
|
|
109
110
|
|
|
110
|
-
|
|
111
|
+
386 tools for any AI agent that speaks MCP. Runs locally: no cloud, no ports.
|
|
111
112
|
|
|
112
113
|
> **No install at all?** Claude.ai, ChatGPT, Cowork, and Microsoft Copilot Studio can use Jaz through the hosted connector. Add `https://mcp.jaz.ai/mcp` as a custom connector and sign in with OAuth, no key. The local setup below is for terminal use, scripting, and editors that run MCP servers as local processes.
|
|
113
114
|
|
|
@@ -140,7 +141,7 @@ OAuth can reach the organizations granted at sign-in; `--org` pins one, and with
|
|
|
140
141
|
|
|
141
142
|
## Skills
|
|
142
143
|
|
|
143
|
-
|
|
144
|
+
160 API rules from production testing: field-name maps, error-recovery patterns, response-shape quirks, plus 12 job playbooks. Installable into any agent project, no server involved.
|
|
144
145
|
|
|
145
146
|
```bash
|
|
146
147
|
clio init # auto-detect the agent, install skills + agent-rules
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: jaz-api
|
|
3
|
-
version: 5.
|
|
3
|
+
version: 5.74.0
|
|
4
4
|
description: >-
|
|
5
5
|
Use this skill whenever you call, debug, or review code that touches the Jaz
|
|
6
|
-
REST API. Covers field names, response shapes,
|
|
6
|
+
REST API. Covers field names, response shapes, 160 production gotchas, error
|
|
7
7
|
recovery (422/400/404/500), search filters, pagination, and edge cases for
|
|
8
8
|
every endpoint: invoices, bills, credit notes, journals, cash entries,
|
|
9
9
|
payments, contacts, CoA, items, tax profiles, bank records, fixed assets,
|
|
@@ -31,7 +31,7 @@ The rest of this skill (field names, gotchas, error catalog, dependency order, s
|
|
|
31
31
|
|
|
32
32
|
**Core fundamentals (read first, every integration):** Identifiers & Dates 1 to 3; Names & Fields 9 to 13; Transaction Creation 14 to 16; Chart of Accounts 17 to 22; Payments / Cross-Currency 4 to 8; Journals & Cash 23 to 26; Credit Notes & Refunds 27 to 28; Reports 36 to 37; Tax Profile Scoping 100; Transaction References 104; Draft Finalization Pipeline 81 to 88; Jaz Magic / PDF-JPG 57 to 63; Currency Rates 39, 49, 105; Withholding Tax 45, 98.
|
|
33
33
|
|
|
34
|
-
**Integration depth (API clients, pipelines, batch jobs, MCP/CLI):** Bulk Upsert (Items/Contacts/Rates); Background Jobs (filter `resourceId` not `jobId`); Export Records; Pagination (38); Search & Filter (50 to 56); Response Shape Gotchas (66 to 73); Cash Entry Response Shape (74 to 77); Entity Resolution (78 to 80); Bank Rules (89 to 90d); Fixed Assets (91 to 92c); Subscriptions & Scheduled (93 to 94); niche endpoints (95 to 102); Journals balance (103); Quick Fix (107, 111); TTB (108); Dynamic Strings (109 to 110); Sub-Resource Shapes (112); Nano-Classifier (113); Scheduler Asymmetry (114); Payment Record CRUD (115 to 117); Bulk Upserts transactions (118 to 122); Reconciliation write-side (123 to 127); Drafts lifecycle (128 to 135); Orders: Sale Quotes / Sale Orders / Purchase Requests / Purchase Orders (`references/orders.md`); Claims: records / lifecycle / bulk + conversion + payouts + Employees + Claim Settings (`references/claims.md`).
|
|
34
|
+
**Integration depth (API clients, pipelines, batch jobs, MCP/CLI):** Bulk Upsert (Items/Contacts/Rates); Background Jobs (filter `resourceId` not `jobId`); Export Records; Pagination (38); Search & Filter (50 to 56); Response Shape Gotchas (66 to 73); Cash Entry Response Shape (74 to 77); Entity Resolution (78 to 80); Bank Rules (89 to 90d); Fixed Assets (91 to 92c); Subscriptions & Scheduled (93 to 94); niche endpoints (95 to 102); Journals balance (103); Quick Fix (107, 111); TTB (108); Dynamic Strings (109 to 110); Sub-Resource Shapes (112); Nano-Classifier (113); Scheduler Asymmetry (114); Payment Record CRUD (115 to 117); Bulk Upserts transactions (118 to 122); Reconciliation write-side (123 to 127); Drafts lifecycle (128 to 135); Orders: Sale Quotes / Sale Orders / Purchase Requests / Purchase Orders (`references/orders.md`); Claims: records / lifecycle / bulk + conversion + payouts + Employees + Claim Settings (`references/claims.md`); Report templates and packs (161, `references/report-templates.md`).
|
|
35
35
|
|
|
36
36
|
## When to Use This Skill
|
|
37
37
|
|
|
@@ -548,7 +548,7 @@ When the user wants to OPEN, SEE, or SHARE something in the Jaz dashboard ("open
|
|
|
548
548
|
- **`reconcile_magic_match`**: bulk-accept MAGIC_MATCH suggestions (max 500 entries). Returns `{reconciled[], failed[]}`; a 200 with non-empty `failed[]` is a PARTIAL success (per-entry `errorCode`); loop on failed only. Entry-level idempotency-keyed server-side (re-submit returns done entries in `reconciled[]`).
|
|
549
549
|
- **`reconcile_learned_prediction`**: accept an ML prediction. `predictedPayload` + `predictedPayloadSchemaVersion` come VERBATIM from a `view_auto_reconciliation` (MAGIC_RECONCILE_WITH_CASH_IN_OUT) suggestion; never hand-construct. `retryToken` forces a fresh journal on edit-retry; omit for idempotent replay. On failure (stale payload), fall back to `reconcile_with_payments`; don't retry the blob.
|
|
550
550
|
|
|
551
|
-
159. **`view_auto_reconciliation` returns execution-ready `suggestions[]`, the suggestion→commit seam.** **`bankStatementEntryResourceIds` is REQUIRED: the endpoint is per-entry and has no account-wide mode; source ids from `search_bank_records` (status `UNRECONCILED`).** Each suggestion carries `recommendedTool` (the commit tool to call), `execute` (ready-to-pass args; merge `bankAccountResourceId` for `reconcile_magic_match`), `confidenceTier` (high/medium/low, code-derived), and `autoCommitEligible` (true ⇒ high confidence + executable plan + under any `autoCommitMaxAmount` cap). **Decision gate:** `autoCommitEligible===true` → auto-commit via `recommendedTool`+`execute`; everything else → surface for confirmation. **Amount threshold is a HARD VETO over confidence** (pass `autoCommitMaxAmount`). Field mapping under the hood: `cftBtResourceId`→`cashflowTransactionResourceId` (single → `reconcile_with_payments`), `cftBtResourceIds[]`/`isBatch`→`matchedBusinessTransactions` (batch → `reconcile_magic_match`), `recommendationType`→tool, `confidenceScore`→tier. **Request vs response vocabulary:** the `recommendationType` you SEND selects what to compute and additionally accepts `RECOMMENDATIONS` (every type except `MAGIC_MATCH`, ranked together, and the cheaper call; `MAGIC_MATCH` scans open transactions so it is requested on its own; nothing returns everything at once). The `recommendationType` you RECEIVE on a suggestion is always a concrete type and is never `RECOMMENDATIONS`, so the `recommendationType`→tool mapping above is unaffected by which selector you sent. Pass `includeRaw:true` for the unmapped payload. Cost tracks the entries you pass; batch large backlogs. On 500 it returns `{degraded:true}
|
|
551
|
+
159. **`view_auto_reconciliation` returns execution-ready `suggestions[]`, the suggestion→commit seam.** **`bankStatementEntryResourceIds` is REQUIRED: the endpoint is per-entry and has no account-wide mode; source ids from `search_bank_records` (status `UNRECONCILED`).** Each suggestion carries `recommendedTool` (the commit tool to call), `execute` (ready-to-pass args; merge `bankAccountResourceId` for `reconcile_magic_match`), `confidenceTier` (high/medium/low, code-derived), and `autoCommitEligible` (true ⇒ high confidence + executable plan + under any `autoCommitMaxAmount` cap). **Decision gate:** `autoCommitEligible===true` → auto-commit via `recommendedTool`+`execute`; everything else → surface for confirmation. **Amount threshold is a HARD VETO over confidence** (pass `autoCommitMaxAmount`). Field mapping under the hood: `cftBtResourceId`→`cashflowTransactionResourceId` (single → `reconcile_with_payments`), `cftBtResourceIds[]`/`isBatch`→`matchedBusinessTransactions` (batch → `reconcile_magic_match`), `recommendationType`→tool, `confidenceScore`→tier. **Request vs response vocabulary:** the `recommendationType` you SEND selects what to compute and additionally accepts `RECOMMENDATIONS` (every type except `MAGIC_MATCH`, ranked together, and the cheaper call; `MAGIC_MATCH` scans open transactions so it is requested on its own; nothing returns everything at once). The `recommendationType` you RECEIVE on a suggestion is always a concrete type and is never `RECOMMENDATIONS`, so the `recommendationType`→tool mapping above is unaffected by which selector you sent. Pass `includeRaw:true` for the unmapped payload. Cost tracks the entries you pass; batch large backlogs. On a 500, or no answer in time (25 s in chat and on hosted MCP, 55 s on stdio MCP, 60 s from the CLI and `clio mcp-call`), it returns `{degraded:true}` with a `reason` naming the wait; use the `clio jobs bank-recon match` cascade. NOT idempotent applies to every commit; see Rules 125 + 158.
|
|
552
552
|
|
|
553
553
|
## See Also
|
|
554
554
|
|
|
@@ -558,3 +558,5 @@ When the user wants to OPEN, SEE, or SHARE something in the Jaz dashboard ("open
|
|
|
558
558
|
- **jaz-cli**: CLI command reference, auth, output formats, pagination, and workflow patterns
|
|
559
559
|
|
|
560
560
|
160. **Payment `adjustment` is a CASH-LEG-only correction; it never moves the document balance.** Records an overpayment or a rounding difference on the bank side of a payment: `netCash = paymentAmount -/+ fees +/- adjustmentValue` (fees are deducted from cash received on an invoice and added to cash spent on a bill). AR/AP is untouched, so an overpaid invoice stays PAID with the excess sitting on the account you chose and NO credit note is created. Shape: `adjustment: { adjustmentValue, adjustmentAccountResourceId, adjustmentDescription? }`, signed, non-zero, max 2dp, always FLAT (never a percentage), never taxed. Accepted on `pay_invoice` / `pay_bill` / both credit-note refunds / `update_payment` / both receipt reconciliations. **NOT** on batch payments, and not for `DEBT_WRITE_OFF` / `CLEARING_SETTLEMENT` / `INTER_COMPANY` / `WITHHOLDING_TAX_CERTIFICATE` (those record a settlement, so there is no cash leg). The account must be non-controlled: not AR/AP, not the VAT or FX accounts, not bank/cash, not deposit-linked. **Write/read asymmetry:** you WRITE the nested `adjustment.adjustmentValue` but READ a flat `adjustmentAmount` on the payment record, alongside `adjustmentOrganizationAccountResourceId`. Don't confuse either with `lineItems[].taxVatAdjustment.adjustmentAmount`, which is line-item tax and unrelated. **On `update_payment`, adding an adjustment and changing an existing one both APPLY.** Verified against production on arap `v10.7.21` (2026-08-11): an amount move and an account-plus-description move both returned 200 and applied. `paymentAmount` and `transactionAmount` are untouched. **Removing one does NOT work.** `adjustmentValue: 0` is rejected at the edge with a 422, and `"adjustment": null` is indistinguishable from omitting the field, so both leave the stored value unchanged. To remove an adjustment, delete the payment record and record it again without one. An explicit clear flag is being added upstream; zero stays rejected, so omit-to-clear will never be the answer. Anything describing this field as inert on update is stale. **arap `v10.7.21` put both guards behind one comparator, and a CHANGE now has two rejection conditions.** `PaymentAmountCalculator.adjustmentChanged` answers "did the adjustment change?" for the reconciled check AND for lock-date validation, which previously disagreed: the reconciled check compared the amount alone, and lock-date validation did not look at the adjustment at all. It compares **amount, account AND description** (amount by `compareTo`, so `5.0` and `5.00` are equal). Two conditions reject a change: **(a) the payment is RECONCILED** to a bank statement entry, giving `PAYMENT_ADJUSTMENT_CANNOT_CHANGE_WHEN_RECONCILED` (moving it to a different account trips this now, where the amount-only check let it through); **(b) an account on the payment's LEDGER ROWS carries a lock date later than the `valueDate`**, giving `DELETE_TRANSACTION_VALUE_DATE_CANNOT_BE_EARLIER_THAN_LOCK_DATE` -- an update deletes and recreates the ledger rows, so the delete half trips first and the code names a delete for a call that was an update. **The escape hatch:** resending the same adjustment, or omitting the field, compares equal and is NOT a change, so it does not by itself trip either guard. **But that is not a general exemption, and this is the trap.** `adjustmentDescription` is optional, so resending only `adjustmentValue` + `adjustmentAccountResourceId` against a payment that HAS a description compares unequal and counts as a change. Resend all three, or omit the object entirely. And lock-date validation gates on seven other fields independently: changing `reference`, `valueDate`, `paymentMethod`, `organizationAccountResourceId`, `paymentAmount`, `transactionAmount`, or converting a draft to active is lock-date validated on its own. Only edits that move no ledger row survive a lock. **Measured on production 2026-08-11**, throwaway account locked at 2026-07-31 over a payment dated 2026-06-15: change the adjustment -> 422; change ONLY `reference` -> 422; resend all three values unchanged -> 200; resend WITHOUT the description -> 422; then unlock and repeat the first call -> 200 and applied. That last row is the control. Condition (a) is read from the deployed source, not triggered: it needs a bank statement entry, which has no delete, so proving it costs permanent state on a shared org.
|
|
561
|
+
|
|
562
|
+
161. **Report templates: the saved layout of a report, one default per report type, and packs.** Eight verbs on MCP and CLI (list, get, search, default-configuration, create, update, set-default, delete), each taking a template by **resourceId or exact name**; render one with `download_export` / `clio exports download --template <name>`. `templateConfiguration` is a JSON-encoded string on the wire; Clio hands it over as an object, never shows the server-owned `coaSnapshot` (a copy of the whole chart of accounts, up to 324 KB), and returns a large layout as a key map plus a row outline. Change part of one with `edits` (set / insert / remove by path; updateRow / addRow / removeRow / moveRow by row id), applied all or nothing and refused when they introduce a silent fault (an unknown enum value, a formula naming a missing row, a removed engine total). **Set default changes every export and dashboard view** of that report for the whole organization; the answer carries `previousDefault` to undo it. A pack's reports come from `packTemplates` (names or ids, in order), written to both places the server reads them from. Measured upstream quirks (2026-09-24): an export given a template it cannot use (unknown, or of another report type) answers **HTTP 200 with an empty body**, so Clio checks the template first; for P&L and cashflow only `IAS_1` and `IFRS_18` have a default layout (`GAAP` / `IND_AS` / `IFRS_SME` answer 422 `NO_TEMPLATE_FOUND` without one); profit-and-loss and balance-sheet saves drop keys the server does not model with no error (Clio reports them as `notSaved`); deleting a pack's last report leaves an empty pack whenever the pack has any other component (Clio refuses it); four of five search filters answer 500 (Clio filters the list). Full reference: `references/report-templates.md`.
|
|
@@ -2908,6 +2908,87 @@ Body `{ "hash": "..." }`. Deletes the record but RELEASES its payment records as
|
|
|
2908
2908
|
|
|
2909
2909
|
Records an unapplied payment for the bank record and matches the two. Direction, account, currency, date and amount come from the bank record. `amount` is optional and, when sent, must EQUAL the bank record's amount (`TOTAL_RECONCILIATION_AMOUNT_MISMATCHED_WITH_STATEMENT_ENTRY_AMOUNT` otherwise); there is no partial match. `rateToFunctional` is required for a foreign-currency account. `amount` belongs INSIDE `unappliedPaymentDetails`: at the top level it is silently dropped. Returns `{ bankStatementEntryResourceId, status, reference, valueDate, unappliedPaymentResourceId }`.
|
|
2910
2910
|
|
|
2911
|
+
## 28. Report Templates
|
|
2912
|
+
|
|
2913
|
+
The saved layout of a report, one default per report type, plus report packs. The layout fields, `edits`, and every measured quirk are in `references/report-templates.md`; this section is the wire.
|
|
2914
|
+
|
|
2915
|
+
Rules that hold on every route below:
|
|
2916
|
+
- `templateConfiguration` is a **JSON-encoded string** in every request and response. Profit-and-loss and balance-sheet layouts come back with a server-owned `coaSnapshot` (a copy of the chart of accounts, rebuilt on every save; omit it when sending).
|
|
2917
|
+
- Writes return only `{ "data": { "resourceId": "..." } }`. Read the template back to see what was stored.
|
|
2918
|
+
- Names are unique per report type, ignoring case and treating space and `_` alike (422 `DUPLICATE_REPORT_TEMPLATE_EXISTS`).
|
|
2919
|
+
|
|
2920
|
+
### GET /api/v1/organization-report-template
|
|
2921
|
+
|
|
2922
|
+
Every template of the organization in one response, **under `reportTemplates`, not `data`**, and ignoring `limit`/`offset`. Rows: `resourceId`, `templateName`, `reportType`, `reportCategory`, `isDefault`, `templateConfiguration`, and on packs `reportPackType` and `packTemplates` (`[{ templateResourceId, templateName, templateOrder, reportType }]`).
|
|
2923
|
+
|
|
2924
|
+
### GET /api/v1/organization-report-template/{resourceId}
|
|
2925
|
+
|
|
2926
|
+
`{ "data": { ...one row... } }`. 404 `Report template not found` for an unknown id. A template read in the same second it was created can 404 once.
|
|
2927
|
+
|
|
2928
|
+
### POST /api/v1/organization-report-template/search
|
|
2929
|
+
|
|
2930
|
+
`{ filter, sort }`, answers a bare array. **Four of its five filters answer 500** (`reportCategory` without `reportType`, `isDefault`, `reportTypes`, `resourceId`), and there is no name filter. Clio filters the list instead.
|
|
2931
|
+
|
|
2932
|
+
### GET /api/v1/organization-report-template/default-configuration
|
|
2933
|
+
|
|
2934
|
+
`?reportType=PROFIT_AND_LOSS&framework=IFRS_18` returns `{ "data": { "templateConfiguration": "..." } }`, the layout a new template starts from, in the organization's own currency. The 12 single-report types only (not `REPORT_PACK`). `framework` is accepted for `PROFIT_AND_LOSS` and `CASHFLOW` only, and **only `IAS_1` and `IFRS_18` have a layout**: the other three answer 422 `NO_TEMPLATE_FOUND`. Takes 0.1-5 s (it builds a report preview).
|
|
2935
|
+
|
|
2936
|
+
### POST /api/v1/organization-report-template
|
|
2937
|
+
|
|
2938
|
+
```json
|
|
2939
|
+
{ "templateName": "Board P&L", "reportType": "PROFIT_AND_LOSS", "framework": "IAS_1", "templateConfiguration": "{...}" }
|
|
2940
|
+
```
|
|
2941
|
+
|
|
2942
|
+
- **Required:** `templateName` (max 255) and `reportType`.
|
|
2943
|
+
- **Omit `templateConfiguration`** to start from the default layout. Any well-formed JSON is accepted as a layout, and an unusable one renders as the default.
|
|
2944
|
+
- **`framework`** is for P&L, balance sheet, cashflow and equity movement only; it defaults to `IAS_1` and cannot change later.
|
|
2945
|
+
- **`REPORT_PACK` requires both** `templateConfiguration` and `packTemplates: [{ templateResourceId, templateOrder }]`. They are refused on any other type.
|
|
2946
|
+
- Keep `packTemplates` and the layout's `TEMPLATE` components in step, or the missing report renders as an error page.
|
|
2947
|
+
- Refusals: `TEMPLATE_NOT_FOUND` for an unknown member, `DUPLICATE_TEMPLATE_ORDER` for a repeated position.
|
|
2948
|
+
- **Not made the default**, except that the first template of a type (in practice, the first pack) becomes its default.
|
|
2949
|
+
- Returns 201.
|
|
2950
|
+
|
|
2951
|
+
### PUT /api/v1/organization-report-template/{resourceId}
|
|
2952
|
+
|
|
2953
|
+
Any of `templateName`, `templateConfiguration`, `packTemplates`.
|
|
2954
|
+
- `templateConfiguration` and `packTemplates` each **replace** the stored value in full.
|
|
2955
|
+
- Report type and framework cannot change; other fields are ignored without an error.
|
|
2956
|
+
- `packTemplates` on a non-pack is 422.
|
|
2957
|
+
|
|
2958
|
+
### POST /api/v1/organization-report-template/{resourceId}/set-default
|
|
2959
|
+
|
|
2960
|
+
No body. Makes the template its report type's default and clears the previous one. **Every export and dashboard view of that report then uses it.** 404 for an unknown id.
|
|
2961
|
+
|
|
2962
|
+
### DELETE /api/v1/organization-report-template/{resourceId}
|
|
2963
|
+
|
|
2964
|
+
Removes the template from every pack that includes it.
|
|
2965
|
+
- **Refused:** the default of a report type (422 `CANNOT_DELETE_REPORT_TEMPLATE`, "Set another template as the default first"), and the only template of a type.
|
|
2966
|
+
- **Not refused:** a pack's last report whenever the pack has any other component, which leaves the pack with no reports. Clio refuses that one itself.
|
|
2967
|
+
- Packs can always be deleted. Deleting the default pack hands the default to the newest remaining pack.
|
|
2968
|
+
|
|
2969
|
+
### Rendering with a template
|
|
2970
|
+
|
|
2971
|
+
`POST /api/v1/data-exports/{type}` takes `templateResourceId` (or `templateName`) for these types, each rendering one report type:
|
|
2972
|
+
|
|
2973
|
+
| Export type | Report type |
|
|
2974
|
+
|---|---|
|
|
2975
|
+
| `profit-and-loss` | `PROFIT_AND_LOSS` |
|
|
2976
|
+
| `balance-sheet` | `BALANCE_SHEET` |
|
|
2977
|
+
| `cashflow` | `CASHFLOW` |
|
|
2978
|
+
| `equity-movement` | `EQUITY_MOVEMENT` |
|
|
2979
|
+
| `trial-balance` | `TRIAL_BALANCE` |
|
|
2980
|
+
| `general-ledger` | `GENERAL_LEDGER` |
|
|
2981
|
+
| `tax-ledger` | `VAT_LEDGER` |
|
|
2982
|
+
| `cash-balance` | `CASH_BALANCE` |
|
|
2983
|
+
| `ar-report` | `AGED_RECEIVABLES_SUMMARY` |
|
|
2984
|
+
| `ar-details-report` | `AGED_RECEIVABLES_DETAILS` |
|
|
2985
|
+
| `ap-report` | `AGED_PAYABLES_SUMMARY` |
|
|
2986
|
+
| `ap-details-report` | `AGED_PAYABLES_DETAILS` |
|
|
2987
|
+
|
|
2988
|
+
- **A template that is unknown, or of another report type, answers HTTP 200 with an EMPTY body.** It is not an error status, and there is no file.
|
|
2989
|
+
- Omit the template to render the organization's default.
|
|
2990
|
+
- The `generate-reports/templated-*` endpoints take `reportTemplateResourceId` and return the report as JSON; a template of the wrong type answers 404 `template_not_found`.
|
|
2991
|
+
|
|
2911
2992
|
---
|
|
2912
2993
|
|
|
2913
|
-
*Last updated: 2026-09-23 (added Unapplied Payments, section 27). Previous: 2026-09-13 (added Bank Rules CRUD, section 26). Previous: 2026-07-11 (added Jots judgment journal, section 25). Previous: 2026-04-09, added Contacts bulk-upsert (22), Background Jobs search (23), Export Records (24). 2026-03-13: Payment record CRUD, nano-classifier, scheduler GET/PUT/DELETE.*
|
|
2994
|
+
*Last updated: 2026-09-24 (added Report Templates, section 28). Previous: 2026-09-23 (added Unapplied Payments, section 27). Previous: 2026-09-13 (added Bank Rules CRUD, section 26). Previous: 2026-07-11 (added Jots judgment journal, section 25). Previous: 2026-04-09, added Contacts bulk-upsert (22), Background Jobs search (23), Export Records (24). 2026-03-13: Payment record CRUD, nano-classifier, scheduler GET/PUT/DELETE.*
|
|
@@ -944,6 +944,30 @@ Two of the seven never reach you through this path: a zero `adjustmentValue` and
|
|
|
944
944
|
**Cause**: `add_employee` was called without `claimProfileResourceId` ("A claim profile is required for an employee — it carries the employee balance account used to convert and pay claims"). The claim profile is **server-required**; the org default is NOT auto-applied for employees (unlike claims), even when a default profile exists.
|
|
945
945
|
**Fix**: Pick a profile via `search_claim_profiles` and pass its `resourceId` as `claimProfileResourceId`. The profile is locked while the employee has unsettled claims.
|
|
946
946
|
|
|
947
|
+
### "DUPLICATE_REPORT_TEMPLATE_EXISTS" (422)
|
|
948
|
+
**Cause**: A report template of the same report type already has this name. Names are unique per report type, ignoring case and treating space and `_` alike ("My P&L" and "my_p&l" collide); the same name on another report type is fine.
|
|
949
|
+
**Fix**: Pick another name, or update the existing template. The Clio tools refuse this before sending and name the existing template's resourceId.
|
|
950
|
+
|
|
951
|
+
### "NO_TEMPLATE_FOUND" (422)
|
|
952
|
+
**Cause**: A P&L or cashflow default layout was asked for (default configuration, or a create with no `templateConfiguration`) under a framework that has none. Only `IAS_1` and `IFRS_18` have one; `GAAP`, `IND_AS` and `IFRS_SME` pass validation and then land here.
|
|
953
|
+
**Fix**: Use `IAS_1` or `IFRS_18`, or pass `templateConfiguration` (for example an existing template's layout, from `get_organization_report_template`).
|
|
954
|
+
|
|
955
|
+
### "CANNOT_DELETE_REPORT_TEMPLATE" (422)
|
|
956
|
+
**Cause**: The template is the default of its report type, or the only template of it.
|
|
957
|
+
**Fix**: Make another template the default first (`set_default_organization_report_template`; CLI `clio report-templates set-default`), then delete.
|
|
958
|
+
|
|
959
|
+
### "TEMPLATE_NOT_FOUND" / "DUPLICATE_TEMPLATE_ORDER" (422)
|
|
960
|
+
**Cause**: A report pack's `packTemplates` lists a template that does not exist, or two reports at the same `templateOrder`.
|
|
961
|
+
**Fix**: List the templates and send `packTemplates` again. The Clio tools take pack reports as names or ids in order and number them, so neither arises through them.
|
|
962
|
+
|
|
963
|
+
### "template_not_found" (404, templated reports)
|
|
964
|
+
**Cause**: A `generate-reports/templated-*` call named a template that exists but is of another report type. The message ("not found in organization") says the template does not exist; it does.
|
|
965
|
+
**Fix**: Pass a template of that report's type.
|
|
966
|
+
|
|
967
|
+
### Export returns HTTP 200 with an EMPTY body
|
|
968
|
+
**Cause**: A `data-exports/{type}` call named a report template it cannot use: one that does not exist, or one of another report type (also an unknown `templateName`). Measured on all 12 template-taking exports. No status code says so.
|
|
969
|
+
**Fix**: Pass a template of that export's report type (table in `references/report-templates.md`). `download_export` / `clio exports download` take `template` (a name or id) and check it first; given a raw `templateResourceId`, they report the empty answer as an error instead of retrying it.
|
|
970
|
+
|
|
947
971
|
---
|
|
948
972
|
|
|
949
973
|
## Repair Suggestions (W1.3)
|
|
@@ -678,6 +678,18 @@ Battle-tested patterns from production Jaz API clients:
|
|
|
678
678
|
| `isPrintable` | `printable` | Boolean, no `is` prefix. Default `false`; most classifiers are not printable |
|
|
679
679
|
| `categories` | `classes` | The "categories" or "options" are called `classes` |
|
|
680
680
|
|
|
681
|
+
### Report Template Fields
|
|
682
|
+
|
|
683
|
+
| What You'd Guess | Actual API Field | Notes |
|
|
684
|
+
|------------------|-------------------|-------|
|
|
685
|
+
| `layout` / `config` | `templateConfiguration` | A JSON-encoded **string** on the wire; the Clio tools hand it over as an object |
|
|
686
|
+
| `name` | `templateName` | Unique per report type |
|
|
687
|
+
| `type` | `reportType` | UPPER_SNAKE: `PROFIT_AND_LOSS`, `VAT_LEDGER` (the tax ledger), `AGED_RECEIVABLES_SUMMARY`, ... |
|
|
688
|
+
| `default` | `isDefault` | One per report type; change with set-default, never on update |
|
|
689
|
+
| `templateId` (exports) | `templateResourceId` | On `data-exports/{type}` |
|
|
690
|
+
| `templateId` (templated reports) | `reportTemplateResourceId` | On `generate-reports/templated-*` |
|
|
691
|
+
| `reports` (pack) | `packTemplates[].templateResourceId` + `templateOrder` | Must match the layout's `includedComponents` TEMPLATE entries |
|
|
692
|
+
|
|
681
693
|
## Background Jobs
|
|
682
694
|
|
|
683
695
|
🚨 The single most dangerous trap in background-jobs:
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
> **The endpoints clio wraps (the REST surface is larger).** This file catalogues roughly 200
|
|
4
4
|
> paths, organized by resource, including undocumented endpoints, magic AI features, admin
|
|
5
5
|
> APIs, and advanced search/filter syntax. The committed OpenAPI spec (`spec/openapi.yaml`)
|
|
6
|
-
> carries
|
|
6
|
+
> carries 468 spec paths and 599 spec operations and is the authoritative list; check it before concluding
|
|
7
7
|
> an endpoint does not exist. For request/response examples of core endpoints, see
|
|
8
8
|
> endpoints.md.
|
|
9
9
|
>
|
|
@@ -519,7 +519,16 @@ Body for all three: `{ items: [{btResourceId: "<uuid>", btType: "SALE|PURCHASE|S
|
|
|
519
519
|
### Organization Report Templates
|
|
520
520
|
| Method | Path | Description |
|
|
521
521
|
|--------|------|-------------|
|
|
522
|
-
|
|
|
522
|
+
| GET | `/organization-report-template` | List every template (rows under `reportTemplates`; ignores paging) |
|
|
523
|
+
| GET | `/organization-report-template/:resourceId` | Get one |
|
|
524
|
+
| POST | `/organization-report-template/search` | Search (4 of 5 filters answer 500; Clio filters the list instead) |
|
|
525
|
+
| GET | `/organization-report-template/default-configuration` | The default layout a new template of a report type starts from |
|
|
526
|
+
| POST | `/organization-report-template` | Create (layout defaults to the system layout; a pack needs its layout and `packTemplates`) |
|
|
527
|
+
| PUT | `/organization-report-template/:resourceId` | Rename, replace the layout, or replace a pack's reports |
|
|
528
|
+
| POST | `/organization-report-template/:resourceId/set-default` | Make it its report type's default (every export then uses it) |
|
|
529
|
+
| DELETE | `/organization-report-template/:resourceId` | Delete (refused for a type's default) |
|
|
530
|
+
|
|
531
|
+
Layouts, `edits` and measured quirks: `report-templates.md`.
|
|
523
532
|
|
|
524
533
|
### Catalogs
|
|
525
534
|
| Method | Path | Description |
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
# Report Templates
|
|
2
|
+
|
|
3
|
+
A **report template** is the saved layout of one report: its title, fonts, colours, the period it opens on, its columns and (for the financial statements) the tree of rows and totals. A **report pack** is a template that bundles several reports, plus an optional cover page, contents, signature page and appendix, into one document.
|
|
4
|
+
|
|
5
|
+
Every report type can hold many templates. **Exactly one per type is the default**, and the default is what every export and every dashboard view of that report uses when no template is named.
|
|
6
|
+
|
|
7
|
+
Measured against production on 2026-09-24. Everything below that says "measured" was probed live, not read from a spec.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## The surfaces
|
|
12
|
+
|
|
13
|
+
Every surface uses the same eight verbs, and each takes a template by **resourceId or exact name**:
|
|
14
|
+
|
|
15
|
+
| Verb | MCP tool | CLI | API |
|
|
16
|
+
|---|---|---|---|
|
|
17
|
+
| list | `list_organization_report_templates` | `clio report-templates list` | `GET /organization-report-template` |
|
|
18
|
+
| read | `get_organization_report_template` | `clio report-templates get <template>` | `GET /organization-report-template/{id}` |
|
|
19
|
+
| search | `search_organization_report_templates` | `clio report-templates search` | (evaluated over the list, see below) |
|
|
20
|
+
| default layout | `get_default_organization_report_template_configuration` | `clio report-templates default-config --type <T>` | `GET /organization-report-template/default-configuration` |
|
|
21
|
+
| create | `create_organization_report_template` | `clio report-templates create` | `POST /organization-report-template` |
|
|
22
|
+
| update | `update_organization_report_template` | `clio report-templates update <template>` | `PUT /organization-report-template/{id}` |
|
|
23
|
+
| make default | `set_default_organization_report_template` | `clio report-templates set-default <template>` | `POST /organization-report-template/{id}/set-default` |
|
|
24
|
+
| delete | `delete_organization_report_template` | `clio report-templates delete <template>` | `DELETE /organization-report-template/{id}` |
|
|
25
|
+
| **render** | `download_export` with `template` | `clio exports download --template <template>` | `POST /data-exports/{type}` with `templateResourceId` |
|
|
26
|
+
|
|
27
|
+
Resolve a name to its id on its own with `clio resolve report-template <name>`.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Report types
|
|
32
|
+
|
|
33
|
+
| reportType | What it lays out | Renders through export | Period | `framework` |
|
|
34
|
+
|---|---|---|---|---|
|
|
35
|
+
| `PROFIT_AND_LOSS` | Profit and loss | `profit-and-loss` | range | yes; picks the starting layout |
|
|
36
|
+
| `BALANCE_SHEET` | Balance sheet | `balance-sheet` | as at a date | accepted; one layout for all |
|
|
37
|
+
| `CASHFLOW` | Cashflow statement | `cashflow` | range | yes; picks the starting layout |
|
|
38
|
+
| `EQUITY_MOVEMENT` | Statement of changes in equity | `equity-movement` | range | accepted; one layout for all |
|
|
39
|
+
| `TRIAL_BALANCE` | Trial balance | `trial-balance` | as at a date | no |
|
|
40
|
+
| `GENERAL_LEDGER` | General ledger | `general-ledger` | range | no |
|
|
41
|
+
| `VAT_LEDGER` | Tax (GST/VAT) ledger | `tax-ledger` | range | no |
|
|
42
|
+
| `CASH_BALANCE` | Cash balance | `cash-balance` | as at a date | no |
|
|
43
|
+
| `AGED_RECEIVABLES_SUMMARY` | Aged receivables | `ar-report` | as at a date | no |
|
|
44
|
+
| `AGED_RECEIVABLES_DETAILS` | Aged receivables, per document | `ar-details-report` | as at a date | no |
|
|
45
|
+
| `AGED_PAYABLES_SUMMARY` | Aged payables | `ap-report` | as at a date | no |
|
|
46
|
+
| `AGED_PAYABLES_DETAILS` | Aged payables, per document | `ap-details-report` | as at a date | no |
|
|
47
|
+
| `REPORT_PACK` | Several reports in one document | not through the public API | range | no |
|
|
48
|
+
|
|
49
|
+
- Bank balance summary and the two bank reconciliation reports have a fixed layout. No template applies to them.
|
|
50
|
+
- **`framework`** takes `IAS_1`, `IFRS_18`, `GAAP`, `IND_AS` or `IFRS_SME`, and defaults to `IAS_1`.
|
|
51
|
+
- **For P&L and cashflow, only `IAS_1` and `IFRS_18` have a default layout.** The other three pass validation, then answer `422 NO_TEMPLATE_FOUND` when no layout is given. Balance sheet and equity movement have one default layout whatever the framework, so every framework works there.
|
|
52
|
+
- To use one of them, create from an existing template's layout.
|
|
53
|
+
- The framework is set on create and cannot change. The list and get responses do not return it.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Reading layouts
|
|
58
|
+
|
|
59
|
+
`templateConfiguration` is stored as a **JSON-encoded string**. Clio tools and the CLI hand it over as an **object**, and accept an object or a string back.
|
|
60
|
+
|
|
61
|
+
**`coaSnapshot` is never shown and never sent.**
|
|
62
|
+
- The server adds it to profit-and-loss and balance-sheet layouts on every save: a copy of the organization's whole chart of accounts. It measured 166-324 KB on a sandbox.
|
|
63
|
+
- It exists only to detect later chart changes. The server rebuilds it on every write, so sending a layout without it is safe (measured).
|
|
64
|
+
|
|
65
|
+
**Layouts are large.** A real P&L or balance sheet layout is 16-59 KB even without the snapshot. So:
|
|
66
|
+
- `list` / `search` return no layouts at all, only `configurationChars` (the size).
|
|
67
|
+
- `get` returns the whole layout when it fits (up to 15,000 characters). Otherwise it returns:
|
|
68
|
+
- `configurationKeys`: each top-level key and its size.
|
|
69
|
+
- `rowOutline`: the row tree as ids, names, types and formulas, with no matching rules.
|
|
70
|
+
- Then ask for what you need:
|
|
71
|
+
- MCP: `keys: ["pdfTitleConfigs", "columnConfigs"]`, or `row: "<row id>"` for one row in full.
|
|
72
|
+
- CLI: `--keys pdfTitleConfigs,columnConfigs`, `--row <id>`.
|
|
73
|
+
- CLI: `--config-out layout.json` writes the whole layout to a file.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Changing layouts: `edits`
|
|
78
|
+
|
|
79
|
+
Upstream replaces the whole layout on every write. To change part of it, send **`edits`**: Clio applies them to the stored layout and sends the result.
|
|
80
|
+
|
|
81
|
+
- **All or nothing.** The first edit that cannot apply stops everything, names its position, and nothing is written.
|
|
82
|
+
- **Checked.** An edit is refused if it *introduces* one of these problems:
|
|
83
|
+
- a value outside a fixed set
|
|
84
|
+
- a formula that names a missing row
|
|
85
|
+
- a removed total the report engine reads
|
|
86
|
+
- a period type the report cannot use
|
|
87
|
+
|
|
88
|
+
A value the layout already had is never held against you.
|
|
89
|
+
|
|
90
|
+
| op | Shape | Does |
|
|
91
|
+
|---|---|---|
|
|
92
|
+
| `set` | `{ op, path, value }` | Set a value (any JSON). On a list position it **replaces** that entry. |
|
|
93
|
+
| `insert` | `{ op, path, value }` | Insert into a list at that position (`.-` appends). Use this to add a component. |
|
|
94
|
+
| `remove` | `{ op, path }` | Remove a key or a list entry. |
|
|
95
|
+
| `updateRow` | `{ op, rowId, fields }` | Merge fields into a row: `name`, `actionCriteria`, `formulaConfig`, `showGroupTotal`, `hideRowIfZeroValue`, ... |
|
|
96
|
+
| `addRow` | `{ op, row, parentId?, index? }` | Add a row (`id`, `name`, `rowType`, then criteria or formula). Omit `parentId` for top level. |
|
|
97
|
+
| `removeRow` | `{ op, rowId }` | Remove a row and its children. Refused while a formula uses it. |
|
|
98
|
+
| `moveRow` | `{ op, rowId, parentId?, index? }` | Move a row under another parent or to another position. |
|
|
99
|
+
|
|
100
|
+
**Paths** are dotted keys and list positions (`pdfTitleConfigs.title`, `columnConfigs.0.type`), or a JSON Pointer (`/pdfTitleConfigs/title`).
|
|
101
|
+
|
|
102
|
+
**Rows are addressed by id**, never by position, because positions shift as rows move.
|
|
103
|
+
|
|
104
|
+
```jsonc
|
|
105
|
+
// Retitle a P&L, calm colours, and rename a group
|
|
106
|
+
{
|
|
107
|
+
"template": "Board P&L",
|
|
108
|
+
"edits": [
|
|
109
|
+
{ "op": "set", "path": "pdfTitleConfigs.title", "value": "Monthly Board P&L" },
|
|
110
|
+
{ "op": "set", "path": "colorTheme", "value": "calm" },
|
|
111
|
+
{ "op": "updateRow", "rowId": "OPERATING_REVENUE", "fields": { "name": "Revenue from operations" } }
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
# The same from the CLI: one flag per change, or a file of edits
|
|
118
|
+
clio report-templates update "Board P&L" \
|
|
119
|
+
--set pdfTitleConfigs.title="Monthly Board P&L" --set colorTheme=calm
|
|
120
|
+
clio report-templates update "Board P&L" --edits edits.json
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**What the server did not keep.** Profit-and-loss and balance-sheet layouts pass through a fixed layout model on every save, and a key it does not model is dropped with no error. Measured 2026-09-24: `showZeroValueAccounts: true` set on a P&L through the API was not kept (older templates saved another way can still carry that key). Every write re-reads the template, and `notSaved` lists any edited path whose value did not survive. The rest of the write went through.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Layout fields
|
|
128
|
+
|
|
129
|
+
Shared by every single-report type. Every value in the table is from the report engine's own list: anything else falls back silently, so an edit that writes it is refused.
|
|
130
|
+
|
|
131
|
+
| Key | Values |
|
|
132
|
+
|---|---|
|
|
133
|
+
| `colorTheme` | `default` `calm` `bold` `trust` `social` `ivory` `gold` `peach` |
|
|
134
|
+
| `margins` | `default` `compact` `comfortable` |
|
|
135
|
+
| `fontSize` | `default` `smaller` `larger` |
|
|
136
|
+
| `templateFont` | `Inter` `LatoFont` `Roboto` `Figtree` `SourceSans3` |
|
|
137
|
+
| `templateStyle` | `REPORT_TEMPLATE_CLASSIC` `REPORT_TEMPLATE_BALANCED` `REPORT_TEMPLATE_MODERN` `REPORT_TEMPLATE_UNIFORM` |
|
|
138
|
+
| `isDecorator`, `isUppercase` | booleans |
|
|
139
|
+
| `lineBreak` | `L0` `L1` `L2` `L3`: spacer rows in Excel down to that tree level |
|
|
140
|
+
| `pdfTitleConfigs` | `{ title, layout, headline, dateTitle, isUppercase }`; `layout` is `left_aligned` or `center_aligned`, `headline` is `HEADLINE_COMPANY_NAME` or `HEADLINE_REPORT_TITLE` |
|
|
141
|
+
| `documentDetailConfigs` | `{ documentTitle, currency: { currencyCode, currencyName, currencySymbol }, enableOrganizationName, enableReportPeriod }`. An empty currency means the organization's own. |
|
|
142
|
+
| `pageFooterConfigs` | `{ enablePageNumber, enableGeneratedBy, enableDatestamp, enableTimeStamp }` |
|
|
143
|
+
| `footerDetailsConfigs` | `{ enableCurrencyDetails, enableReportNotes, enableRoundedValues }` |
|
|
144
|
+
| `reportNotesConfig` | `{ reportNotesValue, reportNotesJson }`, both **base64-encoded** |
|
|
145
|
+
| `documentLogo` | `[{ fileId, fileUrl, fileName, fileType }]` |
|
|
146
|
+
| `sortConfigs` | `{ type, primarySortBy, secondarySortBy }`; `type` is `FIXED` or `CUSTOM`, each sort `CODE_ASC` `CODE_DESC` `NAME_ASC` `NAME_DESC` `AMOUNT_ASC` `AMOUNT_DESC` |
|
|
147
|
+
|
|
148
|
+
**`dateTitle` placeholders:**
|
|
149
|
+
- `{{DateAt}}`: balance sheet, trial balance, aging and cash balance.
|
|
150
|
+
- `{{DateFrom}}` and `{{DateTo}}`: every period report.
|
|
151
|
+
|
|
152
|
+
**`basePeriodConfig.periodType`** depends on whether the report covers a range or is as at a date. Dates are epoch milliseconds and are needed only for `custom`.
|
|
153
|
+
- **Range reports:**
|
|
154
|
+
- `this_month` `last_month` `this_quarter` `last_quarter` `this_year` `last_year` `this_week`
|
|
155
|
+
- `this_financial_year` `last_financial_year` (also `previous_financial_year`)
|
|
156
|
+
- `this_financial_quarter` (also `this_financial_year_this_quarter`), `last_financial_quarter` (also `previous_financial_quarter`, `this_financial_year_last_quarter`)
|
|
157
|
+
- `custom`, with `startDate` and `endDate`
|
|
158
|
+
- **As-at reports:**
|
|
159
|
+
- `today` `end_of_this_month` `end_of_last_month` `end_of_last_quarter` `end_of_last_financial_quarter` `end_of_last_financial_year`
|
|
160
|
+
- `custom`, with `endDate`
|
|
161
|
+
|
|
162
|
+
### Columns
|
|
163
|
+
|
|
164
|
+
`columnConfigs[].type` takes one of the following. With no `COMPARE` column, the engine adds one.
|
|
165
|
+
|
|
166
|
+
| type | Config |
|
|
167
|
+
|---|---|
|
|
168
|
+
| `COMPARE` | `{ dateRange, compareWith: PREVIOUS_DAYS \| PREVIOUS_MONTHS \| PREVIOUS_QUARTERS \| PREVIOUS_YEARS, compareCount, columnOrder: RECENT_TO_OLDEST \| OLDEST_TO_RECENT }` |
|
|
169
|
+
| `YEAR_TO_DATE` | `{ periodType: CALENDAR_YEAR \| FINANCIAL_YEAR, rangeType: THROUGH_TODAY \| THROUGH_PERIOD_END, columnNameType }` |
|
|
170
|
+
| `VARIANCE` | `{ displayAs: ABSOLUTE \| PERCENTAGE, firstColumn, secondColumn }` |
|
|
171
|
+
| `ACCOUNT_CODE` | Shows the account code column |
|
|
172
|
+
| `TOTAL` | Total column |
|
|
173
|
+
| `MONTH_TO_DATE` | Month to date |
|
|
174
|
+
| `CURRENT_PERIOD` | The period itself |
|
|
175
|
+
|
|
176
|
+
There is no budget column.
|
|
177
|
+
|
|
178
|
+
### Per report type
|
|
179
|
+
|
|
180
|
+
- **Trial balance, `reportSelectedColumns`:** `accountCode` `accountName` `accountType` `status` `accountCurrency` `debitAmount` `creditAmount` `debitAmountIso` `creditAmountIso`.
|
|
181
|
+
- **Aged receivables / payables summary, `reportSelectedColumns`:**
|
|
182
|
+
- Name: `customerName` (payables: `supplierName`).
|
|
183
|
+
- Buckets: `current` `lessThanOneMonth` `oneMonth` `twoMonths` `threeMonths` `older` `balance`.
|
|
184
|
+
- Optional: `customerTaxId` / `supplierTaxId`, `contactGroup`, `contactRelationship`, `balanceSource`.
|
|
185
|
+
- **General ledger:**
|
|
186
|
+
- `groupByConfigs.groupBy` is one of `ACCOUNT` `CONTACT` `TRANSACTION` `RELATIONSHIP` `CAPSULE`.
|
|
187
|
+
- `groupByColumns` is `[{ type: <groupBy>, columns: [codes] }]`; the entry matching the active groupBy is used.
|
|
188
|
+
- Column codes: `vldt` date, `btty` type, `trde` description, `btrf` reference, `acna` account, `acty` account type, `cnnm` contact, `debt` debit, `crdt` credit, `amnt` balance, `scc` source currency, `rsf` rate, `sca`/`sda` source credit/debit, `tags` tags, `cpsl` capsule, `cogr` contact groups, `corl` contact relationships, `clfs` classifiers, `ntr` nature.
|
|
189
|
+
- **Aging details:** `groupBy` is `CONTACT` `CONTACT_GROUP` `DUE_MONTH` `RELATIONSHIP` or `CURRENCY`.
|
|
190
|
+
- **Tax ledger:** groups by tax return box (e.g. `IRAS_F5`).
|
|
191
|
+
|
|
192
|
+
### Rows (profit and loss, balance sheet, cashflow)
|
|
193
|
+
|
|
194
|
+
`rowConfigs` is a tree, and each row has these fields:
|
|
195
|
+
|
|
196
|
+
| Field | Meaning |
|
|
197
|
+
|---|---|
|
|
198
|
+
| `id` | Unique in the layout. A system group (`OPERATING_REVENUE`) or an account's resourceId (an account row). |
|
|
199
|
+
| `name` | Label shown on the report |
|
|
200
|
+
| `rowType` | `GROUP` collects accounts. `SECTION` is structure only. `TOTAL` is a formula. `COMBINE_ACCOUNT` merges accounts into one line. `TEXT` is a label. `ROW` is one account. `GROUP_TOTAL` is a group's total line (id `<group id>__GT`), which the dashboard stores for every group that shows its total. |
|
|
201
|
+
| `actionCriteria` | Which accounts a `GROUP` collects: `[{ criteriaVariable, criteriaCondition, criteriaValue }]`, all must match |
|
|
202
|
+
| `formulaConfig.formula` | For `TOTAL`: row ids joined by `+` / `-`, e.g. `OPERATING_REVENUE-COST_OF_SALE`. A group whose id is a UUID is written `GT(<uuid>)`, so its dashes are not read as minus signs. **A missing id silently counts as 0.** |
|
|
203
|
+
| `showGroupTotal`, `hideGroupIfAllRowsHidden`, `hideRowIfZeroValue` | Display switches |
|
|
204
|
+
| `negativeBalanceCriteria` | `MOVE_TO` another group when negative (the only action the engine applies) |
|
|
205
|
+
| `children` | Nested rows |
|
|
206
|
+
|
|
207
|
+
**Criteria fields:**
|
|
208
|
+
- `criteriaVariable`: `ACCOUNT_TYPE` `ACCOUNT_CODE` `ACCOUNT_NAME` `ACCOUNT_CLASS`.
|
|
209
|
+
- `criteriaCondition`: `IS` `IS_NOT` `IS_IN` `IS_NOT_IN` `CONTAINS` `DOES_NOT_CONTAIN` `STARTS_WITH` `ENDS_WITH`. Matching ignores case.
|
|
210
|
+
- `IS_IN A|B` works for `ACCOUNT_TYPE` only. Account types are written uppercase with underscores (`OPERATING_REVENUE`).
|
|
211
|
+
|
|
212
|
+
**Totals the engine reads by id.** Removing one changes the report silently, so `edits` refuses it:
|
|
213
|
+
- **P&L:**
|
|
214
|
+
- `TOTAL_GROSS_PROFIT`, `TOTAL_OPERATING_PROFIT`, `TOTAL_OTHER_PROFIT`
|
|
215
|
+
- `TOTAL_NET_PROFIT`, `TOTAL_NET_PROFIT_BEFORE_TAX`, `TOTAL_NET_PROFIT_AFTER_TAX`
|
|
216
|
+
- IFRS 18 only: `TOTAL_PBFIT`, `TOTAL_PROFIT_BEFORE_INCOME_TAX`, `TOTAL_PROFIT`
|
|
217
|
+
- **Balance sheet:** `TOTAL_ASSETS`, `TOTAL_LIABILITIES_AND_EQUITY`.
|
|
218
|
+
- **Cashflow:** `NET_CASHFLOW`, `CASH_SUMMARY_BEGINNING_CASH_BALANCE`, `CASH_SUMMARY_NET_INCREASE_IN_CASH`, `CASH_SUMMARY_ENDING_CASH_BALANCE`.
|
|
219
|
+
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
## Report packs
|
|
223
|
+
|
|
224
|
+
| Key | Values |
|
|
225
|
+
|---|---|
|
|
226
|
+
| `reportPackType` | `ACCOUNTING` `FINANCIALS` `MANAGEMENT` `PAYABLES` `RECEIVABLES` `CUSTOM` |
|
|
227
|
+
| `decorator` | `FIRST_PAGE_ONLY` `ALL_PAGES` `NO_DECORATOR` |
|
|
228
|
+
| `roundedValuesMode` | `AS_PER_TEMPLATE` `ROUNDED_VALUES` `EXACT_VALUES` |
|
|
229
|
+
| `basePeriodConfig`, `templateFont`, `isUppercase`, `enablePageNumber` | as above |
|
|
230
|
+
| `includedComponents` | The document, in order. Each entry is `{ id, type, <config> }`. |
|
|
231
|
+
|
|
232
|
+
**Component types:**
|
|
233
|
+
|
|
234
|
+
| type | Config |
|
|
235
|
+
|---|---|
|
|
236
|
+
| `COVER_PAGE` | `coverPageConfig { title, subtitle, notes, position, logo[], enableOrganizationName, enableReportPeriod, enablePreparedBy }` |
|
|
237
|
+
| `TABLE_OF_CONTENTS` | `tableOfContentsConfig { title, position, dotLeaders: DOTS \| DASHES \| NONE, startNumberingAt: <component id>, enableSerialNumber }` |
|
|
238
|
+
| `TEMPLATE` | One report: `templateConfig { templateResourceId, templateName, reportType }` |
|
|
239
|
+
| `SIGNATURE_PAGE` | `signaturePageConfig { title, notes, position, labelLines: [{ label, enabled }] }` |
|
|
240
|
+
| `APPENDIX` | `appendixPageConfig { title, position, appendixNotesConfig }` |
|
|
241
|
+
|
|
242
|
+
Cover and notes text may use `{{OrganizationName}}`, `{{PreparedBy}}`, `{{DateFrom}}` and `{{DateTo}}`.
|
|
243
|
+
|
|
244
|
+
**A pack's reports are recorded twice upstream**: in its `TEMPLATE` components and in `packTemplates`. A report missing from either renders as an error page. Clio keeps them in step with one rule:
|
|
245
|
+
- **`packTemplates` decides which reports** are in the pack. Give it names or resourceIds, in order. Clio writes both places from it.
|
|
246
|
+
- **The layout decides everything else**: cover page, contents, signature, styling. Change those with `edits`. Use `insert` (not `set`) to add a component.
|
|
247
|
+
- **Edits that would change the reports are refused** instead of silently dropping one.
|
|
248
|
+
- A new pack starts with its reports and nothing else. Upstream has no default pack layout.
|
|
249
|
+
- A pack cannot contain a pack. The first pack an organization creates becomes its default pack.
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
clio report-templates create --name "Board pack" --type REPORT_PACK \
|
|
253
|
+
--report "Board P&L" --report "Standard Balance Sheet" --set reportPackType=MANAGEMENT
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Rules and refusals
|
|
259
|
+
|
|
260
|
+
| Situation | Result |
|
|
261
|
+
|---|---|
|
|
262
|
+
| Name already used by a template of the same type | Refused before sending. Names are unique per report type, ignoring case and treating space and `_` alike. |
|
|
263
|
+
| The same name on another report type | Allowed |
|
|
264
|
+
| Name blank or only spaces | Refused (upstream accepts whitespace; Clio does not) |
|
|
265
|
+
| `framework` on a type without one | Refused |
|
|
266
|
+
| `framework` GAAP / IND_AS / IFRS_SME on a P&L or cashflow with no layout | `422 NO_TEMPLATE_FOUND`, with a hint to use IAS_1 / IFRS_18 or start from an existing layout |
|
|
267
|
+
| `framework` not one of the five, or on a type without one | Refused before sending, naming the allowed values |
|
|
268
|
+
| A whole replacement layout that leaves out a key the stored one has, or introduces a problem | Refused (the usual cause is a file written from part of a layout) |
|
|
269
|
+
| Update with nothing to change | Refused |
|
|
270
|
+
| Report type or framework on update | Cannot change. Upstream ignores them silently. |
|
|
271
|
+
| Delete the default of a report type | Refused; set another default first (the error names candidates) |
|
|
272
|
+
| Delete a pack's last report | Refused by Clio. Upstream deletes it whenever the pack has any other component (a cover page, say), leaving a pack with no reports. |
|
|
273
|
+
| Delete a report that is in a pack with others | Allowed. It leaves each pack; the answer lists them in `removedFromPacks`. |
|
|
274
|
+
| Delete the default pack | Allowed. The newest remaining pack becomes default, named in `newDefault`. |
|
|
275
|
+
| Set default | Every export and dashboard view of that report changes, org-wide. The answer carries `previousDefault` to undo. Already default: nothing is sent. |
|
|
276
|
+
| A new template straight after create | Can 404 for a moment; the read-back retries briefly |
|
|
277
|
+
| Two edits to the same template at the same moment | The last write wins: upstream has no version check. Clio re-reads just before writing and re-applies its edits on anything changed since it first read, which keeps an earlier change; it cannot protect against one sent in the same instant. |
|
|
278
|
+
| A pack whose layout and report list disagree (written by another client) | A styling edit is refused, naming both lists; pass `packTemplates` to set the reports, and both are rewritten from it |
|
|
279
|
+
| A whole layout on create | Must have every top-level key the report type's default has, and no refused value |
|
|
280
|
+
| `--set path=value` on the CLI | The text is read as the kind of value already stored there: a title stays text, a flag becomes true/false, a list or object is parsed as JSON |
|
|
281
|
+
|
|
282
|
+
### Rendering
|
|
283
|
+
|
|
284
|
+
`download_export` / `clio exports download` take `template` (name or resourceId). Clio checks that the template exists and is of that export's report type before asking for the file.
|
|
285
|
+
|
|
286
|
+
**Upstream answers a template it cannot use with HTTP 200 and an EMPTY body.** That covers a template that doesn't exist, one of another report type, or an unknown `templateName`, and was measured on all 12 exports. If you pass `templateResourceId` directly, Clio turns that empty answer into a named error instead of retrying it.
|
|
287
|
+
|
|
288
|
+
`fixed-assets-register` accepts a template id and ignores it.
|
|
289
|
+
|
|
290
|
+
### Search
|
|
291
|
+
|
|
292
|
+
Upstream's search endpoint answers 500 for four of its five filters, and has no name filter, so `search_organization_report_templates` filters the full list instead. That matches because the list is every template at once. It uses the same filter grammar, plus `templateName`:
|
|
293
|
+
|
|
294
|
+
```jsonc
|
|
295
|
+
{ "filter": { "or": [ { "templateName": { "contains": "board" } }, { "isDefault": { "eq": true }, "reportType": "REPORT_PACK" } ] } }
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
- **Fields:**
|
|
299
|
+
- `resourceId`, `templateName`, `reportType`, `reportCategory` (string operators)
|
|
300
|
+
- `reportTypes` (a list)
|
|
301
|
+
- `isDefault`
|
|
302
|
+
- `and` / `or`
|
|
303
|
+
- **String operators:** `eq` `neq` `in` `contains` `notContains` `likeIn` `reg` `startWith` `notStartWith` `endWith` `isNull`. The `contains` family ignores case.
|
|
304
|
+
- **Sort:** `reportType` `templateName` `isDefault` `resourceId`.
|
|
305
|
+
- An unknown field or operator is refused rather than matching everything.
|
|
@@ -548,7 +548,7 @@ All search endpoints return:
|
|
|
548
548
|
}
|
|
549
549
|
```
|
|
550
550
|
|
|
551
|
-
**Exception**: `GET /organization-report-template` returns its rows under a `reportTemplates` key, not `data` (measured 2026-09-06). Its `/search` sibling returns a **bare array** with no envelope (measured 2026-09-07). The tools normalize the list; read `.data` on both.
|
|
551
|
+
**Exception**: `GET /organization-report-template` returns its rows under a `reportTemplates` key, not `data` (measured 2026-09-06). Its `/search` sibling returns a **bare array** with no envelope (measured 2026-09-07). The tools normalize the list; read `.data` on both. Since 2026-09-24 the report template search tool filters the list itself instead of calling `/search`, because four of the search endpoint's five filters answer 500; it takes the same filter grammar plus `templateName` (`references/report-templates.md`).
|
|
552
552
|
|
|
553
553
|
---
|
|
554
554
|
|
|
@@ -28,7 +28,7 @@ All search endpoints share this identical structure:
|
|
|
28
28
|
|
|
29
29
|
**Response shape**: `{ totalElements, totalPages, data: [...] }`; all search/list endpoints return this flat structure directly (no outer `data` wrapper).
|
|
30
30
|
|
|
31
|
-
> **Exception**: the two halves of this family answer differently upstream. `POST /organization-report-template/search` returns a **bare array** with no envelope; `GET /organization-report-template` returns its rows under a **`reportTemplates`** key with no counts. Measured 2026-09-07: the two halves disagree with each other and with the house style. The tools normalize the list, so you always read `.data`.
|
|
31
|
+
> **Exception**: the two halves of this family answer differently upstream. `POST /organization-report-template/search` returns a **bare array** with no envelope; `GET /organization-report-template` returns its rows under a **`reportTemplates`** key with no counts. Measured 2026-09-07: the two halves disagree with each other and with the house style. The tools normalize the list, so you always read `.data`. The report template search tool does not call `/search` at all: four of its five filters answer 500 upstream (re-measured 2026-09-24), so it filters the full list with the same grammar plus `templateName`. See `references/report-templates.md`.
|
|
32
32
|
|
|
33
33
|
---
|
|
34
34
|
|