jaz-clio 5.40.4 → 5.41.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.40.4
3
+ version: 5.41.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
@@ -181,7 +181,7 @@ The rest of this skill — field names, gotchas, error catalog, dependency order
181
181
  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`**.
182
182
  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"] } } }`.
183
183
  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.
184
- 51. **Filter operator reference** — String: `eq`, `neq`, `contains`, `in` (array, max 100), `likeIn` (array, max 100), `reg` (regex array, max 100), `isNull` (bool). Numeric: `eq`, `gt`, `gte`, `lt`, `lte`, `in`. Date (YYYY-MM-DD): `eq`, `gt`, `gte`, `lt`, `lte`, `between` (exactly 2 values). DateTime (RFC3339): same operators, converted to epoch ms internally. Boolean: `eq`. JSON: `jsonIn`, `jsonNotIn`. Logical: nest with `and`/`or`/`not` objects, or use `andGroup`/`orGroup` arrays (invoices, bills, journals, credit notes).
184
+ 51. **Filter operator reference** — An operator the target type does not declare is now rejected with a 400 naming it, not ignored, so use the exact set. String: `eq`, `neq`, `contains`, `notContains`, `in` (array, max 100), `likeIn` (array, max 100), `reg` (substring array, max 100 — a term match despite the name, not a regex), `startWith`, `notStartWith`, `endWith`, `notEndWith`, `isNull`, `isBlank`, `isNotBlank` (each takes the **string** `"true"`/`"false"`, not a bool). Numeric: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `in`, `inRange`/`notInRange` (exactly 2 values, low then high). Date (YYYY-MM-DD): `eq`, `gt`, `gte`, `lt`, `lte`, `between`/`notBetween` (exactly 2 values), `isNull`, `isNotNull`. DateTime (RFC3339): `eq`, `gt`, `gte`, `lt`, `lte`, `between`, converted to epoch ms internally. Boolean: `eq`. JSON: `jsonIn`, `jsonNotIn`. Logical: `and`/`or` objects everywhere; `andGroup`/`orGroup` arrays on invoices, bills, journals, cashflow transactions, payments, batch payments and the order family (orders, quotes, requests, line items) — **not** on credit notes. `not` exists only on contacts, items, tags, custom fields, tax profiles and nano classifiers; it is **not** available on invoices, bills or journals.
185
185
  52. **Date format asymmetry (CRITICAL)** — Request dates: `YYYY-MM-DD` strings (all create/update and DateExpression filters). Request datetimes: RFC3339 strings (DateTimeExpression filters for `createdAt`, `updatedAt`, `approvedAt`, `submittedAt`). **ALL response dates**: `int64` epoch milliseconds — including `valueDate`, `createdAt`, `updatedAt`, `approvedAt`, `submittedAt`, `matchDate`. Convert: `new Date(epochMs).toISOString().slice(0,10)`. **Timezone convention**: ALL business dates (`valueDate`, `dueDate`, `startDate`, `endDate`, etc.) are in the **organization's timezone** — never UTC. The epoch ms stored in the DB represents the org-local date (no timezone conversion is ever needed). Only audit timestamps (`createdAt`, `updatedAt`, `action_at`) are UTC.
186
186
  53. **Field aliases on create endpoints** — Middleware transparently maps: `issueDate`/`date` → `valueDate` (invoices, bills, credit notes, journals). `name` → `tagName` (tags) or `internalName` (items). `paymentDate` → `valueDate`, `bankAccountResourceId` → `accountResourceId` (payments). `paymentAmount` → `refundAmount`, `paymentMethod` → `refundMethod` (credit note refunds). `accountType` → `classificationType`, `currencyCode` → `currency` (CoA). Canonical names always work; aliases are convenience only.
187
187
  54. **All search/list responses are flat** — every search and list endpoint returns `{ totalElements, totalPages, data: [...] }` directly (no outer `data` wrapper). Access the array via `response.data`, pagination via `response.totalElements`. **Two exceptions**: (a) `GET /bank-accounts` returns a plain array `[{...}]` (see Rule 18), (b) `GET /invoices/:id` returns a flat object `{...}` (no `data` wrapper) — unlike `GET /bills/:id`, `GET /contacts/:id`, `GET /journals/:id` which wrap in `{ data: {...} }`. Normalize the invoice GET response before use.
