jaz-clio 5.61.3 → 5.63.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.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-api
3
- version: 5.61.3
3
+ version: 5.63.0
4
4
  description: >-
5
5
  Use this skill whenever you call, debug, or review code that touches the Jaz
6
6
  REST API. Covers field names, response shapes, 159 production gotchas, error
@@ -148,7 +148,7 @@ The rest of this skill — field names, gotchas, error catalog, dependency order
148
148
  | Trial balance | `startDate`, `endDate` |
149
149
  | Balance sheet | `primarySnapshotDate` |
150
150
  | P&L | `primarySnapshotDate`, `secondarySnapshotDate` |
151
- | General ledger | `startDate`, `endDate`, `groupBy: "ACCOUNT"` (also `CONTACT`, `TRANSACTION`, `RELATIONSHIP`) |
151
+ | General ledger | `startDate`, `endDate`, `groupBy: "ACCOUNT"` (also `CONTACT`, `TRANSACTION`, `RELATIONSHIP`, `CAPSULE`). Optional `limit` (1-1000; omitted = whole report in one response), `offset` (ROW offset, see Rule 38), `filter.account.resourceId: { in: [...] }` |
152
152
  | Cashflow | `primaryStartDate`, `primaryEndDate` |
153
153
  | Cash balance | `reportDate` |
154
154
  | AR/AP report | `endDate` |
@@ -161,7 +161,7 @@ The rest of this skill — field names, gotchas, error catalog, dependency order
161
161
  37a. **Data exports use simpler field names**: P&L export uses `startDate`/`endDate` (NOT `primarySnapshotDate`). AR/AP export uses `endDate`.
162
162
 
163
163
  ### Pagination
164
- 38. **All list/search endpoints use `limit`/`offset` pagination** NOT `page`/`size`. **`offset` is a 0-indexed PAGE NUMBER, not a row-skip** (offset=1 = second page of `limit` rows). Default limit=100, offset=0. Max limit=1000, max offset=65536. `page`/`size` params are silently ignored. Response shape: `{ totalPages, totalElements, truncated, data: [...] }`. When `truncated: true`, a `_meta: { fetchedRows, maxRows }` field explains why (offset cap or `--max-rows` soft cap default 10,000). Use `--max-rows <n>` to override. Always check `truncated` before assuming the full dataset was returned. **Payload tier (`view`) page-then-drill:** `search_*` and the lean `list_*` tools (invoices, bills, contacts, items, journals, customer/supplier credit notes, sale/purchase orders) return a **compact summary row by default** (`view:"lean"` id + reference/status/date/contact/amount). Search lean to FIND a record, then read it in full via its `get_*`; pass `view:"full"` only when you need whole rows up front (heavier avoid for broad searches). Other collections always return full. CLI defaults to full; use `--view lean`.
164
+ 38. **All list/search endpoints use `limit`/`offset` pagination**: NOT `page`/`size`. **`offset` is a 0-indexed PAGE NUMBER, not a row-skip** (offset=1 = second page of `limit` rows). **Exceptions, where `offset` is a 0-indexed ROW offset (next page = offset + limit):** `POST /generate-reports/general-ledger` and `templated-general-ledger`, the AR/AP details reports (`ar-details-report`, `ap-details-report`, `templated-ar-details-report`, `templated-ap-details-report`), `/purchase-items` (list and search), `GET /organization/currencies/{code}/rates`, and `POST /employees/payouts/search`. Paging one of these by page number re-reads overlapping rows and skips the rest. Default limit=100, offset=0. Max limit=1000, max offset=65536. `page`/`size` params are silently ignored. Response shape: `{ totalPages, totalElements, truncated, data: [...] }`. When `truncated: true`, a `_meta: { fetchedRows, maxRows }` field explains why (offset cap or `--max-rows` soft cap: default 10,000). Use `--max-rows <n>` to override. Always check `truncated` before assuming the full dataset was returned. **Payload tier (`view`): page-then-drill:** `search_*` and the lean `list_*` tools (invoices, bills, contacts, items, journals, customer/supplier credit notes, sale/purchase orders) return a **compact summary row by default** (`view:"lean"`: id + reference/status/date/contact/amount). Search lean to FIND a record, then read it in full via its `get_*`; pass `view:"full"` only when you need whole rows up front (heavier: avoid for broad searches). Other collections always return full. CLI defaults to full; use `--view lean`.
165
165
 
166
166
  ### Other
167
167
  39. **Currency rates use `/organization/currencies/:code/rates`** — enable currencies first via `POST /organization/currencies`, then set rates via `POST /organization/currencies/:code/rates` with body `{ "rate": 0.74, "rateApplicableFrom": "YYYY-MM-DD" }` (see Rule 49 for direction). The older hyphenated `/organization-currencies/...` rate paths still resolve but are **superseded**: treat the nested form as the only supported path. The reference is migrating away from the hyphenated form, so do not rely on it being documented. Cannot set rates for org base currency. Full CRUD: POST (create), GET (list), GET/:id, PUT/:id, DELETE/:id.
@@ -178,7 +178,7 @@ The rest of this skill — field names, gotchas, error catalog, dependency order
178
178
  49. **Currency rate direction: `rate` = functionalToSource (1 base = X foreign)** — POST `rate: 0.74` for a SGD org means 1 SGD = 0.74 USD. **If your data stores rates as "1 USD = 1.35 SGD" (sourceToFunctional), you MUST invert: `rate = 1 / 1.35 = 0.74`.** GET confirms both: `rateFunctionalToSource` (what you POSTed) and `rateSourceToFunctional` (the inverse). **You do not have to do the arithmetic**: `add_currency_rate`, `update_currency_rate` and the `currency` object on the 8 FX create tools (invoice, bill, journal, both credit notes, both cash entries, TTB) accept an optional `rateDirection` (`FUNCTIONAL_TO_SOURCE` | `SOURCE_TO_FUNCTIONAL`) — declare how your figure reads and pass it verbatim. Omitting it always means `FUNCTIONAL_TO_SOURCE`. The two families differ only in where the label is applied: the rate-table tools (plus `bulk_upsert_currency_rates`) send it on the wire and the server applies it, while the 8 FX create tools have no such field, so the client inverts and strips it there. Prefer either to inverting by hand: a wrong inversion is silent and wrong by rate². `clio calc fx-reval` REQUIRES the direction — that calculator historically assumed the opposite convention to the API.
179
179
 
180
180
  ### Search & Filter
