jaz-clio 5.34.0 → 5.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -52,6 +52,29 @@ clio invoices search --query 'status:unpaid AND $500+' # structured per-entity s
52
52
 
53
53
  66 command groups, 16 report types, 13 calculators, 12 job playbooks. Every command takes `--json`. Run `clio --help` for the full list.
54
54
 
55
+ ### Foreign currency
56
+
57
+ Rates read **base→source**: `1` unit of your organization's base currency `= N` units of the
58
+ foreign one. Whether that matches how the rate was quoted to you depends on which side your base
59
+ currency is on — an SGD-base org quoting "1 USD = 1.35 SGD" has it backwards and needs `0.74`, while
60
+ a USD-base org quoting "1 USD = 56.5 PHP" already has it right and sends `56.5` unchanged.
61
+
62
+ You do not have to flip it. Pass the number as you have it and say which way it reads:
63
+
64
+ ```bash
65
+ # "1 USD = 1.35 SGD", from a bank statement, on an SGD-base org
66
+ clio invoices create --currency USD --exchange-rate 1.35 \
67
+ --rate-direction SOURCE_TO_FUNCTIONAL ...
68
+
69
+ # 0.74, straight out of clio currency-rates list
70
+ clio invoices create --currency USD --exchange-rate 0.74 ...
71
+ ```
72
+
73
+ Omit `--exchange-rate` entirely to use the organization's stored rate, or the platform daily rate
74
+ when none is set. `clio calc fx-reval` requires `--rate-direction` explicitly — that calculator
75
+ takes rates in the opposite direction to the API, and guessing would silently change a number
76
+ that ends up in a journal.
77
+
55
78
  ## MCP server
56
79
 
57
80
  358 tools for any AI agent that speaks MCP. Runs locally: no cloud, no ports.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-api
3
- version: 5.34.0
3
+ version: 5.35.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, 158 production gotchas, error
@@ -106,8 +106,8 @@ The rest of this skill — field names, gotchas, error catalog, dependency order
106
106
 
107
107
  ### Journals & Cash
108
108
  23. **Journals use `journalEntries`** with `amount` + `type: "DEBIT"|"CREDIT"` — NOT `debit`/`credit` number fields.
109
- 24. **Journals support multi-currency via `currency` object** — same format as invoices/bills: `"currency": { "sourceCurrency": "USD" }` (auto-fetch platform rate) or `"currency": { "sourceCurrency": "USD", "exchangeRate": 1.35 }` (custom rate). Must be enabled for the org. Omit for base currency. Three restrictions apply to foreign currency journals: (a) **no controlled accounts** — accounts with `controlFlag` (AR, AP) are off-limits (use invoices/bills instead), (b) **no FX accounts** — FX Unrealized Gain/Loss/Rounding are system-managed, (c) **bank accounts must match** — can only post to bank accounts in the same currency as the journal (e.g., USD journal → USD bank account only, not SGD bank account). All other non-controlled accounts (expenses, revenue, assets, liabilities) are available.
110
- 25. **`currency` object is the SAME everywhere** — invoices, bills, credit notes, AND journals all use `currency: { sourceCurrency: "USD", exchangeRate?: number }`. Never use `currencyCode: "USD"` (silently ignored on invoices/bills) or `currency: "USD"` (string — causes 400 on invoices/bills).
109
+ 24. **Journals support multi-currency via `currency` object** — same format as invoices/bills: `"currency": { "sourceCurrency": "USD" }` (auto-fetch platform rate) or `"currency": { "sourceCurrency": "USD", "exchangeRate": 0.74 }` (custom rate). Must be enabled for the org. Omit for base currency. **Direction**: `exchangeRate` is functionalToSource (1 org-base unit = N `sourceCurrency`) — usually the inverse of a quoted rate. Pass your figure as-is with `rateDirection` rather than inverting by hand; Rule 49. Three restrictions apply to foreign currency journals: (a) **no controlled accounts** — accounts with `controlFlag` (AR, AP) are off-limits (use invoices/bills instead), (b) **no FX accounts** — FX Unrealized Gain/Loss/Rounding are system-managed, (c) **bank accounts must match** — can only post to bank accounts in the same currency as the journal (e.g., USD journal → USD bank account only, not SGD bank account). All other non-controlled accounts (expenses, revenue, assets, liabilities) are available.
110
+ 25. **`currency` object is the SAME everywhere** — invoices, bills, credit notes, AND journals all use `currency: { sourceCurrency: "USD", exchangeRate?: number, rateDirection?: "FUNCTIONAL_TO_SOURCE" | "SOURCE_TO_FUNCTIONAL" }`. **Direction**: `exchangeRate` is functionalToSource (1 org-base unit = N `sourceCurrency`) — usually the inverse of a quoted rate. Pass your figure as-is with `rateDirection` rather than inverting by hand; Rule 49. Never use `currencyCode: "USD"` (silently ignored on invoices/bills) or `currency: "USD"` (string — causes 400 on invoices/bills).
111
111
  26. **Cash entries use `accountResourceId`** at top level for the BANK account + `lines` array for offsets.
112
112
 
113
113
  ### Credit Notes & Refunds
