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.
- package/assets/skills/api/SKILL.md +3 -3
- package/assets/skills/api/references/endpoints.md +3 -2
- package/assets/skills/api/references/errors.md +2 -1
- package/assets/skills/api/references/search-reference.md +14 -5
- package/assets/skills/cli/SKILL.md +1 -1
- 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/transaction-recipes/SKILL.md +1 -1
- package/cli.mjs +9 -9
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: jaz-api
|
|
3
|
-
version: 5.
|
|
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` (
|
|
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.**
|
|
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
|
-
//
|
|
2116
|
-
//
|
|
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 | **
|
|
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": ["
|
|
91
|
-
| `likeIn` | string[] | `{ "name": { "likeIn": ["Acme
|
|
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
|
|
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`
|
|
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
|
|