181
- 50. **Search endpoint universal pattern** All 32 `POST /*/search` endpoints share identical structure: `{ filter?, sort: { sortBy: ["field"], order: "ASC"|"DESC" }, limit: 1-1000, offset: 0-65536 }`. `offset` is a 0-indexed page number (not row-skip). Sort is REQUIRED when offset is present (even `offset: 0`). Default limit: 100. `sortBy` is always an array on all endpoints (no exceptions). See `references/search-reference.md` for per-endpoint filter/sort fields.
181
+ 50. **Search endpoint universal pattern**: All 32 `POST /*/search` endpoints share identical structure: `{ filter?, sort: { sortBy: ["field"], order: "ASC"|"DESC" }, limit: 1-1000, offset: 0-65536 }`. `offset` is a 0-indexed page number (not row-skip), except on the row-offset endpoints listed in Rule 38. Sort is REQUIRED when offset is present (even `offset: 0`). Default limit: 100. `sortBy` is always an array on all endpoints (no exceptions). See `references/search-reference.md` for per-endpoint filter/sort fields.
182
182
  50a. **`query` field — Jaz search operators** — 14 endpoints accept an optional `query` string alongside `filter`: invoices, bills, customer/supplier credit notes, journals, cashflow-transactions, bank-records, contacts, items, capsules, fixed-assets, scheduled-transactions, chart-of-accounts, tax-profiles. Example: `{ "query": "status:unpaid AND $500+", "limit": 50 }`. Key syntax: amounts (`$500+`, `$100-500`, `amount:>2m`, magnitude suffixes `5k`/`2m`/`1b`), negative (`$-500`), absolute value (`abs:1000+`), dates (`date:this month`, `date:-30d`, `due:overdue`, `submitted:last week`, `lastpayment:-7d`), status/enum (`status:unpaid`, `currency:SGD,USD` — comma = OR), string fields (`customer:acme`, `ref:INV-*` wildcard, `=ref:INV-001` exact, `ref:/\d{4}/` regex), blank checks (`ref:blank`, `tag:!blank`), booleans (`hasattachment:yes`, `customer:yes`), negation (`!status:paid` or `NOT status:void` — **never `-`** for negation), logic (`AND`/`OR` with implicit AND on space), grouping, inline sort (`sort:amount:desc`). Full syntax spec (all fields, aliases, entity field lists, examples): **`references/search-syntax.md`**.
183
183
  50b. **`query` + `filter` merge** — When both are present, they are merged at the filter level. Explicit `filter` keys win on conflict. Use `query` for human-readable shorthand, `filter` for programmatic precision, or combine both: `{ "query": "date:this year", "filter": { "currencyCode": { "in": ["SGD"] } } }`.
184
184
  50c. **`query` error handling** — Unknown field name → `query_not_understood` (400). Bad enum value (e.g. `status:BADVALUE`) → **empty results, no error** (silent miss). Unsupported endpoint → `query_not_supported` (400). Parser unavailable → `query_parse_error` (502). Empty/null/whitespace query → passthrough (ignored). In CLI/MCP: use `--query` / `query` param only on supported entities — unsupported entities have no `--query` flag.
@@ -35,6 +35,10 @@ All GET list endpoints and POST `/search` endpoints use **`limit`/`offset` pagin
35
35
 
36
36
  **IMPORTANT: `offset` is a page number (0-indexed), NOT a row-skip count.** `offset=0` returns the first page, `offset=1` returns the second page, etc. Example: `offset=2, limit=50` returns items 100–149.
37
37
 
38
+ **Exceptions, where `offset` is a 0-indexed ROW offset (next page = offset + limit):** `POST /generate-reports/general-ledger` and `templated-general-ledger`, the AR/AP details reports (`ar-details-report`, `ap-details-report`, `templated-ar-details-report`, `templated-ap-details-report`), `/purchase-items` (list and search), `GET /organization/currencies/{code}/rates`, and `POST /employees/payouts/search`. There, `offset=2, limit=50` returns items 2-51.
39
+
40
+ Paging one of these by page number reads overlapping windows: duplicates in, the rest never reached, while `totalElements` still looks right.
41
+
38
42
  | Property | Value |
39
43
  |----------|-------|
40
44
  | **GET list endpoints** | `?limit=100&offset=0` (query params) |
@@ -1486,10 +1490,15 @@ Both `primarySnapshotDate` and `secondarySnapshotDate` required. NOT `startDate`
1486
1490
 
1487
1491
  ```json
1488
1492
  // Request:
1489
- { "startDate": "2026-01-01", "endDate": "2026-02-28", "groupBy": "ACCOUNT" }
1493
+ { "startDate": "2026-01-01", "endDate": "2026-02-28", "groupBy": "ACCOUNT", "limit": 200, "offset": 0,
1494
+ "filter": { "account": { "resourceId": { "in": ["<accountResourceId>"] } } } }
1490
1495
  ```
1491
1496
 
1492
- `groupBy` is required. Valid values: `"ACCOUNT"`. Uses `startDate`/`endDate` like trial balance.
1497
+ `groupBy` is required. Valid values: `"ACCOUNT"`, `"CONTACT"`, `"TRANSACTION"`, `"RELATIONSHIP"`, `"CAPSULE"`. Uses `startDate`/`endDate` like trial balance.
1498
+
1499
+ **Paging**: `limit` 1-1000; **omit it and the whole report comes back in one response** (630 rows is ~628KB). `offset` is a 0-indexed **ROW** offset, not a page number: the next page is `offset + limit`. Max offset 65536; past that, narrow the dates. Rows sit under `data.searchGeneralLedgersReport.data`, with `totalElements` and `totalPages` beside them.
1500
+
1501
+ **Filter**: `filter.account.resourceId` (StringExpression, e.g. `{ "in": [...] }`, max 100) narrows to accounts; `filter.account.accountType`, `contactResourceId.name`, `businessTransactionType`, `businessTransactionReference`, `description`, `tags` and the amount expressions are also accepted.
1493
1502
 
1494
1503
  ### POST /api/v1/generate-reports/cashflow
1495
1504
 
@@ -1718,7 +1727,7 @@ POST /api/v1/invoices/search
1718
1727
 
1719
1728
  ### Pagination
1720
1729
  - `limit`: max 1000 per page (default 100)
1721
- - `offset`: page number, 0-indexed (max 65536)
1730
+ - `offset`: page number, 0-indexed (max 65536); a ROW offset on the exceptions listed under "Pagination (All List Endpoints)"
1722
1731
  - `sort`: **REQUIRED when `offset` is present** (even `offset: 0`)
1723
1732
  - Response includes `totalElements` and `totalPages`
1724
1733
 
@@ -765,7 +765,7 @@ The same applies on update: `PUT /cash-in-entries/:id` with `saveAsDraft: true`
765
765
 
766
766
  ### "limit must be 1000 or less" (422)
767
767
  **Cause**: Sending `?limit=1001` or higher on a GET endpoint.
768
- **Fix**: Maximum limit is 1000. To fetch all records, paginate with `limit=1000&offset=0`, then `limit=1000&offset=1` (offset is a page number, not row-skip), etc.
768
+ **Fix**: Maximum limit is 1000. To fetch all records, paginate with `limit=1000&offset=0`, then `limit=1000&offset=1` (offset is a page number, not row-skip), etc. On the row-offset endpoints the second page is `offset=1000` instead. Exceptions, where `offset` is a 0-indexed ROW offset (next page = offset + limit): `POST /generate-reports/general-ledger` and `templated-general-ledger`, the AR/AP details reports (`ar-details-report`, `ap-details-report`, `templated-ar-details-report`, `templated-ap-details-report`), `/purchase-items` (list and search), `GET /organization/currencies/{code}/rates`, and `POST /employees/payouts/search`.
769
769
 
770
770
  ### "offset must be 65536 or less" (422)
771
771
  **Cause**: Offset exceeds maximum.
@@ -48,7 +48,7 @@ Transaction fees are added to cash spent (not deducted like invoices), and a pay
48
48
 