@@ -548,4 +548,4 @@ When the user wants to OPEN, SEE, or SHARE something in the Jaz dashboard ("open
548
548
  - **jaz-conversion** — Data migration workflows from Xero, QuickBooks, Sage, MYOB, and Excel
549
549
  - **jaz-cli** — CLI command reference, auth, output formats, pagination, and workflow patterns
550
550
 
551
- 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.** Both paths verified separately against production on 2026-08-10 on arap `v10.7.20`: changing an existing one moved it 5.00 to 9.00, and adding one to a payment that had none set it to 7.25 with its account and description. `paymentAmount` and `transactionAmount` were untouched in both. **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. Because update applies again, `PAYMENT_ADJUSTMENT_CANNOT_CHANGE_WHEN_RECONCILED` is reachable through this API again. Anything written between arap `v10.7.19` and `v10.7.20` describing this field as inert on update is stale.
551
+ 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.
@@ -2112,8 +2112,9 @@ Update an existing payment record. All fields optional — only included fields
2112
2112
  "feeTaxVatApplicable": false
2113
2113
  },
2114
2114
  // Cash-leg adjustment: overpayment or rounding. Bank leg only, never AR/AP.
2115
- // Only applies when RECORDING a payment. On update it is inert: add, change
2116
- // and remove all return 200 and do nothing. See rule 160.
2115
+ // On update, add and change APPLY; remove does not (0 rejected, null reads as
2116
+ // omitted). Rejected if the payment is reconciled or an account on its ledger
2117
+ // rows is lock-dated. See rule 160.
2117
2118
  "adjustment": {
2118
2119
  "adjustmentValue": -0.03,
2119
2120
  "adjustmentAccountResourceId": "uuid-rounding-account",
@@ -856,7 +856,8 @@ Two of the seven never reach you through this path: a zero `adjustmentValue` and
856
856
  | `INVALID_PAYMENT_ADJUSTMENT_ACCOUNT` | Account is a control account (AR, AP, the VAT pair, the FX accounts, Retained Earnings, withholding tax), a bank or cash account, deposit-linked, missing, or deleted | Pick an ordinary postable account. Most seeded accounts qualify — Rounding, Other Income, Bank Charges |
857
857
  | `PAYMENT_ADJUSTMENT_MAKES_NET_CASH_INVALID` | Net cash would not remain above zero | Reduce the magnitude of a negative adjustment |
858
858
  | `PAYMENT_ADJUSTMENT_NOT_APPLICABLE_FOR_PAYMENT_METHOD` | Method is `DEBT_WRITE_OFF`, `CLEARING_SETTLEMENT`, `INTER_COMPANY` or `WITHHOLDING_TAX_CERTIFICATE` | Those record a settlement, not a bank movement, so there is no cash leg to adjust. Drop the adjustment |
859
- | `PAYMENT_ADJUSTMENT_CANNOT_CHANGE_WHEN_RECONCILED` | The payment is matched to a bank statement entry and the request would change its adjustment | **Unreachable through this API since 2026-08-10.** The update path never reads the caller's adjustment, so nothing can present a changed value for this rule to reject. Editing the `reference` on a reconciled adjusted payment now succeeds. Still listed because it fires for other clients and will return here when the upstream regression is fixed |
859
+ | `PAYMENT_ADJUSTMENT_CANNOT_CHANGE_WHEN_RECONCILED` | The payment is matched to a bank statement entry and the request would change its adjustment | **Reachable.** The update path reads the caller's adjustment, and since arap v10.7.21 the comparison covers amount, account AND description, so an account-only or description-only move trips it too. Un-reconcile the payment first, or resend the adjustment unchanged. Resending all three values unchanged, or omitting the field, is not a change. See rule 160 |
860
+ | `DELETE_TRANSACTION_VALUE_DATE_CANNOT_BE_EARLIER_THAN_LOCK_DATE` | An account on the payment's ledger rows has a lock date later than the payment `valueDate` | 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. Since arap v10.7.21 the adjustment account is in scope too. Not adjustment-specific: changing the `reference`, `valueDate`, `paymentMethod`, either amount or the bank account is lock-date validated on its own. Clear the lock via `update_account`, or leave the ledger rows alone. Verified on production 2026-08-11 |
860
861
  | `PAYMENT_ADJUSTMENT_NOT_APPLICABLE_FOR_BATCH_PAYMENT` | Batch payments do not support adjustments | Record the adjustment on an individual payment |
861
862
 
862
863
  ---
@@ -87,9 +87,13 @@ When both are present, merged at filter level. Explicit `filter` keys win on con
87
87
  | `neq` | string | `{ "status": { "neq": "DRAFT" } }` | Not equal |
88
88
  | `contains` | string | `{ "reference": { "contains": "INV" } }` | Substring match |
89
89
  | `in` | string[] | `{ "status": { "in": ["ACTIVE", "DRAFT"] } }` | Max 100 values |
90
- | `reg` | string[] | `{ "name": { "reg": ["^Acme.*"] } }` | Regex patterns, max 100 |
91
- | `likeIn` | string[] | `{ "name": { "likeIn": ["Acme%"] } }` | SQL LIKE patterns, max 100 |
92
- | `isNull` | string | `{ "reference": { "isNull": "true" } }` | Null check |
90
+ | `reg` | string[] | `{ "name": { "reg": ["Acme"] } }` | Substring match despite the name, max 100 |
91
+ | `likeIn` | string[] | `{ "name": { "likeIn": ["Acme"] } }` | Substring match, max 100 |
92
+ | `isNull` | string | `{ "reference": { "isNull": "true" } }` | Null check, takes "true"/"false" |
93
+ | `notContains` | string | `{ "reference": { "notContains": "DRAFT" } }` | Substring exclusion |
94
+ | `startWith` / `notStartWith` | string | `{ "reference": { "startWith": "INV-" } }` | Prefix match, case sensitive |
95
+ | `endWith` / `notEndWith` | string | `{ "reference": { "endWith": "-01" } }` | Suffix match, case sensitive |
96
+ | `isBlank` / `isNotBlank` | string | `{ "terms": { "isBlank": "true" } }` | Null-or-empty check, takes "true"/"false" |
93
97
 
94
98
  ### Numeric Operators (`BigDecimalExpression`)
95
99
 
@@ -101,10 +105,13 @@ When both are present, merged at filter level. Explicit `filter` keys win on con
101
105
  | `lt` | number | `{ "totalAmount": { "lt": 10000 } }` |
102
106
  | `lte` | number | `{ "totalAmount": { "lte": 10000 } }` |
103
107
  | `in` | number[] | `{ "totalAmount": { "in": [100, 200, 300] } }` |
108
+ | `neq` | number | `{ "totalAmount": { "neq": 1000 } }` |
109
+ | `inRange` | number[2] | `{ "totalAmount": { "inRange": [100, 1000] } }` |
110
+ | `notInRange` | number[2] | `{ "totalAmount": { "notInRange": [100, 1000] } }` |
104
111
 
105
112
  ### Integer Operators (`IntExpression`)
106
113
 
107
- Same as numeric: `eq`, `gt`, `gte`, `lt`, `lte`, `in` (used by `terms` field on invoices/bills).
114
+ Same as numeric minus `neq`: `eq`, `gt`, `gte`, `lt`, `lte`, `in`, `inRange`, `notInRange` (used by `terms` field on invoices/bills).
108
115
 
109
116
  ### Date Operators (`DateExpression`) — format: `YYYY-MM-DD`
110
117
 
@@ -116,8 +123,10 @@ Same as numeric: `eq`, `gt`, `gte`, `lt`, `lte`, `in` (used by `terms` field on
116
123
  | `lt` | string | `{ "valueDate": { "lt": "2026-12-31" } }` |
117
124
  | `lte` | string | `{ "valueDate": { "lte": "2026-12-31" } }` |
118
125
  | `between` | string[2] | `{ "valueDate": { "between": ["2026-01-01", "2026-03-31"] } }` |
126
+ | `notBetween` | string[2] | `{ "valueDate": { "notBetween": ["2026-01-01", "2026-03-31"] } }` |
127
+ | `isNull` / `isNotNull` | string | `{ "dueDate": { "isNull": "0001-01-01" } }` | takes the placeholder DATE, not "true" |
119
128
 
120
- **CRITICAL**: `between` requires EXACTLY 2 values. All date strings must be `YYYY-MM-DD`.
129
+ **CRITICAL**: `between`, `notBetween`, `inRange` and `notInRange` require EXACTLY 2 values, low then high. Date `isNull`/`isNotNull` take the placeholder date `"0001-01-01"` — unlike the string ones, which take `"true"`/`"false"`. All date strings must be `YYYY-MM-DD`.
121
130
 
122
131
  ### DateTime Operators (`DateTimeExpression`) — format: RFC3339
123
132
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-cli
3
- version: 5.40.4
3
+ version: 5.41.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,
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-conversion
3
- version: 5.40.4
3
+ version: 5.41.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.40.4
3
+ version: 5.41.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.40.4
3
+ version: 5.41.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.40.4
3
+ version: 5.41.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,
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-recipes
3
- version: 5.40.4
3
+ version: 5.41.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