@@ -164,8 +164,8 @@ The rest of this skill — field names, gotchas, error catalog, dependency order
164
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`.
165
165
 
166
166
  ### Other
167
- 39. **Currency rates use `/organization-currencies/:code/rates`** — note the HYPHENATED path (NOT `/organization/currencies`). 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). Cannot set rates for org base currency. Full CRUD: POST (create), GET (list), GET/:id, PUT/:id, DELETE/:id.
168
- 40. **FX invoices/bills MUST use `currency` object** — `currencyCode: "USD"` (string) is **silently ignored** (transaction created in base currency!). Use `currency: { sourceCurrency: "USD" }` to auto-fetch platform rate (ECB/FRANKFURTER), or `currency: { sourceCurrency: "USD", exchangeRate: 1.35 }` for a custom rate. Rate hierarchy: org rate → platform/ECB → transaction-level.
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 marked **deprecated** in the OpenAPI spec — prefer the nested form. Cannot set rates for org base currency. Full CRUD: POST (create), GET (list), GET/:id, PUT/:id, DELETE/:id.
168
+ 40. **FX invoices/bills MUST use `currency` object** — `currencyCode: "USD"` (string) is **silently ignored** (transaction created in base currency!). Use `currency: { sourceCurrency: "USD" }` to auto-fetch platform rate (ECB/FRANKFURTER), or `currency: { sourceCurrency: "USD", exchangeRate: 0.74 }` for a custom rate. Rate hierarchy: org rate → platform/ECB → transaction-level. **Direction**: `exchangeRate` is functionalToSource (1 org-base unit = N `sourceCurrency`) — usually the inverse of a quoted rate. Pass your figure as-is with `rateDirection` rather than inverting by hand; Rule 49.
169
169
  41. **Invoice GET uses `organizationAccountResourceId`** for line item accounts — POST uses `accountResourceId`. Request-side aliases resolve `issueDate` → `valueDate`, `bankAccountResourceId` → `accountResourceId`, etc.
170
170
  42. **Scheduler GET returns `interval`** — POST uses `repeat`. (Response-side asymmetry remains.)
171
171
  43. **Search sort is an object** — `{ sort: { sortBy: ["valueDate"], order: "DESC" } }`. Required when `offset` is present (even `offset: 0`).
@@ -174,7 +174,7 @@ The rest of this skill — field names, gotchas, error catalog, dependency order
174
174
  46. **Known API bugs (500s)**: Contact groups PUT (nil pointer on search response), custom fields PUT (dangling stack pointers in mapping), capsules POST (upstream returns nil), catalogs POST, inventory balances by status GET (`/inventory-balances/:status`, missing `c.Bind`) — all return 500.
175
175
  47. **Non-existent endpoints**: `POST /deposits`, `POST /inventory/adjustments`, `GET /payments` (list), and `POST /payments/search` return 404 — these endpoints are not implemented. To list/search payments, use `POST /cashflow-transactions/search` (the unified transaction ledger — see Rule 63).
176
176
  48. **Attachments — full CRUD**: **Add**: `POST /:type/:id/attachments` (multipart, `file` field, `application/pdf` or `image/*` — NOT `text/plain`). **List**: `GET /:type/:id/attachments`. **Delete**: `DELETE /:type/:id/attachments/:attachmentResourceId` (HTTP 200). CLI: `clio attachments add --file <path>` or `--url <url>`, `clio attachments list`, `clio attachments delete <attachmentResourceId>`. **Response shape is non-standard**: `{ reference, resourceId, attachments: [{fileName, fileType, fileId, attachmentResourceId}] }` — NOT `{ data: [...] }`. The attachment ID field is `attachmentResourceId` (not `resourceId`).
177
- 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).
177
+ 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`, the same enum `bulk_upsert_currency_rates` sends on the wire) — declare how your figure reads and pass it verbatim; the client inverts and strips the label, which is never transmitted. Prefer this 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.
178
178
 
179
179
  ### Search & Filter
180
180
  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.
@@ -532,7 +532,7 @@ When the user wants to OPEN, SEE, or SHARE something in the Jaz dashboard ("open
532
532
  - **Bank records**: Create via JSON `POST /bank-records/:id` or multipart `POST /magic/importBankStatementFromAttachment`. Search via `POST /bank-records/:id/search` with filters (valueDate, status, description, extContactName, netAmount, extReference).
533
533
  - **Scheduled invoices/bills**: Wrap as `{ status, startDate, endDate, repeat, invoice/bill: { reference, valueDate, dueDate, contactResourceId, lineItems, saveAsDraft: false } }`. `reference` is required.
534
534
  - **Scheduled journals**: Flat: `{ status, startDate, endDate, repeat, valueDate, schedulerEntries, reference }`. `valueDate` is required.
535
- - **FX currency (invoices, bills, credit notes, AND journals)**: `currency: { sourceCurrency: "USD" }` (auto-fetches platform rate) or `currency: { sourceCurrency: "USD", exchangeRate: 1.35 }` (custom rate). Same object form on all transaction types. **Never use `currencyCode` string** — silently ignored.
535
+ - **FX currency (invoices, bills, credit notes, AND journals)**: `currency: { sourceCurrency: "USD" }` (auto-fetches platform rate) or `currency: { sourceCurrency: "USD", exchangeRate: 0.74 }` (custom rate). Same object form on all transaction types. **Never use `currencyCode` string** — silently ignored.
536
536
 
537
537
  158. **Match-to-EXISTING reconciliation — `reconcile_with_payments` / `reconcile_magic_match` / `reconcile_learned_prediction`.** These reconcile a bank entry against transactions/payments the org ALREADY has, vs `invoice_receipt`/`bill_receipt` which CREATE new ones. Prefer match-to-existing to avoid duplicates.
538
538
  - **`reconcile_with_payments`** — the headline. `businessTransactionPayments[]` each carry an open bill/invoice's `cashflowTransactionResourceId` (from `search_cashflow_transactions` or a suggestion's `cftBtResourceId`) + `transactionAmount`; the endpoint CREATES the payment AND reconciles in one call — **no `pay_bill`/`pay_invoice` first.** Also accepts `matchedPayments[]` (existing payments) / `matchedBatchPayments[]` / `adjustment` (over/under-payment + FX write-off). Guard: ≥1 match array non-empty. **FX is auto-resolved server-side — pass NO `currencySettings`/rate for the common case.** Only the rare bill-currency ≠ bank-currency case needs explicit `paymentAmount` (bank ccy) + `currencySettings`. FX gain/loss is NOT auto-posted — post it via `adjustment.cashAdjustmentEntries[]` to an FX account. Errors: `PAYMENT_AMOUNT_REQUIRED_IN_BUSINESS_TRANSACTION_SOURCE_CURRENCY` (cross-ccy missing paymentAmount), `INVALID_EXCHANGE_RATE_ERROR` (adjustment leg in non-functional ccy missing rate), `TOTAL_RECONCILIATION_AMOUNT_MISMATCHED_WITH_STATEMENT_ENTRY_AMOUNT` (sum ≠ entry → add adjustment leg). **NOT idempotent, no client key — a blind retry double-creates a payment; re-check `search_bank_records(status:'RECONCILED')` before retry.**
@@ -289,11 +289,11 @@ Enable currencies first, then set rates via the **separate** rate endpoints belo
289
289
  - To check before enabling: `GET /organization/currencies` and check if the code is already in the list.
290
290
  - Sending an empty array `{ "currencies": [] }` returns 400.
291
291
 
292
- ### Currency Rates — `/organization-currencies` (hyphenated path)
292
+ ### Currency Rates — `/organization/currencies/:code/rates`
293
293
 
294
- **CRITICAL path difference**: Enable uses `/organization/currencies` (nested). Rates use `/organization-currencies` (hyphenated). Using the wrong path returns 404.
294
+ **Path note**: both enable and rates live under the nested `/organization/currencies` family. The older hyphenated `/organization-currencies/...` rate paths still resolve but are marked **deprecated** in the OpenAPI spec use the nested form.
295
295
 
296
- #### POST /api/v1/organization-currencies/:currencyCode/rates
296
+ #### POST /api/v1/organization/currencies/:currencyCode/rates
297
297
 
298
298
  ```json
299
299
  // Request:
@@ -304,16 +304,16 @@ Enable currencies first, then set rates via the **separate** rate endpoints belo
304
304
  // HTTP 201
305
305
  ```
306
306
 
307
- **CRITICAL**: Response `data` is a **plain string** `"Rate added successfully"` — NOT a CurrencyRate object. You do NOT get back a `resourceId`. If you need the rate's `resourceId` (e.g., for later PUT/DELETE), you must follow up with `GET /organization-currencies/:code/rates` and match by `rateApplicableFrom` date.
307
+ **CRITICAL**: Response `data` is a **plain string** `"Rate added successfully"` — NOT a CurrencyRate object. You do NOT get back a `resourceId`. If you need the rate's `resourceId` (e.g., for later PUT/DELETE), you must follow up with `GET /organization/currencies/:code/rates` and match by `rateApplicableFrom` date.
308
308
 
309
309
  **Required fields**:
310
- - `rate` — positive number (must be > 0). Direction is **functionalToSource** (1 base = X foreign). Example for SGD org setting USD rate: `rate: 0.74` means 1 SGD = 0.74 USD. **If your data is sourceToFunctional (1 USD = 1.35 SGD), invert: `rate = 1 / yourRate`.**
310
+ - `rate` — positive number (must be > 0). Direction is **functionalToSource** (1 base = X foreign). Example for SGD org setting USD rate: `rate: 0.74` means 1 SGD = 0.74 USD. **If your data is sourceToFunctional (1 USD = 1.35 SGD), invert: `rate = 1 / yourRate`.** You do not have to do this by hand: pass your figure as-is with `rateDirection` (`FUNCTIONAL_TO_SOURCE` | `SOURCE_TO_FUNCTIONAL`) and the client inverts and strips it. See SKILL.md Rule 49.
311
311
  - `rateApplicableFrom` — `YYYY-MM-DD` string (NOT ISO datetime — `"2026-02-10T00:00:00Z"` is rejected with "does not match 2006-01-02 format")
312
312
 
313
313
  **Optional fields**:
314
314
  - `rateApplicableTo` — `YYYY-MM-DD` string. Must be after `rateApplicableFrom` (422 `INVALID_DATE_RANGE` otherwise).
315
315
 
316
- #### GET /api/v1/organization-currencies/:currencyCode/rates
316
+ #### GET /api/v1/organization/currencies/:currencyCode/rates
317
317
 
318
318
  ```json
319
319
  // Response:
@@ -335,7 +335,7 @@ Enable currencies first, then set rates via the **separate** rate endpoints belo
335
335
  }
336
336
  ```
337
337
 
338
- #### PUT /api/v1/organization-currencies/:currencyCode/rates/:resourceId
338
+ #### PUT /api/v1/organization/currencies/:currencyCode/rates/:resourceId
339
339
 
340
340
  ```json
341
341
  // Request:
@@ -346,7 +346,7 @@ Enable currencies first, then set rates via the **separate** rate endpoints belo
346
346
  // HTTP 200
347
347
  ```
348
348
 
349
- #### DELETE /api/v1/organization-currencies/:currencyCode/rates/:resourceId
349
+ #### DELETE /api/v1/organization/currencies/:currencyCode/rates/:resourceId
350
350
 
351
351
  ```json
352
352
  // Response:
@@ -371,12 +371,12 @@ Create exchange rates in bulk (max 500). **Auto-enables currencies not yet enabl
371
371
  {
372
372
  "sourceCurrencyCode": "USD",
373
373
  "rate": 1.35,
374
- "rateDirection": "FUNCTIONAL_TO_SOURCE",
374
+ "rateDirection": "SOURCE_TO_FUNCTIONAL",
375
375
  "rateApplicableFrom": "2026-03-29"
376
376
  },
377
377
  {
378
378
  "sourceCurrencyCode": "EUR",
379
- "rate": 1.48,
379
+ "rate": 0.68,
380
380
  "rateDirection": "FUNCTIONAL_TO_SOURCE",
381
381
  "rateApplicableFrom": "2026-03-29",
382
382
  "rateApplicableTo": "2026-04-30"
@@ -388,7 +388,7 @@ Create exchange rates in bulk (max 500). **Auto-enables currencies not yet enabl
388
388
  { "data": { "resourceId": null, "resourceIds": ["uuid1", "uuid2"] } }
389
389
  ```
390
390
 
391
- `rateDirection`: `FUNCTIONAL_TO_SOURCE` (e.g., 1 SGD = 1.35 USD) or `SOURCE_TO_FUNCTIONAL` (e.g., 1 USD = 0.74 SGD). Unlike the single-rate POST endpoint, this returns `resourceIds` directly.
391
+ `rateDirection` (SGD-base org, USD source): `FUNCTIONAL_TO_SOURCE` means the value is source-per-base — 1 SGD = 0.74 USD send `0.74`. `SOURCE_TO_FUNCTIONAL` means base-per-source — 1 USD = 1.35 SGD → send `1.35` as-is, no inversion. This is the ONLY rate endpoint that accepts the everyday quote directly; the single-rate POST always wants `FUNCTIONAL_TO_SOURCE`. Unlike the single-rate POST endpoint, this returns `resourceIds` directly.
392
392
 
393
393
  ---
394
394
 
@@ -547,7 +547,7 @@ Create defaults: `status=ACTIVE`, `itemCategory=NON_INVENTORY`. Update: only sen
547
547
  - **`currency: "USD"` (string)** causes "Invalid request body" error (400).
548
548
 
549
549
  **Rate hierarchy** (when using `currency: { sourceCurrency }` without `exchangeRate`):
550
- 1. Org-level rate (set via `/organization-currencies/:code/rates`) — auto-filled if exists
550
+ 1. Org-level rate (set via `/organization/currencies/:code/rates`) — auto-filled if exists
551
551
  2. Platform rate (ECB via FRANKFURTER) — auto-fetched if no org rate
552
552
  3. Transaction-level rate (via `exchangeRate` in the `currency` object) — overrides all
553
553
 
@@ -2078,7 +2078,7 @@ Update an existing payment record. All fields optional — only included fields
2078
2078
  "valueDate": "2026-03-02",
2079
2079
  "paymentMethod": "BANK_TRANSFER",
2080
2080
  "accountResourceId": "uuid-bank",
2081
- "currency": { "sourceCurrency": "USD", "exchangeRate": 1.35 },
2081
+ "currency": { "sourceCurrency": "USD", "exchangeRate": 0.74 },
2082
2082
  "transactionFee": 5.00,
2083
2083
  "transactionFeeCollected": true
2084
2084
  }
@@ -175,21 +175,23 @@ Valid `type` values: `"TEXT"`, `"DATE"`, `"DROPDOWN"` (UPPERCASE).
175
175
 
176
176
  ## Currency Errors
177
177
 
178
- ### 404 on rate endpoints — WRONG PATH (common mistake)
179
- **Cause**: Using `/organization/currencies` (nested) instead of `/organization-currencies` (hyphenated) for rate endpoints.
178
+ ### 404 on rate endpoints — MALFORMED PATH (common mistake)
179
+ **Cause**: Omitting the currency code, or using singular `rate` instead of `rates`.
180
180
  **Endpoints that 404 (wrong paths)**:
181
- - `/api/v1/organization/currencies/rates` → 404
182
- - `/api/v1/organization/currencies/USD/rate` → 404
183
- - `/api/v1/organization/currencies/{id}/rate` → 404
184
- **Fix**: Rate endpoints use the **hyphenated** path `/organization-currencies` (NOT `/organization/currencies`):
181
+ - `/api/v1/organization/currencies/rates` → 404 (no currency code)
182
+ - `/api/v1/organization/currencies/USD/rate` → 404 (singular)
183
+ - `/api/v1/organization/currencies/{id}/rate` → 404 (singular)
184
+ **Fix**: Rate endpoints live under the nested `/organization/currencies` family. The older
185
+ hyphenated `/organization-currencies/...` rate paths still resolve but are marked
186
+ **deprecated** in the OpenAPI spec — prefer the nested form:
185
187
  ```
186
- POST /api/v1/organization-currencies/:currencyCode/rates Set rate
187
- GET /api/v1/organization-currencies/:currencyCode/rates List rates
188
- GET /api/v1/organization-currencies/:currencyCode/rates/:id Get rate
189
- PUT /api/v1/organization-currencies/:currencyCode/rates/:id Update rate
190
- DELETE /api/v1/organization-currencies/:currencyCode/rates/:id Delete rate
188
+ POST /api/v1/organization/currencies/:currencyCode/rates Set rate
189
+ GET /api/v1/organization/currencies/:currencyCode/rates List rates
190
+ GET /api/v1/organization/currencies/:currencyCode/rates/:id Get rate
191
+ PUT /api/v1/organization/currencies/:currencyCode/rates/:id Update rate
192
+ DELETE /api/v1/organization/currencies/:currencyCode/rates/:id Delete rate
191
193
  ```
192
- Enable currencies first via `POST /organization/currencies`, then set rates via `/organization-currencies/:code/rates`.
194
+ Enable currencies first via `POST /organization/currencies`, then set rates via `/organization/currencies/:code/rates`.
193
195
 
194
196
  ### "Cannot set rate for organization base currency" (400)
195
197
  **Cause**: Trying to POST/PUT a rate for the org's base currency (e.g., SGD for a Singapore org).
@@ -208,7 +210,7 @@ Enable currencies first via `POST /organization/currencies`, then set rates via
208
210
  **Fix**: Ensure `rateApplicableTo` is after `rateApplicableFrom`, or omit `rateApplicableTo` entirely.
209
211
 
210
212
  ### Rates appear inverted (wrong direction)
211
- **Cause**: POSTing a sourceToFunctional rate (1 foreign = X base) as the `rate` field, which expects functionalToSource (1 base = X foreign).
213
+ **Cause**: POSTing a sourceToFunctional rate (1 foreign = X base) as the `rate` field, which expects functionalToSource (1 base = X foreign). You do not have to do this by hand: pass your figure as-is with `rateDirection` (`FUNCTIONAL_TO_SOURCE` | `SOURCE_TO_FUNCTIONAL`) and the client inverts and strips it. See SKILL.md Rule 49.
212
214
  **Symptom**: UI shows "1 SGD = 0.0088 JPY" instead of "1 SGD ≈ 111 JPY" — the reciprocal of what you intended.
213
215
  **Fix**: Invert before POSTing: `rate = 1 / yourRate`. If your data says "1 JPY = 0.009 SGD", POST `rate: 111.11`.
214
216
 
@@ -241,11 +243,11 @@ Enable currencies first via `POST /organization/currencies`, then set rates via
241
243
  **Rate sources in response** (inspect `currencyExchange.rateSource`):
242
244
  - `rateSource: "EXTERNAL"`, `providerName: "FRANKFURTER"` — auto-fetched from ECB
243
245
  - `rateSource: "INTERNAL_TRANSACTION"`, `providerName: "CUSTOM"` — user-specified `exchangeRate`
244
- - `rateSource: "INTERNAL_ORG"` — org-level rate (set via `/organization-currencies/:code/rates`)
246
+ - `rateSource: "INTERNAL_ORG"` — org-level rate (set via `/organization/currencies/:code/rates`)
245
247
 
246
248
  ### "Invalid request body" (400) — `currency` as string
247
249
  **Cause**: Using `currency: "USD"` (string) instead of object form.
248
- **Fix**: Use object form `currency: { sourceCurrency: "USD" }` or `currency: { sourceCurrency: "USD", exchangeRate: 1.35 }`.
250
+ **Fix**: Use object form `currency: { sourceCurrency: "USD" }` or `currency: { sourceCurrency: "USD", exchangeRate: 0.74 }`.
249
251
 
250
252
  ---
251
253
 
@@ -617,7 +619,7 @@ if (acct.code) ctx.coaIds[acct.code] = acct.resourceId;
617
619
  ## Journal Errors
618
620
 
619
621
  ### Multi-currency journals — `currency` object
620
- Journals support a top-level `currency` object to create entries in a foreign currency — **same format as invoices/bills**: `{ "sourceCurrency": "USD" }` (auto-fetch platform rate) or `{ "sourceCurrency": "USD", "exchangeRate": 1.35 }` (custom rate). The currency must be enabled for the org. Omit the field for base currency journals.
622
+ Journals support a top-level `currency` object to create entries in a foreign currency — **same format as invoices/bills**: `{ "sourceCurrency": "USD" }` (auto-fetch platform rate) or `{ "sourceCurrency": "USD", "exchangeRate": 0.74 }` (custom rate). The currency must be enabled for the org. Omit the field for base currency journals.
621
623
 
622
624
  ```json
623
625
  // Base currency journal (omit currency):
@@ -627,7 +629,7 @@ Journals support a top-level `currency` object to create entries in a foreign cu
627
629
  { "saveAsDraft": false, "reference": "JV-001", "valueDate": "2026-02-08", "currency": { "sourceCurrency": "USD" }, "journalEntries": [...] }
628
630
 
629
631
  // Foreign currency journal (custom rate):
630
- { "saveAsDraft": false, "reference": "JV-001", "valueDate": "2026-02-08", "currency": { "sourceCurrency": "USD", "exchangeRate": 1.35 }, "journalEntries": [...] }
632
+ { "saveAsDraft": false, "reference": "JV-001", "valueDate": "2026-02-08", "currency": { "sourceCurrency": "USD", "exchangeRate": 0.74 }, "journalEntries": [...] }
631
633
  ```
632
634
 
633
635
  **Three restrictions apply to foreign currency journals:**
@@ -229,7 +229,7 @@ Fixed assets lock their linked line items — cannot edit account, amounts, or e
229
229
 
230
230
  **User Roles**: Admin (full access), Preparer (create drafts, submit for approval), Member (own drafts only). Granular permissions by module (AR, AP, Treasury, Journals).
231
231
 
232
- **API**: CRUD `GET/POST/PUT/DELETE /chart-of-accounts`, `POST /chart-of-accounts/bulk-upsert`, `POST /chart-of-accounts/search`, `GET /tax-profiles`, `POST /tax-profiles/search`, CRUD `GET/POST/PUT/DELETE /tags`, `POST /tags/search`, CRUD `GET/POST/PUT/DELETE /custom-fields`, `POST /custom-fields/search`, `POST /organization/currencies` (enable), `DELETE /organization/currencies/:code` (disable), `POST /organization-currencies/:code/rates` (set FX rate, CRUD), `GET /organization` (org details), CRUD `GET/POST/PUT/DELETE /organization/bookmarks`, CRUD `GET/POST/PUT/DELETE /nano-classifiers`, `POST /nano-classifiers/search`, `GET /organization-users`, `POST /organization-users/search`. Reference data: `GET /account-classifications`, `GET /withholding-tax-codes`, `GET /tax-types`, `GET /modules`
232
+ **API**: CRUD `GET/POST/PUT/DELETE /chart-of-accounts`, `POST /chart-of-accounts/bulk-upsert`, `POST /chart-of-accounts/search`, `GET /tax-profiles`, `POST /tax-profiles/search`, CRUD `GET/POST/PUT/DELETE /tags`, `POST /tags/search`, CRUD `GET/POST/PUT/DELETE /custom-fields`, `POST /custom-fields/search`, `POST /organization/currencies` (enable), `DELETE /organization/currencies/:code` (disable), `POST /organization/currencies/:code/rates` (set FX rate, CRUD), `GET /organization` (org details), CRUD `GET/POST/PUT/DELETE /organization/bookmarks`, CRUD `GET/POST/PUT/DELETE /nano-classifiers`, `POST /nano-classifiers/search`, `GET /organization-users`, `POST /organization-users/search`. Reference data: `GET /account-classifications`, `GET /withholding-tax-codes`, `GET /tax-types`, `GET /modules`
233
233
 
234
234
  ---
235
235
 
@@ -213,7 +213,7 @@ When POSTing, `classificationType` must be one of these exact strings (same as `
213
213
  | `lines` | `journalEntries` | Same |
214
214
  | `entries` | `journalEntries` | Same |
215
215
  | `debit` / `credit` (entry) | `amount` + `type` | `amount`: number, `type`: `"DEBIT"` or `"CREDIT"` (UPPERCASE) |
216
- | `currency: "USD"` (string) | `currency: { sourceCurrency: "USD" }` | **Object form** — same as invoices/bills. Auto-fetches platform rate. Add `exchangeRate: 1.35` for custom rate. Omit for base currency. |
216
+ | `currency: "USD"` (string) | `currency: { sourceCurrency: "USD" }` | **Object form** — same as invoices/bills. Auto-fetches platform rate. Add `exchangeRate: 0.74` for custom rate. Omit for base currency. |
217
217
 
218
218
  ### Bulk upsert (`POST /journals/bulk-upsert`)
219
219
 
@@ -460,7 +460,7 @@ DELETE → expects "A" (parentEntityResourceId, via /cash-entries/:id)
460
460
 
461
461
  | What You'd Guess | Actual API Field | Notes |
462
462
  |------------------|-------------------|-------|
463
- | `/organization/currencies/:code/rates` | `/organization-currencies/:code/rates` | **Hyphenated** path for rates (NOT nested) |
463
+ | `/organization-currencies/:code/rates` (older docs) | `/organization/currencies/:code/rates` | Nested path is current; hyphenated form is **deprecated** in the spec |
464
464
  | `exchangeRate` (rate POST body) | `rate` | Just `rate`, not `exchangeRate` |
465
465
  | `effectiveDate` / `valueDate` / `date` | `rateApplicableFrom` | Rate start date, `YYYY-MM-DD` only |
466
466
  | `expiryDate` | `rateApplicableTo` | Optional rate end date |
@@ -470,9 +470,9 @@ DELETE → expects "A" (parentEntityResourceId, via /cash-entries/:id)
470
470
  | `baseCurrency` (GET response) | `functionalCurrencyCode` | The org's base currency code in rate response |
471
471
  | `notes` (GET response) | `notes: { date, name }` | Metadata object — `date` is the creation date, `name` is the creator |
472
472
 
473
- > **Rate direction cheat-sheet**: POST `rate` = GET `rateFunctionalToSource` = "1 base → X foreign". If your data is "1 foreign → X base", **invert before POSTing**.
473
+ > **Rate direction cheat-sheet**: POST `rate` = GET `rateFunctionalToSource` = "1 base → X foreign". If your data is "1 foreign → X base", **invert before POSTing** — or set `rateDirection: "SOURCE_TO_FUNCTIONAL"` and let the client do it (Rule 49).
474
474
 
475
- **Rate POST response gotcha**: `POST /organization-currencies/:code/rates` returns `{ "data": "Rate added successfully" }` — a **plain string**, NOT a CurrencyRate object. No `resourceId` is returned. To get the rate's `resourceId` for later PUT/DELETE, follow up with GET and match by `rateApplicableFrom`.
475
+ **Rate POST response gotcha**: `POST /organization/currencies/:code/rates` returns `{ "data": "Rate added successfully" }` — a **plain string**, NOT a CurrencyRate object. No `resourceId` is returned. To get the rate's `resourceId` for later PUT/DELETE, follow up with GET and match by `rateApplicableFrom`.
476
476
 
477
477
  ---
478
478
 
@@ -582,8 +582,8 @@ DELETE → expects "A" (parentEntityResourceId, via /cash-entries/:id)
582
582
  | `GET /organization/currencies` | `{ data: { data: [...] } }` | `{ data: [...] }` |
583
583
  | `POST /chart-of-accounts/bulk-upsert` | `{ data: { resourceIds: [...] } }` | Individual results |
584
584
  | `POST /organization/currencies` | `{ data: { resourceIds: [...] } }` | Confirmation object |
585
- | `POST /organization-currencies/:code/rates` | `{ data: "Rate added successfully" }` (string!) | `{ data: { resourceId } }` (object) |
586
- | `PUT /organization-currencies/:code/rates/:id` | `{ data: "Rate updated successfully" }` (string!) | `{ data: { resourceId } }` (object) |
585
+ | `POST /organization/currencies/:code/rates` | `{ data: "Rate added successfully" }` (string!) | `{ data: { resourceId } }` (object) |
586
+ | `PUT /organization/currencies/:code/rates/:id` | `{ data: "Rate updated successfully" }` (string!) | `{ data: { resourceId } }` (object) |
587
587
  | `GET /invoices/{id}` line items | `organizationAccountResourceId` | `accountResourceId` (POST uses `accountResourceId`) |
588
588
  | `POST /invoices` (create) | `{ resourceId }` only | Full entity (minimal response!) |
589
589
  | `POST /bills` (create) | `{ resourceId }` only | Full entity (minimal response!) |
@@ -606,7 +606,7 @@ Battle-tested patterns from production Jaz API clients:
606
606
  | Bill payments | Always embedded in bill creation body, never standalone |
607
607
  | Bank records | JSON POST `/bank-records/:id` or multipart `/magic/importBankStatementFromAttachment`. Search via `/bank-records/:id/search`. |
608
608
  | Scheduled bills | Wrapped as `{ repeat, startDate, endDate, bill: {...} }`. Field is `repeat` (NOT `frequency`/`interval`) |
609
- | FX currency | MUST use `currency` OBJECT on ALL transaction types (invoices, bills, credit notes, journals): `{ sourceCurrency: "USD" }` (auto platform rate) or `{ sourceCurrency: "USD", exchangeRate: 1.35 }` (custom). String `currencyCode` silently ignored. |
609
+ | FX currency | MUST use `currency` OBJECT on ALL transaction types (invoices, bills, credit notes, journals): `{ sourceCurrency: "USD" }` (auto platform rate) or `{ sourceCurrency: "USD", exchangeRate: 0.74 }` (custom). String `currencyCode` silently ignored. `exchangeRate` = functionalToSource; invert an everyday "1 USD = 1.35 SGD" quote (Rule 49). |
610
610
 
611
611
  ---
612
612
 
@@ -307,17 +307,17 @@ Body for all three: `{ items: [{btResourceId: "<uuid>", btType: "SALE|PURCHASE|S
307
307
  | GET | `/organization/currencies/:currencyCode` | Get by code |
308
308
  | DELETE | `/organization/currencies/:currencyCode` | Disable currency |
309
309
 
310
- ### Currency Rates (via /organization-currencies)
310
+ ### Currency Rates (via /organization/currencies)
311
311
  | Method | Path | Description |
312
312
  |--------|------|-------------|
313
- | POST | `/organization-currencies/:currencyCode/rates` | Set custom rate |
314
- | GET | `/organization-currencies/:currencyCode/rates` | List rates |
315
- | GET | `/organization-currencies/:currencyCode/rates/:resourceId` | Get rate |
316
- | PUT | `/organization-currencies/:currencyCode/rates/:resourceId` | Update rate |
317
- | DELETE | `/organization-currencies/:currencyCode/rates/:resourceId` | Delete rate |
313
+ | POST | `/organization/currencies/:currencyCode/rates` | Set custom rate |
314
+ | GET | `/organization/currencies/:currencyCode/rates` | List rates |
315
+ | GET | `/organization/currencies/:currencyCode/rates/:resourceId` | Get rate |
316
+ | PUT | `/organization/currencies/:currencyCode/rates/:resourceId` | Update rate |
317
+ | DELETE | `/organization/currencies/:currencyCode/rates/:resourceId` | Delete rate |
318
318
  | POST | `/organization/currencies/rates/bulk-upsert` | Bulk create exchange rates (max 500, auto-enables currencies) |
319
319
 
320
- **CRITICAL path difference**: Currency rate management uses `/organization-currencies` (hyphenated), while basic enable/disable uses `/organization/currencies` (nested). The bulk-upsert endpoint uses the nested path (`/organization/currencies/rates/bulk-upsert`). Using the wrong path returns 404. POST body: `{ "rate": 0.74, "rateApplicableFrom": "YYYY-MM-DD" }` (rate = 1 base → X foreign; see endpoints.md for direction details). Base currency rates return 400. See endpoints.md for full examples.
320
+ **Path note**: rate management, enable/disable and bulk-upsert all live under the nested `/organization/currencies` family. The older hyphenated `/organization-currencies/...` rate paths still resolve but are marked **deprecated** in the OpenAPI spec prefer the nested form. POST body: `{ "rate": 0.74, "rateApplicableFrom": "YYYY-MM-DD" }` (rate = 1 base → X foreign; see endpoints.md for direction details). Base currency rates return 400. See endpoints.md for full examples.
321
321
 
322
322
  ---
323
323
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-cli
3
- version: 5.34.0
3
+ version: 5.35.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,
@@ -183,9 +183,8 @@ Same subcommands and flags as `cash-in`.
183
183
  |------------|-----------|
184
184
  | `list <code>` | `--limit`, `--offset`, `--all`, `--format`, `--json` |
185
185
  | `add <code>` | `--rate`, `--from`, `--to` |
186
- | `update <rateId>` | `--rate`, `--from`, `--to` |
187
- | `import <code>` | `--from`, `--to` (auto-fetch from ECB/MAS) |
188
- | `import-status <jobId>` | Check import job status |
186
+ | `update <code> <rateId>` | `--rate`, `--from`, `--to` |
187
+ | `bulk-upsert` | `--input <file>` (max 500 rates; requires `rateDirection` per rate) |
189
188
 
190
189
  ### `clio tax-profiles` — Tax profiles and tax types
191
190
  | Subcommand | Key flags |
@@ -472,7 +471,7 @@ All calculators work offline (no auth). Use `--json` for structured output.
472
471
  | `depreciation` | `--cost`, `--salvage`, `--life`, `--method` |
473
472
  | `prepaid-expense` | `--amount`, `--start-date`, `--months` |
474
473
  | `deferred-revenue` | `--amount`, `--start-date`, `--months` |
475
- | `fx-reval` | `--amount`, `--original-rate`, `--new-rate`, `--currency` |
474
+ | `fx-reval` | `--amount`, `--book-rate`, `--closing-rate`, `--rate-direction`, `--currency` |
476
475
  | `ecl` | `--receivables`, `--aging-buckets` (via `--input`) |
477
476
  | `provision` | `--amount`, `--rate`, `--periods` |
478
477
  | `fixed-deposit` | `--principal`, `--rate`, `--term` |
@@ -98,8 +98,10 @@ Handle a foreign currency invoice from start to finish.
98
98
  # Add USD currency to the org (idempotent — safe to re-run)
99
99
  clio currencies add USD
100
100
 
101
- # Set the exchange rate for the period
102
- clio currency-rates add USD --rate 1.3450 --from 2026-03-01 --to 2026-03-31
101
+ # Set the exchange rate for the period.
102
+ # --rate is functionalToSource: 1 SGD = 0.7435 USD. Invert an everyday
103
+ # "1 USD = 1.3450 SGD" quote before sending (1 / 1.3450 = 0.7435).
104
+ clio currency-rates add USD --rate 0.7435 --from 2026-03-01 --to 2026-03-31
103
105
 
104
106
  # For bulk updates, prepare a JSON file and use bulk-upsert (max 500 rates/call)
105
107
  clio currency-rates bulk-upsert --input rates.json
@@ -111,7 +113,7 @@ clio invoices create \
111
113
  --due 2026-04-15 \
112
114
  --ref "INV-USD-001" \
113
115
  --currency USD \
114
- --rate 1.3450 \
116
+ --exchange-rate 0.7435 \
115
117
  --lines '[{"name":"Software License","quantity":1,"unitPrice":2000,"accountResourceId":"Revenue"}]' \
116
118
  --finalize
117
119
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-conversion
3
- version: 5.34.0
3
+ version: 5.35.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:
@@ -9,11 +9,11 @@ Enable currencies and set FYE closing rates before creating any FX transactions:
9
9
  POST /api/v1/organization/currencies // Enable currency
10
10
  { "currencies": ["USD"] }
11
11
 
12
- POST /api/v1/organization-currencies/USD/rates // Set FYE rate
13
- { "rate": 1.35, "rateApplicableFrom": "2024-12-31" }
12
+ POST /api/v1/organization/currencies/USD/rates // Set FYE rate
13
+ { "rate": 0.74, "rateApplicableFrom": "2024-12-31" }
14
14
  ```
15
15
 
16
- **Rate direction:** Jaz uses `functionalToSource` — how many functional currency units per 1 source currency unit. If base = SGD and "1 USD = 1.35 SGD", then `rate = 1.35`.
16
+ **Rate direction:** Jaz uses `functionalToSource` — how many SOURCE (foreign) units per 1 FUNCTIONAL (base) unit. If base = SGD and the quote is "1 USD = 1.35 SGD", that quote is the inverse: `rate = 1 / 1.35 = 0.74`. Invert only when the quote is written foreign-first; a USD-base org quoting "1 USD = 56.5 PHP" already has the right direction and sends `56.5`. Rather than deciding, set `rateDirection` and pass the figure verbatim (SKILL.md Rule 49).
17
17
 
18
18
  **CRITICAL:** `currencyCode: "USD"` (string) is **silently ignored** by the API — it creates the transaction in base currency. You MUST use the `currency` object form.
19
19
 
@@ -27,7 +27,7 @@ FX conversion transactions use **original dates** (for aging) but an **explicit
27
27
  "dueDate": "2024-07-15",
28
28
  "currency": {
29
29
  "sourceCurrency": "USD",
30
- "exchangeRate": 1.35
30
+ "exchangeRate": 0.74
31
31
  }
32
32
  }
33
33
  ```
@@ -222,16 +222,16 @@ POST /api/v1/organization/currencies
222
222
  For Quick Conversion, all conversion transactions use the FYE closing rate. Rates MUST be set AFTER currencies are enabled:
223
223
 
224
224
  ```
225
- POST /api/v1/organization-currencies/<code>/rates
225
+ POST /api/v1/organization/currencies/<code>/rates
226
226
  {
227
- "rate": 1.35,
227
+ "rate": 0.74,
228
228
  "rateApplicableFrom": "2023-12-31"
229
229
  }
230
230
  ```
231
231
 
232
- **Rate direction:** `functionalToSource` — how many units of functional (base) currency = 1 unit of source (foreign) currency. Example: If base is SGD and rate is 1 SGD = 0.74 USD, then rate = 0.74.
232
+ **Rate direction:** `functionalToSource` — how many units of SOURCE (foreign) currency = 1 unit of FUNCTIONAL (base) currency. Example: base SGD, 1 SGD = 0.74 USD rate = 0.74. A quote written foreign-first ("1 USD = 1.35 SGD") is the inverse — send 1/1.35 = 0.74, or send 1.35 with `rateDirection: "SOURCE_TO_FUNCTIONAL"` and let the client invert.
233
233
 
234
- **Note:** Rate endpoints use `/organization-currencies` (hyphenated). Enable/disable uses `/organization/currencies` (nested path). These are DIFFERENT endpoints.
234
+ **Note:** Rate endpoints and enable/disable both live under the nested `/organization/currencies` family. The older hyphenated `/organization-currencies/...` rate paths are **deprecated** in the OpenAPI spec.
235
235
 
236
236
  **CRITICAL:** The field is `rateApplicableFrom` (NOT `effectiveDate`). Using the wrong field name will silently fail.
237
237
 
@@ -93,7 +93,7 @@ POST /api/v1/invoices
93
93
 
94
94
  **Dates:** Use original dates from the aging report (preserves aging schedules). Fall back to FYE date if not available.
95
95
 
96
- **FX invoices:** Use `currency: { sourceCurrency: "USD", exchangeRate: 1.35 }` (object form with explicit FYE rate). The `exchangeRate` field overrides Jaz's auto-fetch, ensuring zero UGL on day 1. See "FX Invoices/Bills — Dates vs. Rates" in Edge Cases below.
96
+ **FX invoices:** Use `currency: { sourceCurrency: "USD", exchangeRate: 0.74 }` (object form with explicit FYE rate). The `exchangeRate` field overrides Jaz's auto-fetch, ensuring zero UGL on day 1. See "FX Invoices/Bills — Dates vs. Rates" in Edge Cases below.
97
97
 
98
98
  #### 2.2 Conversion Bills (AP)
99
99
  For each line in the AP Aging report:
@@ -193,7 +193,7 @@ POST /api/v1/invoices
193
193
  "dueDate": "<original due date>", // preserves aging buckets
194
194
  "currency": {
195
195
  "sourceCurrency": "USD",
196
- "exchangeRate": 1.35 // explicit FYE rate — overrides auto-fetch
196
+ "exchangeRate": 0.74 // explicit FYE rate — overrides auto-fetch
197
197
  },
198
198
  ...
199
199
  }
@@ -207,7 +207,7 @@ POST /api/v1/invoices
207
207
 
208
208
  **When dates aren't available:** Some aging reports only show contact name + amount (no individual dates). In this case, fall back to FYE date for both valueDate and dueDate. Aging schedules will be flat ("current") but FX handling remains correct.
209
209
 
210
- **Rate source:** FYE rates come from the exchange rates file (e.g., MAS closing rates). They're set on the org in Phase 1 via `POST /organization-currencies/:code/rates` and also passed explicitly on each FX transaction to guarantee the rate is used regardless of valueDate.
210
+ **Rate source:** FYE rates come from the exchange rates file (e.g., MAS closing rates). They're set on the org in Phase 1 via `POST /organization/currencies/:code/rates` and also passed explicitly on each FX transaction to guarantee the rate is used regardless of valueDate.
211
211
 
212
212
  ### Partially Paid Invoices in AR Aging
213
213
  The AR Aging shows only the **outstanding** balance. Create the conversion invoice for the outstanding amount only — the historical payments are not relevant for Quick Conversion.
@@ -108,11 +108,11 @@ Conversion Bills (AP) XX
108
108
  TTB Journal 1
109
109
  Lock Date Set <FYE date>
110
110
 
111
- FX RATES APPLIED
111
+ FX RATES APPLIED (functionalToSource — 1 base unit = N foreign)
112
112
  --------------------------------------------
113
- USD 1.3500
114
- EUR 1.4500
115
- JPY 0.0092
113
+ USD 0.7407
114
+ EUR 0.6897
115
+ JPY 108.6957
116
116
 
117
117
  ERRORS (if any)
118
118
  --------------------------------------------
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-kit
3
- version: 5.34.0
3
+ version: 5.35.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.34.0
3
+ version: 5.35.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.34.0
3
+ version: 5.35.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,
@@ -164,7 +164,9 @@ generate_general_ledger(period_end: '2025-01-31', accountResourceIds: [<FX accou
164
164
 
165
165
  2. For each foreign-currency monetary balance at period end (from a separate `generate_general_ledger` filtered to non-base-currency accounts), independently compute:
166
166
  ```
167
- clio calc fx-reval --amount <foreign> --book-rate <historical> --closing-rate <list_currency_rates valueDate: '2025-01-31'> --currency <code> --base-currency <base currency> --json
167
+ # --rate-direction is REQUIRED. A list_currency_rates value is FUNCTIONAL_TO_SOURCE
168
+ # and can be passed as-is; an everyday "1 USD = 1.35 SGD" quote is SOURCE_TO_FUNCTIONAL.
169
+ clio calc fx-reval --amount <foreign> --book-rate <historical> --closing-rate <rateFunctionalToSource at 2025-01-31> --rate-direction FUNCTIONAL_TO_SOURCE --currency <code> --base-currency <base currency> --json
168
170
  ```
169
171
 
170
172
  3. Sum your independent gain/loss across all foreign balances. Compare against Jaz's auto-posted FX totals from step 1. Variance > materiality threshold → investigate (see the `fx-reval` recipe for likely causes — settlement-realized FX shifts, explicit `currency.exchangeRate` overrides, multi-leg FX through bank-side spreads).
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-recipes
3
- version: 5.34.0
3
+ version: 5.35.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
@@ -189,7 +189,9 @@ clio calc asset-disposal --cost 50000 --salvage 5000 --life 5 --acquired 2022-01
189
189
 
190
190
  # FX revaluation — unrealized gain/loss on non-AR/AP items (IAS 21)
191
191
  # Typical context: month-end close (period-end FX reval) + year-end (revaluation)
192
- clio calc fx-reval --amount 50000 --book-rate 1.35 --closing-rate 1.38 [--currency USD] [--base-currency SGD] [--json]
192
+ # --rate-direction is REQUIRED these rates read foreign-first (1 USD = 1.35 SGD).
193
+ # A list_currency_rates value would be FUNCTIONAL_TO_SOURCE instead.
194
+ clio calc fx-reval --amount 50000 --book-rate 1.35 --closing-rate 1.38 --rate-direction SOURCE_TO_FUNCTIONAL [--position ASSET|LIABILITY] [--currency USD] [--base-currency SGD] [--json]
193
195
 
194
196
  # Expected credit loss provision matrix (IFRS 9)
195
197
  # Typical context: GST/VAT filing cycle (ECL reviewed alongside the return prep) + year-end (ECL true-up)