49
49
  Pre-invoice / pre-bill documents. Sales pipeline: **Sale Quote** (estimate/quotation) → **Sale Order** → Invoice. Purchase pipeline: **Purchase Request** (requisition) → **Purchase Order** (PO) → Bill.
50
50
 
51
- Key capabilities: a Sale Order links to its source quote via `saleQuoteResourceId` (and a PO to its request via `purchaseRequestResourceId`) — the parent must be **issued** (created with `saveAsDraft:false`, i.e. CREATED/ACTIVE), not a DRAFT. Quotes/requests advance with **accept** (from the issued state), orders with **confirm**. Fulfillment is tracked on the parent via `orderState` (NOT_ORDERED / PARTIALLY_ORDERED / FULLY_INVOICED for quotes, FULLY_BILLED for requests — an order+invoice rollup, arap 2026-06; FULLY_ORDERED retired). Orders convert to invoices/bills via `convert_sale_order_to_invoice` / `convert_purchase_order_to_bill`, and the reverse link is exposed as create-time fields (see orders.md). Delete is draft-only; use void otherwise.
51
+ Key capabilities: a Sale Order links to its source quote via `saleQuoteResourceId` (and a PO to its request via `purchaseRequestResourceId`) — the parent must be **issued** (CREATED/ACTIVE), not a DRAFT: create it with `saveAsDraft:false`, or issue an existing draft in place with the update tool's `isDraftToActive*` flag. Orders made by Jaz Magic start PENDING until the update tool's `isPendingToActive*` flag takes them live. Order updates edit lines by `resourceId` and add any line without one (see orders.md). Quotes/requests advance with **accept** (from the issued state), orders with **confirm**. Fulfillment is tracked on the parent via `orderState` (NOT_ORDERED / PARTIALLY_ORDERED / FULLY_INVOICED for quotes, FULLY_BILLED for requests — an order+invoice rollup, arap 2026-06; FULLY_ORDERED retired). Orders convert to invoices/bills via `convert_sale_order_to_invoice` / `convert_purchase_order_to_bill`, and the reverse link is exposed as create-time fields (see orders.md). Delete is draft-only; use void otherwise.
52
52
 
53
53
  **API**: per entity (`sale-quotes`, `sale-orders`, `purchase-requests`, `purchase-orders`): CRUD `GET/POST/PUT/DELETE`, `POST /…/search`, `POST /…/:id/{accept|confirm}`, `POST /…/:id/void`, `POST /…/:id/fast-fix`, `POST /…/bulk-{accept|confirm|void|delete}`; orders also `POST /…/line-items/bulk-upsert`. Agent surface: `sale_orders` + `purchase_orders` namespaces (create/get/search/update/transition). See `references/orders.md`.
54
54
 
@@ -581,9 +581,9 @@ DELETE → expects "A" (parentEntityResourceId, via /cash-entries/:id)
581
581
  | `size` | NOT SUPPORTED | Silently ignored — use `limit`/`offset` |
582
582
  | `pageSize` | NOT SUPPORTED | Use `limit` |
583
583
  | `per_page` | NOT SUPPORTED | Use `limit` |
584
- | `page_number` | `offset` | **offset IS the page number (0-indexed)**, not a row-skip count. `offset=0` = page 1, `offset=1` = page 2. |
584
+ | `page_number` / (page number) | `offset` (default: 0) | **offset IS the page number (0-indexed)**, not a row-skip count. `offset=0` = page 1, `offset=1` = page 2; `offset=2, limit=50` returns items 100–149. |
585
585
  | (default page size) | `limit` (default: 100) | Query param for GET, JSON body for POST /search |
586
- | (page number) | `offset` (default: 0) | 0-indexed page number. `offset=2, limit=50` returns items 100–149. |
586
+ | (row offset) | `offset` on the exceptions only | Exceptions, where `offset` is a 0-indexed ROW offset (next page = offset + limit): `POST /generate-reports/general-ledger` and `templated-general-ledger`, the AR/AP details reports (`ar-details-report`, `ap-details-report`, `templated-ar-details-report`, `templated-ap-details-report`), `/purchase-items` (list and search), `GET /organization/currencies/{code}/rates`, and `POST /employees/payouts/search`. |
587
587
 
588
588
  **GET list endpoints**: `?limit=100&offset=0` (query params)
589
589
  **POST /search endpoints**: `{ "limit": 100, "offset": 0 }` (JSON body)
@@ -85,9 +85,9 @@ Four entities sharing one shape. Replace `{entity}` with `sale-quotes`, `sale-or
85
85
  |--------|------|-------------|
86
86
  | GET | `/{entity}` | List |
87
87
  | GET | `/{entity}/:resourceId` | Get single |
88
- | POST | `/{entity}` | Create. Sale Order links via `saleQuoteResourceId`; Purchase Order via `purchaseRequestResourceId` (parent must be issued saveAsDraft:false not a draft). |
88
+ | POST | `/{entity}` | Create. Optional `currency { sourceCurrency, exchangeRate? }`. Sale Order links via `saleQuoteResourceId`; Purchase Order via `purchaseRequestResourceId` (parent must be issued, not a DRAFT/VOID, and in the same currency). |
89
89
  | POST | `/{entity}/search` | Advanced search with filters |
90
- | PUT | `/{entity}/:resourceId` | Update |
90
+ | PUT | `/{entity}/:resourceId` | Update. `lineItems` edits by line `resourceId` (`deleted: true` removes; a line without one is ADDED): send every stored line once any names a resourceId. `isDraftToActiveSaleQuote` / `isDraftToActivePurchaseRequest` issue a DRAFT; `isPendingToActiveSaleOrder` / `isPendingToActivePurchaseOrder` take a PENDING order live. |
91
91
  | DELETE | `/{entity}/:resourceId` | Delete (DRAFT only — else use void) |
92
92
  | POST | `/{entity}/:resourceId/void` | Void (cancel) |
93
93
  | POST | `/sale-quotes\|purchase-requests/:resourceId/accept` | Accept (→ ACCEPTED) |
@@ -645,7 +645,7 @@ All 28 `POST /*/search` endpoints accept this filter structure in the POST body:
645
645
 
646
646
  ### Pagination
647
647
 
648
- See `endpoints.md` § "Pagination (All List Endpoints)" for the full spec (limit/offset semantics, defaults, constraints, sort requirement, examples). Key points: `offset` is a 0-indexed page number (not row-skip), default limit=100, max 1000, sort required on POST /search when offset is present. Exception: `GET organization-report-template` returns rows under a `reportTemplates` key with no counts (its /search is conventional); the tools normalize it to the standard envelope.
648
+ See `endpoints.md` § "Pagination (All List Endpoints)" for the full spec (limit/offset semantics, defaults, constraints, sort requirement, examples). Key points: `offset` is a 0-indexed page number (not row-skip); Exceptions, where `offset` is a 0-indexed ROW offset (next page = offset + limit): `POST /generate-reports/general-ledger` and `templated-general-ledger`, the AR/AP details reports (`ar-details-report`, `ap-details-report`, `templated-ar-details-report`, `templated-ap-details-report`), `/purchase-items` (list and search), `GET /organization/currencies/{code}/rates`, and `POST /employees/payouts/search`. Default limit=100, max 1000, sort required on POST /search when offset is present. Exception: `GET organization-report-template` returns rows under a `reportTemplates` key with no counts (its /search is conventional); the tools normalize it to the standard envelope.
649
649
 
650
650
  ### Date Format Asymmetry (CRITICAL)
651
651
 
@@ -19,8 +19,10 @@ Two MCP namespaces wrap these: **`sale_orders`** (Sale Quotes + Sale Orders) and
19
19
  | `PURCHASE_ORDER` | DRAFT (default) / ACTIVE (saveAsDraft:false) | **confirm** → CONFIRMED | VOID | yes (default true) |
20
20
 
21
21
  - **Quotes & Requests use `accept`. Orders use `confirm`.** (`transition_*` enforces this via a documentType × action matrix.)
22
- - **`accept` works on the ISSUED state, not DRAFT** a Sale Quote must be CREATED, a Purchase Request must be ACTIVE. Accepting a DRAFT returns `422 Invalid status` (verified live). There is no exposed DRAFTissued verb: **issue a document by creating it with `saveAsDraft:false`** (update with `saveAsDraft:false` does NOT issue an existing DRAFT verified live).
23
- - **Sale Orders have no draft state** `saveAsDraft` is ignored; created directly as `CREATED`.
22
+ - **`accept` works on the ISSUED state, not DRAFT**: a Sale Quote must be CREATED, a Purchase Request must be ACTIVE. Accepting a DRAFT returns `422 Invalid status` (verified live). **Issue an existing DRAFT in place** with the update tool's flag: `update_sale_order` `isDraftToActiveSaleQuote: true` (CREATED), `update_purchase_order` `isDraftToActivePurchaseRequest: true` (→ ACTIVE). Send every stored line with its `resourceId` in the SAME update: a flag-only update is refused with `422 SALE_LINE_ITEMS_REQUIRED` (verified live 2026-09-23 on a sale quote). It keeps the document's number and line ids (verified live: DRAFT → CREATED, same reference, same line `resourceId`; a line without an account issued fine), so never create a second quote/request to issue one. (Update with `saveAsDraft:false` does NOT issue a DRAFT, verified live.) To create one already issued, pass `saveAsDraft:false` on create.
23
+ - **Sale Orders have no draft state**: `saveAsDraft` is ignored; created directly as `CREATED`.
24
+ - **PENDING orders.** Jaz Magic creates Sale Orders and Purchase Orders as `PENDING`. Take one live with `update_sale_order` `isPendingToActiveSaleOrder: true` / `update_purchase_order` `isPendingToActivePurchaseOrder: true`. Search accepts `status: PENDING`.
25
+ - **Each activation flag belongs to one documentType** (`isDraftToActiveSaleQuote` → SALE_QUOTE, `isPendingToActiveSaleOrder` → SALE_ORDER, `isDraftToActivePurchaseRequest` → PURCHASE_REQUEST, `isPendingToActivePurchaseOrder` → PURCHASE_ORDER). The update tools refuse a flag sent for the other type.
24
26
  - Statuses verified live: SQ create → `DRAFT` (or `CREATED` with `saveAsDraft:false`); SQ accept (from CREATED) → `ACCEPTED`; SO create → `CREATED`; SO confirm → `CONFIRMED`; PR create → `DRAFT` (or `ACTIVE` with `saveAsDraft:false`); PR accept (from ACTIVE) → `ACCEPTED`; PO `saveAsDraft:false` → `ACTIVE`; PO confirm → `CONFIRMED`.
25
27
 
26
28
  ## Linking (quote → order, request → PO)
@@ -30,7 +32,9 @@ Quote→Order / Request→PO linking is a **create-time reference field**:
30
32
  - **Quote → Order**: pass `saleQuoteResourceId` on `create_sale_order` (documentType `SALE_ORDER`).
31
33
  - **Request → PO**: pass `purchaseRequestResourceId` on `create_purchase_order` (documentType `PURCHASE_ORDER`).
32
34
 
33
- **The parent must be ISSUED (not DRAFT/VOID).** A CREATED/ACCEPTED quote (or ACTIVE/ACCEPTED request) is linkable — accept is **optional** (CREATED already links). Linking to a `DRAFT`/`VOID` parent returns `SALE_QUOTE_STATUS_INVALID_FOR_ORDER_CONVERSION` ("must not be in VOID or DRAFT status to create sale order"). The `create_*` tools pre-flight this: for a DRAFT parent the `repair` hint says to issue it (create with `saveAsDraft:false`) **not** to accept it (accept fails on DRAFT). So: to order from a quote/request, create the quote/request with `saveAsDraft:false`.
35
+ **The parent must be ISSUED (not DRAFT/VOID).** A CREATED/ACCEPTED quote (or ACTIVE/ACCEPTED request) is linkable — accept is **optional** (CREATED already links). Linking to a `DRAFT`/`VOID` parent returns `SALE_QUOTE_STATUS_INVALID_FOR_ORDER_CONVERSION` ("must not be in VOID or DRAFT status to create sale order"). The `create_*` tools pre-flight this: for a DRAFT parent the `repair` hint points at issuing THAT document in place (`update_sale_order` `isDraftToActiveSaleQuote: true` / `update_purchase_order` `isDraftToActivePurchaseRequest: true`), **not** at accepting it (accept fails on DRAFT) and not at creating a duplicate.
36
+
37
+ **Currency.** A linked order must use the same currency as its quote/request: a linked order in a different currency is refused.
34
38
 
35
39
  Creating and confirming an order from an issued quote rolls the parent quote's `orderState` up to reflect downstream progress (arap order-status rollup, 2026-06): a confirmed order **not yet invoiced** shows `PARTIALLY_ORDERED` (verified live); the terminal `FULLY_INVOICED` is reached only once every linked order is fully invoiced. Purchase requests mirror this with `FULLY_BILLED`. `orderState` is a **response field**, not a search filter — values: `NOT_ORDERED` / `PARTIALLY_ORDERED` / `FULLY_INVOICED` (quotes) / `FULLY_BILLED` (requests). The older `FULLY_ORDERED` value was retired by this rollup.
36
40
 
@@ -49,12 +53,34 @@ Body: `valueDate` + `dueDate` required; `reference` is required unless you set `
49
53
 
50
54
  ## Fields (create)
51
55
 
52
- Required: `valueDate`. Recommended: `reference` (omit it to take the next number from your org's own series must be unique per org), `contactResourceId`, `lineItems`.
56
+ Required: `valueDate`, and `reference` unless you set `autoReference: true` (takes the next number from your org's own series). Omitting both is refused: omitting `reference` does NOT auto-number. `reference` must be unique per org. Recommended: `contactResourceId`, `lineItems`.
57
+
58
+ - `currency: { sourceCurrency, exchangeRate?, rateDirection? }` (all four documents). Omit for the org's base currency; the currency must be enabled on the org. A linked order must name the same currency as its quote/request. Currency is fixed at create: the update body has no currency field. CLI: `--currency <code>` (+ `--exchange-rate`, `--rate-direction`), same as invoices.
53
59
 
54
60
  - Line items reuse the standard shape: `{ name, quantity, unitPrice, accountResourceId?, taxProfileResourceId?, … }`. `accountResourceId` is **required on each line when the document is not a draft** (i.e. always for Sale Orders; for quotes/requests/POs when `saveAsDraft:false`). The `create_*` tools pre-flight this.
55
61
  - Notes field differs by side: **sales** use `invoiceNotes`, **purchases** use `purchaseNotes`. The `notes` tool param maps to the right one automatically.
56
62
  - Other optional fields: `dueDate`, `terms` (0/7/15/30/45/60), `tag`, `customFields`, `billTo`, `billFrom`, `capsuleResourceId`, `expectedTotal`.
57
63
 
64
+ ## Editing line items (update)
65
+
66
+ `lineItems` on `update_sale_order` / `update_purchase_order` **edits lines, it does not replace them.** The API pairs each sent line with a stored line by its `resourceId`:
67
+
68
+ | Sent line | Effect |
69
+ |-----------|--------|
70
+ | has the `resourceId` of a stored line | that line is edited in place |
71
+ | has a stored `resourceId` + `deleted: true` | that line is removed |
72
+ | has no `resourceId` | a NEW line is added |
73
+ | stored line not sent (when any line names a resourceId) | whole update refused: `422 lineItems must include every line item on this … when any line names a resourceId; N not sent` |
74
+
75
+ Measured live (2026-09-23, one-line sale quote): resending the lines without ids answered **200 and left 2 lines** (the "replace" reading duplicates every line); naming some ids but not all → 422; every id sent (+ `deleted: true` on one) → edited in place.
76
+
77
+ To change lines:
78
+ 1. `get_sale_order` / `get_purchase_order` and copy every `lineItems[].resourceId`.
79
+ 2. Send EVERY stored line with its `resourceId`, in display order: edit fields in place, `deleted: true` to remove, and add new lines without a `resourceId`.
80
+ 3. An edited line keeps only what you send: resend every stored field you want kept (tax profile, `taxVatAdjustment`, `withholdingTax`, `discount`, `unit`, `itemResourceId`). Dropping a stored tax profile or a non-zero `taxVatAdjustment` is refused; dropping `withholdingTax` (or the others) is NOT refused, it is silently lost. A GET returns the account as `organizationAccountResourceId`; send it back as `accountResourceId`.
81
+
82
+ The update tools (and `clio … update --lines`) pre-flight this against the stored document: lines with no `resourceId` at all on a document that already has lines are refused locally (nothing is written) unless `appendLines: true` (CLI `--append-lines`) says adding is the intent; naming some stored lines but not all is refused locally with the missing ids.
83
+
58
84
  ## Delete vs Void
59
85
 
60
86
  - **DELETE is draft-only** (422 on anything non-draft). `transition_* action:DELETE` pre-flights status and returns a `repair` hint to use `VOID` for non-draft records.
@@ -82,12 +108,13 @@ clio supplier-credit-notes download <id> # supplier CN PDF
82
108
 
83
109
  ```bash
84
110
  # 1. Issue the quote (--finalize → saveAsDraft:false → status CREATED). A plain
85
- # draft (no --finalize) stays DRAFT and cannot be linked or accepted.
86
- clio sale-orders create -t quote --finalize --contact <id> --lines '[{"name":"Widget","quantity":2,"unitPrice":50,"accountResourceId":"<acct>"}]' --date 2026-05-30 --json
111
+ # draft (no --finalize) stays DRAFT and cannot be linked or accepted until issued
112
+ # in place: clio sale-orders update <quoteId> -t quote --activate --lines '[{"resourceId":"<lineId>",…}]'
113
+ clio sale-orders create -t quote --finalize --ref Q-1001 --contact <id> --lines '[{"name":"Widget","quantity":2,"unitPrice":50,"accountResourceId":"<acct>"}]' --date 2026-05-30 --json
87
114
  # 2. (Optional) accept it (CREATED → ACCEPTED). A CREATED quote is already linkable.
88
115
  clio sale-orders accept <quoteId> --json
89
116
  # 3. Create the order linked to the issued quote (created as CREATED)
90
- clio sale-orders create -t order --quote <quoteId> --contact <id> --lines '[…]' --date 2026-05-30 --json
117
+ clio sale-orders create -t order --auto-ref --quote <quoteId> --contact <id> --lines '[…]' --date 2026-05-30 --json
91
118
  # 4. Confirm the order (CREATED → CONFIRMED)
92
119
  clio sale-orders confirm <orderId> --json
93
120
  # 5. The parent quote now rolls up to orderState = PARTIALLY_ORDERED (confirmed order, not yet invoiced)
@@ -100,7 +127,7 @@ Purchase side is symmetric: `create -t request --finalize` (→ ACTIVE) → (opt
100
127
 
101
128
  ## Search
102
129
 
103
- `search_sale_orders` / `search_purchase_orders` take `documentType` plus the standard filter set (reference, status, contact, contactResourceId, currencyCode, date range, amount range, tag). The `status` enum is the per-side union (sales: DRAFT/CREATED/ACCEPTED/CONFIRMED/VOID; purchases: DRAFT/ACTIVE/ACCEPTED/CONFIRMED/VOID). For advanced/nested queries (e.g. filter by `saleQuoteResourceId`), pass the raw `filter` object. See `search-reference.md` §24–25 and `search-enums.md` §25–26.
130
+ `search_sale_orders` / `search_purchase_orders` take `documentType` plus the standard filter set (reference, status, contact, contactResourceId, currencyCode, date range, amount range, tag). The `status` enum is the per-side union (sales: DRAFT/PENDING/CREATED/ACCEPTED/CONFIRMED/VOID; purchases: DRAFT/PENDING/ACTIVE/ACCEPTED/CONFIRMED/VOID). For advanced/nested queries (e.g. filter by `saleQuoteResourceId`), pass the raw `filter` object. See `search-reference.md` §24–25 and `search-enums.md` §25–26.
104
131
 
105
132
  Search behaves exactly like the other entities: `sortBy` is an array, `order` is `ASC`/`DESC`, and an `offset` must be paired with a sort. Duplicate `sortBy` values are rejected (`422 — must contain unique values`).
106
133
 
@@ -414,7 +414,7 @@ No enum fields. Plain string filters (no operators): `currencyCode`, `name`, `pu
414
414
 
415
415
  | Field | Valid Values |
416
416
  |-------|-------------|
417
- | `status` | `DRAFT`, `CREATED`, `ACCEPTED`, `CONFIRMED`, `VOID` |
417
+ | `status` | `DRAFT`, `PENDING`, `CREATED`, `ACCEPTED`, `CONFIRMED`, `VOID` |
418
418
  | `currencyCode` | ISO 4217 (see above) |
419
419
  | `terms` | `0`, `7`, `15`, `30`, `45`, `60` (integer — payment terms in days) |
420
420
 
@@ -422,7 +422,7 @@ No enum fields. Plain string filters (no operators): `currencyCode`, `name`, `pu
422
422
  **Date fields**: `valueDate`, `dueDate`, `createdAt` (DateTime), `updatedAt` (DateTime), `approvedAt`
423
423
  **Link field**: `saleQuoteResourceId` (on Sale Orders — the source quote)
424
424
 
425
- > The `status` enum is the union across both sale documents: a **Sale Quote** moves `DRAFT → CREATED → ACCEPTED` (then `VOID`); a **Sale Order** is created as `CREATED → CONFIRMED` (then `VOID`). Fulfillment is reported on the parent quote via `orderState` (`NOT_ORDERED`, `PARTIALLY_ORDERED`, `FULLY_INVOICED`) — an order+invoice rollup (arap, 2026-06; `FULLY_ORDERED` retired), response field only, not a search filter.
425
+ > The `status` enum is the union across both sale documents: a **Sale Quote** moves `DRAFT → CREATED → ACCEPTED` (then `VOID`); a **Sale Order** is created as `CREATED → CONFIRMED` (then `VOID`). An order made by Jaz Magic starts `PENDING` until `update_sale_order` with `isPendingToActiveSaleOrder: true` takes it live. Fulfillment is reported on the parent quote via `orderState` (`NOT_ORDERED`, `PARTIALLY_ORDERED`, `FULLY_INVOICED`) — an order+invoice rollup (arap, 2026-06; `FULLY_ORDERED` retired), response field only, not a search filter.
426
426
 
427
427
  ---
428
428
 
@@ -430,7 +430,7 @@ No enum fields. Plain string filters (no operators): `currencyCode`, `name`, `pu
430
430
 
431
431
  | Field | Valid Values |
432
432
  |-------|-------------|
433
- | `status` | `DRAFT`, `ACTIVE`, `ACCEPTED`, `CONFIRMED`, `VOID` |
433
+ | `status` | `DRAFT`, `PENDING`, `ACTIVE`, `ACCEPTED`, `CONFIRMED`, `VOID` |
434
434
  | `currencyCode` | ISO 4217 |
435
435
  | `terms` | `0`, `7`, `15`, `30`, `45`, `60` |
436
436
 
@@ -438,7 +438,7 @@ No enum fields. Plain string filters (no operators): `currencyCode`, `name`, `pu
438
438
  **Date fields**: `valueDate`, `dueDate`, `createdAt` (DateTime), `updatedAt` (DateTime), `approvedAt`
439
439
  **Link field**: `purchaseRequestResourceId` (on Purchase Orders — the source request)
440
440
 
441
- > The `status` enum is the union across both purchase documents: a **Purchase Request** moves `DRAFT → ACTIVE → ACCEPTED` (then `VOID`); a **Purchase Order** moves `DRAFT → ACTIVE → CONFIRMED` (then `VOID`). Fulfillment is reported on the parent request via `orderState` (`NOT_ORDERED`, `PARTIALLY_ORDERED`, `FULLY_BILLED`) — an order+invoice rollup (arap, 2026-06; `FULLY_ORDERED` retired), response field only, not a search filter.
441
+ > The `status` enum is the union across both purchase documents: a **Purchase Request** moves `DRAFT → ACTIVE → ACCEPTED` (then `VOID`); a **Purchase Order** moves `DRAFT → ACTIVE → CONFIRMED` (then `VOID`). An order made by Jaz Magic starts `PENDING` until `update_purchase_order` with `isPendingToActivePurchaseOrder: true` takes it live. Fulfillment is reported on the parent request via `orderState` (`NOT_ORDERED`, `PARTIALLY_ORDERED`, `FULLY_BILLED`) — an order+invoice rollup (arap, 2026-06; `FULLY_ORDERED` retired), response field only, not a search filter.
442
442
 
443
443
  ---
444
444
 
@@ -24,7 +24,7 @@ All search endpoints share this identical structure:
24
24
  | `filter` | object | No | Per-endpoint fields (see tables below) |
25
25
  | `sort` | object | **Yes if `offset` is present** | `sortBy`: array of field names; `order`: `"ASC"` or `"DESC"` |
26
26
  | `limit` | int | No | Default: 100, min: 1, max: 1000 |
27
- | `offset` | int | No | **Page number** (0-indexed, not row-skip). Default: 0, min: 0, max: 65536 |
27
+ | `offset` | int | No | **Page number** (0-indexed, not row-skip). Default: 0, min: 0, max: 65536. Exceptions, where `offset` is a 0-indexed ROW offset (next page = offset + limit): `POST /generate-reports/general-ledger` and `templated-general-ledger`, the AR/AP details reports (`ar-details-report`, `ap-details-report`, `templated-ar-details-report`, `templated-ap-details-report`), `/purchase-items` (list and search), `GET /organization/currencies/{code}/rates`, and `POST /employees/payouts/search`. |
28
28
 
29
29
  **Response shape**: `{ totalElements, totalPages, data: [...] }` — all search/list endpoints return this flat structure directly (no outer `data` wrapper).
30
30
 
@@ -723,7 +723,7 @@ Also covers `POST /api/v1/sale-quotes/search` — same filter shape (`SaleOrderF
723
723
  |-------|------|-------|
724
724
  | `resourceId` | StringExpression | |
725
725
  | `reference` | StringExpression | Order/quote number |
726
- | `status` | StringExpression | DRAFT, CREATED, ACCEPTED, CONFIRMED, VOID |
726
+ | `status` | StringExpression | DRAFT, PENDING, CREATED, ACCEPTED, CONFIRMED, VOID |
727
727
  | `contactResourceId` | StringExpression | |
728
728
  | `contact` | ContactNestedFilter | Nested: name, resourceId, status |
729
729
  | `saleQuoteResourceId` | StringExpression | (Sale Orders) source quote link |
@@ -755,7 +755,7 @@ Also covers `POST /api/v1/purchase-requests/search` — same filter shape (`Purc
755
755
  |-------|------|-------|
756
756
  | `resourceId` | StringExpression | |
757
757
  | `reference` | StringExpression | Order/request number |
758
- | `status` | StringExpression | DRAFT, ACTIVE, ACCEPTED, CONFIRMED, VOID |
758
+ | `status` | StringExpression | DRAFT, PENDING, ACTIVE, ACCEPTED, CONFIRMED, VOID |
759
759
  | `contactResourceId` | StringExpression | Supplier |
760
760
  | `contact` | ContactNestedFilter | Nested: name, resourceId, status |
761
761
  | `purchaseRequestResourceId` | StringExpression | (Purchase Orders) source request link |
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-cli
3
- version: 5.61.3
3
+ version: 5.63.0
4
4
  description: >-
5
5
  Use this skill when running Clio CLI commands, building shell scripts with
6
6
  Clio, debugging auth issues, understanding --json output, paginating results,
@@ -400,7 +400,7 @@ See `references/common-workflows.md` for end-to-end multi-command patterns.
400
400
  1. **Create returns only {resourceId}.** Always `get` afterward for full data.
401
401
  2. **Line-item accounts don't fuzzy-resolve.** Use UUID or exact name.
402
402
  3. **Cash entries finalize immediately.** Unlike invoices which default to draft.
403
- 4. **--offset is page number (0-indexed), not row count.**
403
+ 4. **--offset is page number (0-indexed), not row count.** Exceptions, where `--offset` is a ROW offset (next page = offset + limit): `purchase-items list/search`, `currency-rates list`, `claims payouts` and `reports generate general-ledger`. Each command's `--offset` help says which it is, and `--all` pages both kinds correctly.
404
404
  5. **Explicit organization selection wins.** `--org` uses the selected OAuth organization or saved key profile even when `JAZ_API_KEY` is set.
405
405
 
406
406
  See [references/agent-gotchas.md](./references/agent-gotchas.md) for the full list of 19 critical gotchas. See [references/output-shapes.md](./references/output-shapes.md) for `--json` output structures. See [references/error-recovery.md](./references/error-recovery.md) for 30+ error patterns with fixes.
@@ -11,7 +11,7 @@
11
11
 
12
12
  3. **Create responses are minimal.** All create commands return only `{ "resourceId": "uuid" }`. To get the full entity (status, amounts, line items), run `clio <entity> get <id> --json` afterward.
13
13
 
14
- 4. **--offset is page number (0-indexed), not row skip count.** `--offset 0 --limit 100` = rows 1-100. `--offset 1 --limit 100` = rows 101-200. This is not the same as SQL OFFSET.
14
+ 4. **--offset is page number (0-indexed), not row skip count.** `--offset 0 --limit 100` = rows 1-100. `--offset 1 --limit 100` = rows 101-200. This is not the same as SQL OFFSET. Exceptions, where `--offset` is a ROW offset (next page = offset + limit): `purchase-items list/search`, `currency-rates list`, `claims payouts` and `reports generate general-ledger`. Each command's `--offset` help says which it is, and `--all` pages both kinds correctly.
15
15
 
16
16
  5. **--all caps at 1,000 rows by default** (lowered from 10,000 in 2026-04). For large orgs, pass `--max-rows 50000` explicitly. The CLI auto-paginates with concurrent requests and **stops fetching once `--max-rows` is reached** (early-stop, not slice-after — the previous behavior would pull every page and discard the excess, causing minute-long hangs on busy sandboxes).
17
17
 
@@ -515,8 +515,11 @@ Types: `trial-balance`, `balance-sheet`, `profit-loss`, `cashflow`, `aged-ar`, `
515
515
  | `--from` | Start date (P&L, cashflow, equity, recon) |
516
516
  | `--to` | End/snapshot date |
517
517
  | `--currency` | Currency code override |
518
- | `--group-by` | GL: ACCOUNT, CONTACT, TRANSACTION, RELATIONSHIP · FA summary: ACCOUNT, TYPE, CATEGORY, STATUS |
518
+ | `--group-by` | GL: ACCOUNT, CONTACT, TRANSACTION, RELATIONSHIP, CAPSULE · FA summary: ACCOUNT, TYPE, CATEGORY, STATUS |
519
519
  | `--bank-account` | Bank account ID (for bank-recon-*) |
520
+ | `--limit` / `--offset` | GL only: rows per response (1-1000; omitted = whole report) and a ROW offset (next page = offset + limit) |
521
+ | `--all` / `--max-rows` | GL only: fetch every row in row-offset pages of `--limit` (default 1000), optionally stopping at `--max-rows` |
522
+ | `--account <id...>` | GL only: only these accounts (repeatable, max 100) |
520
523
 
521
524
  Also: `clio reports pdf` — generate PDF from a message/document.
522
525
 
@@ -69,7 +69,7 @@ Use in search: `clio invoices search --status UNPAID --json`
69
69
  1. **Create returns minimal response** — only `{ "resourceId": "uuid" }`. Run `clio invoices get <id> --json` for full data.
70
70
  2. **Line-item accounts don't fuzzy-resolve** — `--lines` JSON requires exact account name or UUID. Top-level `--account` fuzzy-resolves.
71
71
  3. **Transaction creates default to draft** — invoices, bills, journals, credit notes and orders. Use `--finalize` to create as finalized (UNPAID for invoices, ACTIVE for journals). Cash entries and cash transfers are the exception: they post ACTIVE and accept no `--finalize`. Master data has no draft state either: contacts, items, accounts, tags, tax profiles and the rest are created live.
72
- 4. **`--offset` is page number (0-indexed)** not row skip count. offset=0 + limit=100 = page 1.
72
+ 4. **`--offset` is page number (0-indexed)**: not row skip count. offset=0 + limit=100 = page 1. Exceptions, where `--offset` is a ROW offset (next page = offset + limit): `purchase-items list/search`, `currency-rates list`, `claims payouts` and `reports generate general-ledger`. Each command's `--offset` help says which it is, and `--all` pages both kinds correctly.
73
73
  5. **`customer` is boolean** — `--customer true`, not `--customer "Acme"`.
74
74
  6. **Dates are YYYY-MM-DD** — org-local timezone. API returns epoch ms but CLI formats them.
75
75
  7. **`--all` caps at 1,000 rows** — lowered from 10,000 in 2026-04 (DEFAULT_MAX_ROWS in commands/pagination.ts). Pass `--max-rows 50000` for larger datasets, and check `truncated` in the response before treating a page as complete.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-conversion
3
- version: 5.61.3
3
+ version: 5.63.0
4
4
  description: >-
5
5
  Use this skill when migrating accounting data into Jaz — importing from Xero,
6
6
  QuickBooks, Sage, MYOB, or Excel exports. Covers the full conversion pipeline:
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-kit
3
- version: 5.61.3
3
+ version: 5.63.0
4
4
  description: >-
5
5
  Use this skill when an accountant, bookkeeper, or owner is running real books
6
6
  in Jaz across one or more organizations from the terminal — setting up a
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-pseudo-sql
3
- version: 5.61.3
3
+ version: 5.63.0
4
4
  description: >-
5
5
  Use this skill when answering ad-hoc data questions that aren't covered by
6
6
  download_export (canonical reports — anomaly, audit, aging, P&L, BS, GL,
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-jobs
3
- version: 5.61.3
3
+ version: 5.63.0
4
4
  description: >-
5
5
  Use this skill for recurring accounting workflows — month/quarter/year-end
6
6
  close, bank reconciliation, GST/VAT filing, payment runs, credit control,
@@ -40,7 +40,7 @@
40
40
  - Runs after year-end-close; it's the bridge between year-end close and statutory filing.
41
41
  - Org inputs this job needs (confirm with the user when not already on file): whether a statutory audit is required, the tax jurisdiction (`SG` | `PH`), and the FY-end — these scope the deliverables.
42
42
  - Sibling jobs: `year-end-close.md` (must complete BEFORE this job — audit-prep assumes books are closed), plus the SG Form C-S / PH ITR statutory filing that consumes the pack this job produces (see step 13).
43
- - API rules: `jaz-api/SKILL.md` rule 36 (`endDate` not `startDate` for AR/AP point-in-time reports), rule 38 (pagination for `general-ledger`), rule 52 (response dates are epoch ms).
43
+ - API rules: `jaz-api/SKILL.md` rule 36 (`endDate` not `startDate` for AR/AP point-in-time reports), rule 38 (`general-ledger` pages by ROW offset, not page number), rule 52 (response dates are epoch ms).
44
44
 
45
45
  ---
46
46
 
@@ -96,7 +96,7 @@ Cashflow classifies into Operating / Investing / Financing per IAS 7. Equity Mov
96
96
  generate_general_ledger(period_start: '2025-01-01', period_end: '2025-12-31', groupBy: 'ACCOUNT')
97
97
  ```
98
98
 
99
- Per `jaz-api/SKILL.md` rule 38, paginate via `offset` if `totalElements > <page-size>`. Keep the full GL for the pack the auditor will sample-test from this.
99
+ The tool returns 50 rows per call by default. Page it at `limit` 50-100: a GL row is about 1KB, so larger pages get cut to the tool's result cap and come back with `_truncated`. The offset is a ROW offset (`jaz-api/SKILL.md` rule 38): step it by the rows the call actually returned, which is what `_paging.nextOffset` holds, and stop when a result carries no `_paging`. Keep the full GL for the pack, since the auditor will sample-test from it. From the CLI, `clio reports generate general-ledger --all` pages it for you.
100
100
 
101
101
  ## Step 6 — AR / AP aging
102
102
 
@@ -186,10 +186,15 @@ POST /api/v1/generate-reports/general-ledger
186
186
  {
187
187
  "startDate": "2025-01-01",
188
188
  "endDate": "2025-12-31",
189
- "groupBy": "ACCOUNT"
189
+ "groupBy": "ACCOUNT",
190
+ "filter": { "account": { "resourceId": { "in": ["<accountResourceId>", "..."] } } },
191
+ "limit": 1000,
192
+ "offset": 0
190
193
  }
191
194
  ```
192
195
 
196
+ `filter.account.resourceId.in` (max 100 ids) keeps the pull to the accounts identified in Phase 2.
197
+
193
198
  **Target these account categories:**
194
199
 
195
200
  | GL Account Keywords | Add-Back Category | What to Look For |
@@ -205,7 +210,7 @@ POST /api/v1/generate-reports/general-ledger
205
210
  | "Unrealized", "FX", "Foreign Exchange" | `unrealizedFxLoss` / `deductions.unrealizedFxGain` | Separate gains from losses |
206
211
  | "Dividend Income" | `exemptDividends` | SG one-tier dividends only |
207
212
 
208
- **Tip:** For large GLs, you may need to paginate. Check the response for pagination metadata and follow up with offset/limit parameters.
213
+ **Paging:** the GL reads `offset` as a **ROW offset**, unlike the search endpoints below. Read `data.searchGeneralLedgersReport.totalElements`; while rows remain, request `offset + limit` next (0, 1000, 2000, ...). Omitting `limit` returns the whole report in one response.
209
214
 
210
215
  ### Step 10: Search for capital items on P&L
211
216
 
@@ -294,8 +299,9 @@ All search endpoints support pagination. The standard pattern:
294
299
  ```
295
300
 
296
301
  **Check the response metadata:**
297
- - `offset` is a **page number** (0-indexed), NOT a row-skip count. Increment `offset` by 1 for each subsequent page.
302
+ - On the search endpoints, `offset` is a **page number** (0-indexed), NOT a row-skip count. Increment `offset` by 1 for each subsequent page.
298
303
  - Example: `offset=0` returns items 0–999, `offset=1` returns items 1000–1999, etc.
304
+ - **The general ledger is the exception**: its `offset` is a ROW offset, so the second page of 1000 is `offset=1000` (see Phase 5).
299
305
  - Always aggregate totals across all pages before mapping to input fields
300
306
  - Do not assume a single page contains all results
301
307
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-recipes
3
- version: 5.61.3
3
+ version: 5.63.0
4
4
  description: >-
5
5
  Use this skill when modeling complex multi-step accounting transactions —
6
6
  anything that spans multiple periods, involves changing amounts, or requires
@@ -63,7 +63,7 @@ The recipe engine uses capsules automatically. But capsules also enable advanced
63
63
 
64
64
  2. **Use Capsule Types as the search axis, not Capsule Name.** Capsule names are unique per instance ("FY2025 Office Insurance"); types are reusable ("Prepaid Expenses"). `search_capsules(filter: {status: {eq: 'ACTIVE'}}) (capsule type is not filterable — see `jobs/references/building-blocks.md` § Filter limits)` returns ALL prepaid capsules across history.
65
65
 
66
- 3. **Tie capsule entries back for the auditor.** `generate_general_ledger` does NOT group by capsule -- `groupBy` is ACCOUNT, CONTACT, TRANSACTION or RELATIONSHIP, and `CAPSULE` returns 422 (verified live 2026-09-01). Auditor sample-test: pick 3 capsules per type, read each with `get_capsule` to get its transaction resource ids, then pull those documents (bills, invoices, journals) directly. Same evidence, one more hop.
66
+ 3. **Tie capsule entries back for the auditor.** `generate_general_ledger(groupBy: 'CAPSULE')` groups the period's GL rows by capsule, so each capsule's full lifecycle reads as one block. Auditor sample-test: pick 3 capsules per type from that report and pull the underlying documents (bills, invoices, journals) by their resource ids.
67
67
 
68
68
  **MCP tool shape:**
69
69
 
@@ -91,7 +91,7 @@ search_capsules(filter: {status: {eq: 'ACTIVE'}}) # capsule type is not filtera
91
91
  # All open loan capsules — feed into year-end-close.md Y6 reclassification
92
92
  **STOP — not selectable by filter.** Journals carry no capsule or fixed-asset link in either direction (`JournalFilter` declares neither; a journal row has no such field even at `view: 'full'`; `GET /capsules/{id}` returns only a `totalTransactions` count — measured 2026-09-07). A date+status search returns every matching DRAFT in the org, so it must never feed `bulk_update_journals` or `delete_journal`. Surface the capsule and its expected count to the practitioner and let them identify the journals.
93
93
  # Full GL for one capsule — the auditor's view
94
- get_capsule(resourceId) # returns the capsule's transactions; GL cannot group by capsule
94
+ generate_general_ledger(startDate, endDate, groupBy: 'CAPSULE') # GL rows grouped by capsule
95
95
  # Period activity grouped by capsule — the practitioner's view
96
96
  ```
97
97
 
@@ -241,7 +241,7 @@ Apply enrichments to recipe transactions for richer reporting and record-keeping
241
241
  }
242
242
  ```
243
243
  4. **Supported transaction types**: invoices, bills, credit notes, journals, cash entries
244
- 5. **Reports**: General Ledger cannot group by capsule (`groupBy` accepts ACCOUNT, CONTACT, TRANSACTION, RELATIONSHIP only). Read the capsule with `get_capsule` and pull its transactions.
244
+ 5. **Reports**: `generate_general_ledger` groups by ACCOUNT, CONTACT, TRANSACTION, RELATIONSHIP or CAPSULE.
245
245
 
246
246
  ---
247
247
 
@@ -20,7 +20,7 @@ No API key needed: `describe_capabilities`, `plan_recipe`, `search_help_center`
20
20
  2. **Transaction dates are `valueDate`** (YYYY-MM-DD) — never `issueDate` / `invoiceDate` / `date`.
21
21
  3. **Line item text field is `name`** — never `description`.
22
22
  4. **`saveAsDraft` defaults `false`** at the API; CLI/MCP create-tools default `true`. Set explicitly when the user says "finalize".
23
- 5. **Pagination uses `limit` / `offset`** `offset` is a 0-indexed page number (offset=1 = second page), not a row-skip. Sort is required when `offset` is set.
23
+ 5. **Pagination uses `limit` / `offset`**: `offset` is a 0-indexed page number (offset=1 = second page), not a row-skip. Exceptions, where `offset` is a 0-indexed ROW offset (next page = offset + limit): the general ledger (and templated), the AR/AP details reports (and templated), purchase items (list and search), currency rates, and employee payouts search. Sort is required when `offset` is set.
24
24
  6. **Create responses return `{ resourceId }` only** — re-GET to load the full entity.
25
25
 
26
26
  ## Transactions — never hand-construct journals for IFRS