jaz-clio 4.2.1 → 4.3.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.
Files changed (40) hide show
  1. package/README.md +6 -0
  2. package/assets/skills/api/SKILL.md +61 -6
  3. package/assets/skills/api/references/endpoints.md +74 -5
  4. package/assets/skills/api/references/full-api-surface.md +2 -1
  5. package/assets/skills/conversion/SKILL.md +1 -1
  6. package/assets/skills/jobs/SKILL.md +1 -1
  7. package/assets/skills/jobs/references/document-collection.md +58 -12
  8. package/assets/skills/transaction-recipes/SKILL.md +1 -1
  9. package/dist/commands/accounts.js +18 -10
  10. package/dist/commands/bank.js +43 -1
  11. package/dist/commands/bills.js +240 -12
  12. package/dist/commands/capsules.js +18 -10
  13. package/dist/commands/cash-entry.js +18 -10
  14. package/dist/commands/cash-transfer.js +18 -10
  15. package/dist/commands/contacts.js +18 -10
  16. package/dist/commands/currency-rates.js +9 -5
  17. package/dist/commands/customer-credit-notes.js +18 -10
  18. package/dist/commands/draft-helpers.js +276 -0
  19. package/dist/commands/invoices.js +18 -10
  20. package/dist/commands/items.js +18 -10
  21. package/dist/commands/jobs.js +52 -2
  22. package/dist/commands/journals.js +18 -10
  23. package/dist/commands/magic.js +262 -0
  24. package/dist/commands/pagination.js +44 -10
  25. package/dist/commands/payments.js +18 -10
  26. package/dist/commands/reports.js +18 -1
  27. package/dist/commands/resolve.js +52 -0
  28. package/dist/commands/supplier-credit-notes.js +18 -10
  29. package/dist/commands/tags.js +18 -10
  30. package/dist/core/api/attachments.js +7 -12
  31. package/dist/core/api/bills.js +10 -0
  32. package/dist/core/api/magic.js +78 -0
  33. package/dist/core/api/reports.js +3 -0
  34. package/dist/core/jobs/document-collection/tools/ingest/classify.js +38 -8
  35. package/dist/core/jobs/document-collection/tools/ingest/decrypt.js +111 -0
  36. package/dist/core/jobs/document-collection/tools/ingest/format.js +6 -3
  37. package/dist/core/jobs/document-collection/tools/ingest/scanner.js +17 -2
  38. package/dist/core/jobs/document-collection/tools/ingest/upload.js +20 -7
  39. package/dist/index.js +2 -0
  40. package/package.json +4 -1
package/README.md CHANGED
@@ -12,6 +12,12 @@ CLI for the [Jaz](https://jaz.ai) accounting platform. Create invoices, record b
12
12
 
13
13
  > Also fully compatible with [Juan Accounting](https://juan.ac).
14
14
 
15
+ ## Prerequisites
16
+
17
+ **Node.js 18 or later** is required. If `node --version` works, skip ahead.
18
+
19
+ Otherwise: download the LTS installer from [nodejs.org](https://nodejs.org). It includes `npm`.
20
+
15
21
  ## Install
16
22
 
17
23
  ```bash
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-api
3
- version: 4.2.1
3
+ version: 4.3.0
4
4
  description: Complete reference for the Jaz REST API — the accounting platform backend. Use this skill whenever building, modifying, debugging, or extending any code that calls the API — including API clients, integrations, data seeding, test data, or new endpoint work. Contains every field name, response shape, error, gotcha, and edge case discovered through live production testing.
5
5
  license: MIT
6
6
  compatibility: Requires Jaz API key (x-jk-api-key header). Works with Claude Code, Google Antigravity, OpenAI Codex, GitHub Copilot, Cursor, and any agent that reads markdown.
@@ -94,7 +94,7 @@ You are working with the **Jaz REST API** — the accounting platform backend. A
94
94
  | Trial balance | `startDate`, `endDate` |
95
95
  | Balance sheet | `primarySnapshotDate` |
96
96
  | P&L | `primarySnapshotDate`, `secondarySnapshotDate` |
97
- | General ledger | `startDate`, `endDate`, `groupBy: "ACCOUNT"` |
97
+ | General ledger | `startDate`, `endDate`, `groupBy: "ACCOUNT"` (also `TRANSACTION`, `CAPSULE`) |
98
98
  | Cashflow | `primaryStartDate`, `primaryEndDate` |
99
99
  | Cash balance | `reportDate` |
100
100
  | AR/AP report | `endDate` |
@@ -105,7 +105,7 @@ You are working with the **Jaz REST API** — the accounting platform backend. A
105
105
  37. **Data exports use simpler field names**: P&L export uses `startDate`/`endDate` (NOT `primarySnapshotDate`). AR/AP export uses `endDate`.
106
106
 
107
107
  ### Pagination
108
- 38. **All list/search endpoints use `limit`/`offset` pagination** — NOT `page`/`size`. Default limit=100, offset=0. Max limit=1000, max offset=65536. `page`/`size` params are silently ignored. Response shape: `{ totalPages, totalElements, data: [...] }`.
108
+ 38. **All list/search endpoints use `limit`/`offset` pagination** — NOT `page`/`size`. 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.
109
109
 
110
110
  ### Other
111
111
  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.
@@ -132,11 +132,12 @@ You are working with the **Jaz REST API** — the accounting platform backend. A
132
132
  ### Jaz Magic — Extraction & Autofill
133
133
  57. **When the user starts from an attachment, always use Jaz Magic** — if the input is a PDF, JPG, or any document image (invoice, bill, receipt), the correct path is `POST /magic/createBusinessTransactionFromAttachment`. Do NOT manually construct a `POST /invoices` or `POST /bills` payload from an attachment — Jaz Magic handles the entire extraction-and-autofill pipeline server-side: OCR, line item detection, contact matching, CoA auto-mapping via ML learning, and draft creation with all fields pre-filled. Only use `POST /invoices` or `POST /bills` when building transactions from structured data (JSON, CSV, database rows) where the fields are already known.
134
134
  58. **Two upload modes with different content types** — `sourceType: "FILE"` requires **multipart/form-data** with `sourceFile` blob (JSON body fails with 400 "sourceFile is a required field"). `sourceType: "URL"` accepts **application/json** with `sourceURL` string. The OAS only documents URL mode — FILE mode (the common case) is undocumented.
135
- 59. **Three required fields**: `sourceFile` (multipart blob — NOT `file`), `businessTransactionType` (`"INVOICE"` or `"BILL"` only — `EXPENSE` rejected), `sourceType` (`"FILE"` or `"URL"`). All three are validated server-side. **CRITICAL: multipart form field names are camelCase** — `businessTransactionType`, `sourceType`, `sourceFile`, NOT snake_case. Using `business_transaction_type` returns 422 "businessTransactionType is a required field". The File blob must include a filename and correct MIME type (e.g. `application/pdf`, `image/jpeg`) — bare `application/octet-stream` blobs are rejected with 400 "Invalid file type".
136
- 60. **Response maps transaction types**: Request `BILL` → response `businessTransactionType: "PURCHASE"`. Request `INVOICE` → response `businessTransactionType: "SALE"`. S3 paths follow: `/purchases/` vs `/sales/`.
137
- 61. **Extraction is asynchronous** — the API response is immediate (file upload confirmation only). The actual Magic pipeline — OCR, line item extraction, contact matching, CoA learning, and autofill — runs asynchronously. The `subscriptionFBPath` in the response (e.g., `magic_transactions/{orgId}/purchase/{fileId}`) is a Firebase Realtime Database path for subscribing to extraction status updates.
135
+ 59. **Three required fields**: `sourceFile` (multipart blob — NOT `file`), `businessTransactionType` (`"INVOICE"`, `"BILL"`, `"CUSTOMER_CREDIT_NOTE"`, or `"SUPPLIER_CREDIT_NOTE"` — `EXPENSE` rejected), `sourceType` (`"FILE"` or `"URL"`). All three are validated server-side. **CRITICAL: multipart form field names are camelCase** — `businessTransactionType`, `sourceType`, `sourceFile`, NOT snake_case. Using `business_transaction_type` returns 422 "businessTransactionType is a required field". The File blob must include a filename and correct MIME type (e.g. `application/pdf`, `image/jpeg`) — bare `application/octet-stream` blobs are rejected with 400 "Invalid file type".
136
+ 60. **Response maps transaction types**: Request `INVOICE` → response `SALE`. Request `BILL` → response `PURCHASE`. Request `CUSTOMER_CREDIT_NOTE` → response `SALE_CREDIT_NOTE`. Request `SUPPLIER_CREDIT_NOTE` → response `PURCHASE_CREDIT_NOTE`. S3 paths follow the response type. The response `validFiles[]` array contains `workflowResourceId` for tracking extraction progress via `POST /magic/workflows/search`.
137
+ 61. **Extraction is asynchronous** — the API response is immediate (file upload confirmation only). The actual Magic pipeline — OCR, line item extraction, contact matching, CoA learning, and autofill — runs asynchronously. Use `POST /magic/workflows/search` with `filter.resourceId.eq: "<workflowResourceId>"` to check status (SUBMITTED → PROCESSING → COMPLETED/FAILED). When COMPLETED, `businessTransactionDetails.businessTransactionResourceId` contains the created draft BT ID. The `subscriptionFBPath` in the response is a Firebase Realtime Database path for real-time status updates (alternative to polling).
138
138
  62. **Accepts PDF and JPG/JPEG** — both file types confirmed working. Handwritten documents are accepted at upload stage (extraction quality varies). `fileType` in response reflects actual format: `"PDF"`, `"JPEG"`.
139
139
  63. **Never use magic-search endpoints** — `GET /invoices/magic-search` and `GET /bills/magic-search` require a separate `x-magic-api-key` (not available to agents). Always use `POST /invoices/search` or `POST /bills/search` with standard `x-jk-api-key` auth instead.
140
+ 63b. **Workflow search tracks all magic uploads** — `POST /magic/workflows/search` searches across BT extractions AND bank statement imports. Filter by `resourceId` (eq), `documentType` (SALE, PURCHASE, SALE_CREDIT_NOTE, PURCHASE_CREDIT_NOTE, BANK_STATEMENT), `status` (SUBMITTED, PROCESSING, COMPLETED, FAILED), `fileName` (contains), `fileType`, `createdAt` (date range). Response: paginated `MagicWorkflowItem` with `businessTransactionDetails.businessTransactionResourceId` (the draft BT ID when COMPLETED) or `bankStatementDetails` (for bank imports). Standard search sort: `{ sortBy: ["createdAt"], order: "DESC" }`.
140
141
 
141
142
  ### Cashflow & Unified Ledger
142
143
  64. **No standalone payments list/search** — `GET /payments`, `POST /payments/search`, and `GET /payments` do NOT exist. Per-payment CRUD (`GET/PUT/DELETE /payments/:resourceId`) exists for individual payment records, but to **list or search** payments, use `POST /cashflow-transactions/search` — the unified transaction ledger that spans invoices, bills, credit notes, journals, cash entries, and payments. Filter by `businessTransactionType` (e.g., `SALE`, `PURCHASE`) and `direction` (`PAYIN`, `PAYOUT`). Response dates are epoch milliseconds.
@@ -163,6 +164,60 @@ You are working with the **Jaz REST API** — the accounting platform backend. A
163
164
  79. **`capsule-transaction` recipes auto-resolve accounts** — when `--input` is omitted, the CLI searches the org's chart of accounts for each blueprint account name (e.g., "Interest Expense", "Loan Payable"). If all accounts resolve with high confidence, no JSON mapping file is needed. If any fail, the error message shows exactly which accounts could not be found and suggests close matches. `--contact` and `--bank-account` on recipes also accept names.
164
165
  80. **Payment/refund account filter is conditional on `--method`** — for BANK_TRANSFER, CASH, and CHEQUE, the `--account` resolver filters to bank/cash accounts only. For other payment methods, all account types are considered.
165
166
 
167
+ ### Draft Finalization Pipeline (Convert & Next)
168
+
169
+ The `clio bills draft` subcommand group enables the full "review → fill missing → convert" workflow that mirrors the Jaz UI's "Convert and Next" button. Designed for AI agents processing a queue of draft bills.
170
+
171
+ #### Commands
172
+
173
+ | Command | Purpose |
174
+ |---------|---------|
175
+ | `clio bills draft list [--ids <ids>] [--json]` | Queue view: all drafts with per-field validation + attachment count |
176
+ | `clio bills draft finalize <id> [flags] [--json]` | Fill missing fields + convert DRAFT → UNPAID in one PUT |
177
+ | `clio bills draft attachments <id> [--json]` | List attachments with download URLs for agent inspection |
178
+
179
+ #### Mandatory Fields for Bill Finalization
180
+
181
+ | Field | JSON Path | CLI Flag | Resolver |
182
+ |-------|-----------|----------|----------|
183
+ | Contact | `contactResourceId` | `--contact <name/UUID>` | Fuzzy resolved |
184
+ | Bill date | `valueDate` | `--date <YYYY-MM-DD>` | Literal |
185
+ | Due date | `dueDate` | `--due <YYYY-MM-DD>` | Literal |
186
+ | Line items | `lineItems` (non-empty) | `--lines <json>` | — |
187
+ | Item name | `lineItems[i].name` | via `--lines` | — |
188
+ | Item price | `lineItems[i].unitPrice` | via `--lines` | — |
189
+ | Item account | `lineItems[i].accountResourceId` | `--account <name/UUID>` (bulk) | Fuzzy resolved |
190
+
191
+ Optional: `--ref`, `--notes`, `--tag`, `--tax-profile <name/UUID>` (bulk, fuzzy resolved), `--tax`, `--tax-inclusive`, `--dry-run`, `--input <file>`.
192
+
193
+ #### Agent Workflow Pattern
194
+
195
+ ```
196
+ Step 1: clio bills draft list --json
197
+ → Batch queue: every DRAFT with per-field validation + attachment count
198
+
199
+ Step 2: For each draft where ready = false:
200
+ a) Read validation.missingFields from Step 1 output
201
+ b) Optional: clio bills draft attachments <id> --json
202
+ → Download fileUrl, read PDF/image, extract or verify values
203
+ c) Resolve values (ask user, or infer from attachment + context)
204
+ d) clio bills draft finalize <id> --contact "Acme" --date 2025-01-15 ... --json
205
+ → Updates + converts to UNPAID in one PUT (Rule 67: bills/invoices → UNPAID, journals → APPROVED)
206
+
207
+ Step 3: For each draft where ready = true:
208
+ clio bills draft finalize <id> --json
209
+ → Converts directly (all mandatory fields already present)
210
+ ```
211
+
212
+ 81. **`--account` bulk patches line items** — when used with `clio bills draft finalize`, `--account` resolves the name to a UUID then sets `accountResourceId` on EVERY line item where it's currently null. Existing accounts are NOT overwritten. Same for `--tax-profile`. `--lines` takes priority (full replacement).
213
+ 82. **`--dry-run` validates without modifying** — returns the same validation structure as `draft list` (per-field status/hint), so agents can preview what would happen before committing. No API write occurs.
214
+ 83. **Finalization is a single PUT** — `updateBill()` with `saveAsDraft: false` transitions DRAFT → UNPAID (per Rule 67) and updates all fields in one call. No delete-and-recreate.
215
+ 84. **Draft list attachment count** — `draft list` includes `attachmentCount` per draft (from `GET /bills/:id/attachments`). Use `draft attachments <id>` for full details including `fileUrl` download links.
216
+
217
+ #### DRY Extension Pattern
218
+
219
+ Bills, invoices, and credit notes share identical mandatory field specs. Adding `clio invoices draft` or `clio customer-credit-notes draft` later reuses all validation, formatting, and CLI flag logic from `draft-helpers.ts` — only the API calls differ.
220
+
166
221
  ## Supporting Files
167
222
 
168
223
  For detailed reference, read these files in this skill directory:
@@ -919,6 +919,12 @@ Valid `datatypeCode`: `TEXT`, `NUMBER`, `BOOLEAN`, `DATE`, `LINK`.
919
919
 
920
920
  Processing is **asynchronous** — the API response confirms file upload immediately. The extraction pipeline runs server-side and pushes status updates via Firebase Realtime Database.
921
921
 
922
+ **Supported document types:**
923
+ - `INVOICE` → creates a draft sale (response type: `SALE`)
924
+ - `BILL` → creates a draft purchase (response type: `PURCHASE`)
925
+ - `CUSTOMER_CREDIT_NOTE` → creates a draft customer CN (response type: `SALE_CREDIT_NOTE`)
926
+ - `SUPPLIER_CREDIT_NOTE` → creates a draft supplier CN (response type: `PURCHASE_CREDIT_NOTE`)
927
+
922
928
  **Two modes** — content type depends on `sourceType`:
923
929
 
924
930
  #### FILE mode (multipart/form-data) — most common
@@ -929,7 +935,7 @@ Content-Type: multipart/form-data
929
935
 
930
936
  Fields:
931
937
  - sourceFile: PDF or JPG file blob (NOT "file")
932
- - businessTransactionType: "INVOICE" or "BILL" (NOT "EXPENSE")
938
+ - businessTransactionType: "INVOICE", "BILL", "CUSTOMER_CREDIT_NOTE", or "SUPPLIER_CREDIT_NOTE"
933
939
  - sourceType: "FILE"
934
940
  ```
935
941
 
@@ -941,6 +947,7 @@ Fields:
941
947
  "filename": "NB64458.pdf",
942
948
  "invalidFiles": [],
943
949
  "validFiles": [{
950
+ "workflowResourceId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
944
951
  "subscriptionFBPath": "magic_transactions/{orgId}/purchase/{fileId}",
945
952
  "errorCode": null,
946
953
  "errorMessage": null,
@@ -980,12 +987,74 @@ Content-Type: application/json
980
987
 
981
988
  **Key gotchas:**
982
989
  - `sourceFile` is the field name (NOT `file`) — same pattern as bank statement endpoint
983
- - Only `INVOICE` and `BILL` accepted `EXPENSE` returns 422
984
- - Response maps types: `INVOICE` → `SALE`, `BILL` → `PURCHASE`
990
+ - `EXPENSE` returns 422use one of the 4 valid types above
991
+ - Response maps types: `INVOICE` → `SALE`, `BILL` → `PURCHASE`, `CUSTOMER_CREDIT_NOTE` → `SALE_CREDIT_NOTE`, `SUPPLIER_CREDIT_NOTE` → `PURCHASE_CREDIT_NOTE`
985
992
  - JSON body with `sourceType: "FILE"` always fails (400) — MUST use multipart
986
- - `subscriptionFBPath` is the Firebase path for tracking extraction progress
993
+ - `workflowResourceId` in `validFiles[]` is for tracking via `POST /magic/workflows/search`
994
+ - `subscriptionFBPath` is the Firebase path for real-time status updates
987
995
  - All three fields (`sourceFile`/`sourceURL`, `businessTransactionType`, `sourceType`) are required — omitting any returns 422
988
- - File types confirmed: PDF, JPG/JPEG (handwritten documents accepted extraction quality varies)
996
+ - File types confirmed: PDF, JPG/JPEG, PNG, HEIC, XLS, XLSX, EML (max 1 MB)
997
+
998
+ ---
999
+
1000
+ ### POST /api/v1/magic/workflows/search
1001
+
1002
+ Search across magic BT extraction workflows and bank statement imports. Use this to track the status of uploads and retrieve the created draft BT resource ID.
1003
+
1004
+ ```json
1005
+ / Request:
1006
+ POST /api/v1/magic/workflows/search
1007
+ Content-Type: application/json
1008
+
1009
+ {
1010
+ "filter": {
1011
+ "resourceId": { "eq": "f47ac10b-58cc-4372-a567-0e02b2c3d479" },
1012
+ "documentType": ["SALE", "PURCHASE"],
1013
+ "status": ["COMPLETED"],
1014
+ "fileName": { "contains": "invoice" },
1015
+ "createdAt": { "gte": "2025-01-01", "lte": "2025-12-31" }
1016
+ },
1017
+ "limit": 20,
1018
+ "offset": 0,
1019
+ "sort": { "sortBy": ["createdAt"], "order": "DESC" }
1020
+ }
1021
+
1022
+ / Response (200):
1023
+ {
1024
+ "data": [{
1025
+ "resourceId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
1026
+ "documentType": "SALE",
1027
+ "status": "COMPLETED",
1028
+ "fileName": "invoice.pdf",
1029
+ "fileType": "PDF",
1030
+ "fileUrl": "https://s3...",
1031
+ "fileId": "6e999313...",
1032
+ "createdAt": "2025-01-15",
1033
+ "updatedAt": "2025-01-15",
1034
+ "businessTransactionDetails": {
1035
+ "businessTransactionResourceId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
1036
+ "ocrJobType": "SYNC",
1037
+ "workflowStatus": "TRANSACTION_CREATED"
1038
+ }
1039
+ }],
1040
+ "totalElements": 1,
1041
+ "totalPages": 1
1042
+ }
1043
+ ```
1044
+
1045
+ **Filter fields:**
1046
+ - `resourceId`: StringExpression (eq, contains) — workflow ID from magic create response
1047
+ - `documentType`: Array — SALE, PURCHASE, SALE_CREDIT_NOTE, PURCHASE_CREDIT_NOTE, BANK_STATEMENT
1048
+ - `status`: Array — SUBMITTED, PROCESSING, COMPLETED, FAILED
1049
+ - `fileName`: StringExpression — original uploaded filename
1050
+ - `fileType`: Array — PDF, PNG, JPEG, JPG, HEIC, CSV, XLS, XLSX, EML
1051
+ - `createdAt`: DateExpression (eq, gte, lte) — workflow creation date
1052
+
1053
+ **Workflow for agents:**
1054
+ 1. Upload via `POST /magic/createBusinessTransactionFromAttachment` → get `workflowResourceId`
1055
+ 2. Search with `filter.resourceId.eq` → check `status`
1056
+ 3. When `COMPLETED` → read `businessTransactionDetails.businessTransactionResourceId`
1057
+ 4. Use the BT resource ID with `GET /invoices/:id`, `GET /bills/:id`, `GET /customer-credit-notes/:id`, or `GET /supplier-credit-notes/:id`
989
1058
 
990
1059
  ---
991
1060
 
@@ -381,7 +381,8 @@
381
381
 
382
382
  | Method | Path | Description |
383
383
  |--------|------|-------------|
384
- | POST | `/magic/createBusinessTransactionFromAttachment` | **Jaz Magic: Extraction & Autofill.** Upload PDF/JPG → full extraction pipeline (OCR, line items, contact matching, CoA ML learning) → draft invoice or bill with all fields autofilled. FILE mode = multipart (`sourceFile` blob), URL mode = JSON (`sourceURL`). Async — returns `subscriptionFBPath` for tracking extraction progress. Request `INVOICE` → response `SALE`, `BILL` `PURCHASE`. |
384
+ | POST | `/magic/createBusinessTransactionFromAttachment` | **Jaz Magic: Extraction & Autofill.** Upload PDF/JPG → full extraction pipeline (OCR, line items, contact matching, CoA ML learning) → draft transaction with all fields autofilled. Supports `INVOICE`, `BILL`, `CUSTOMER_CREDIT_NOTE`, `SUPPLIER_CREDIT_NOTE`. FILE mode = multipart (`sourceFile` blob), URL mode = JSON (`sourceURL`). Async — returns `workflowResourceId` for tracking via workflow search. Request maps: `INVOICE`→`SALE`, `BILL`→`PURCHASE`, `CUSTOMER_CREDIT_NOTE`→`SALE_CREDIT_NOTE`, `SUPPLIER_CREDIT_NOTE`→`PURCHASE_CREDIT_NOTE`. |
385
+ | POST | `/magic/workflows/search` | **Workflow search.** Track magic upload status across BT extractions and bank imports. Filter by `resourceId`, `documentType` (SALE/PURCHASE/SALE_CREDIT_NOTE/PURCHASE_CREDIT_NOTE/BANK_STATEMENT), `status` (SUBMITTED/PROCESSING/COMPLETED/FAILED), `fileName`, `fileType`, `createdAt`. Response includes `businessTransactionDetails.businessTransactionResourceId` (the draft BT ID) when COMPLETED. |
385
386
  | POST | `/magic/importBankStatementFromAttachment` | Convert bank statement → entries |
386
387
  | PUT | `/invoices/magic-update` | AI-enhanced invoice update |
387
388
  | PUT | `/bills/magic-update` | AI-enhanced bill update |
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-conversion
3
- version: 4.2.1
3
+ version: 4.3.0
4
4
  description: Accounting data conversion skill — migrates customer data from Xero, QuickBooks, Sage, MYOB, and Excel exports to Jaz. Covers config, quick, and full conversion workflows, Excel parsing, CoA/contact/tax/items mapping, clearing accounts, TTB, and TB verification.
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-jobs
3
- version: 4.2.1
3
+ version: 4.3.0
4
4
  description: 12 accounting jobs for SMB bookkeepers and accountants — month-end, quarter-end, and year-end close playbooks plus 9 ad-hoc operational jobs (bank recon, document collection, GST/VAT filing, payment runs, credit control, supplier recon, audit prep, fixed asset review, statutory filing). Jobs can have paired tools as nested subcommands (e.g., `clio jobs bank-recon match`, `clio jobs document-collection ingest`, `clio jobs statutory-filing sg-cs`). Paired with an interactive CLI blueprint generator (clio jobs).
5
5
  license: MIT
6
6
  compatibility: Works with Claude Code, Claude Cowork, Claude.ai, and any agent that reads markdown. For API payloads, load the jaz-api skill. For individual transaction patterns, load the jaz-recipes skill.
@@ -1,31 +1,34 @@
1
1
  # Document Collection
2
2
 
3
- Scan and classify client documents (invoices, bills, bank statements) from local directories or cloud share links (Dropbox, Google Drive, OneDrive). Outputs classified file paths with metadata the AI agent handles uploads via the api skill.
3
+ Scan and classify client documents (invoices, bills, credit notes, bank statements) from local directories or cloud share links (Dropbox, Google Drive, OneDrive). Outputs classified file paths with metadata. Supports encrypted PDF detection and decryption via `qpdf`. The AI agent handles uploads via the api skill.
4
4
 
5
5
  ## When to Use
6
6
 
7
- - Client sends a folder of PDFs (invoices, bills, receipts) for bulk processing
7
+ - Client sends a folder of PDFs (invoices, bills, credit notes, receipts) for bulk processing
8
8
  - Processing bank statement CSVs/OFX files for import
9
9
  - Migrating documents from file dumps (Dropbox, shared folders, email attachments)
10
10
  - Processing documents from a shared Dropbox, Google Drive, or OneDrive link
11
11
  - Batch-processing scanned documents during onboarding
12
+ - Handling password-protected bank statement PDFs (auto-detection + decryption)
12
13
 
13
14
  ## How It Works
14
15
 
15
16
  ```
16
17
  Source (local or cloud) CLI Output (IngestPlan)
17
18
  ┌───────────────┐ ┌──────────────────────────────────────────┐
18
- │ invoices/ │── scan + classify ─► absolutePath, documentType: INVOICE
19
+ │ invoices/ │── scan + classify ─► documentType: INVOICE
19
20
  │ inv-001.pdf │ │ sizeBytes: 45230 │
20
- inv-002.jpg │ │
21
- bills/ │── scan + classify ─► absolutePath, documentType: BILL
21
+ credit-notes/ │── scan + classify ─► documentType: CUSTOMER_CREDIT_NOTE
22
+ cn-001.pdf │ │
23
+ │ bills/ │── scan + classify ─► documentType: BILL │
22
24
  │ acme-jan.pdf│ │ │
23
- │ bank/ │── scan + classify ─► absolutePath, documentType: BANK_STATEMENT│
24
- │ dbs-jan.csv │ │
25
+ │ bank/ │── scan + classify ─► documentType: BANK_STATEMENT
26
+ │ dbs-jan.csv │ │ encrypted: true (if password-protected)
27
+ │ dbs-feb.pdf │ │ │
25
28
  └───────────────┘ └──────────────────────────────────────────┘
26
29
 
27
30
  AI Agent reads plan,
28
- uploads via api skill (curl)
31
+ uploads via api skill / clio magic
29
32
  ```
30
33
 
31
34
  Cloud links are downloaded to a temp directory first, then scanned through the same pipeline.
@@ -36,14 +39,18 @@ The tool auto-classifies documents by **folder name** (case-insensitive prefix m
36
39
 
37
40
  | Folder name pattern | Classification |
38
41
  |---|---|
39
- | `invoice*`, `sales*`, `ar*`, `receivable*`, `revenue*` | INVOICE |
42
+ | `invoice*`, `sales*`, `ar*`, `receivable*`, `revenue*`, `customer*` | INVOICE |
43
+ | `credit-note*`, `cn*`, `customer-credit*`, `sales-credit*` | CUSTOMER_CREDIT_NOTE |
44
+ | `debit-note*`, `dn*`, `supplier-credit*`, `vendor-credit*`, `purchase-credit*` | SUPPLIER_CREDIT_NOTE |
40
45
  | `bill*`, `purchase*`, `expense*`, `ap*`, `payable*`, `supplier*`, `vendor*`, `cost*` | BILL |
41
- | `bank*`, `statement*`, `recon*` | BANK_STATEMENT |
46
+ | `bank*`, `statement*`, `recon*`, `payment*`, `transaction*` | BANK_STATEMENT |
42
47
  | (unknown) | UNKNOWN — skipped unless `--type` forced |
43
48
 
49
+ Multilingual support: Filipino/Tagalog, Bahasa Indonesia/Malay, Vietnamese, and Mandarin (pinyin) folder names are also recognized for all five document types.
50
+
44
51
  ### File Extension Filters
45
52
 
46
- - **Invoices/Bills**: `.pdf`, `.jpg`, `.jpeg`, `.png`
53
+ - **Invoices/Bills/Credit Notes**: `.pdf`, `.jpg`, `.jpeg`, `.png`
47
54
  - **Bank Statements**: `.csv`, `.ofx`
48
55
  - **Skipped** (with warning): `.xlsx`, `.xls`, `.doc`, `.docx`, `.txt`, `.zip`, `.rar`, `.7z`
49
56
 
@@ -88,6 +95,10 @@ clio jobs document-collection ingest --source "https://www.dropbox.com/..." --ti
88
95
 
89
96
  # Force classification (skip auto-detect)
90
97
  clio jobs document-collection ingest --source ./scans/ --type invoice [--json]
98
+ clio jobs document-collection ingest --source ./scans/ --type credit-note-customer [--json]
99
+
100
+ # Scan + upload (decrypt encrypted PDFs with password)
101
+ clio jobs document-collection ingest --source ./bank-docs/ --upload --bank-account "DBS Checking" --pdf-password "mypass" --json
91
102
  ```
92
103
 
93
104
  ### Options
@@ -95,7 +106,11 @@ clio jobs document-collection ingest --source ./scans/ --type invoice [--json]
95
106
  | Flag | Description |
96
107
  |------|-------------|
97
108
  | `--source <path\|url>` | Local directory path or public cloud share link — Dropbox, Google Drive, OneDrive (required) |
98
- | `--type <type>` | Force all files to: `invoice`, `bill`, or `bank-statement` |
109
+ | `--type <type>` | Force all files to: `invoice`, `bill`, `credit-note-customer`, `credit-note-supplier`, or `bank-statement` |
110
+ | `--upload` | Upload classified files to Jaz after scanning (requires auth) |
111
+ | `--bank-account <name-or-id>` | Bank account name or resourceId (required for bank statements) |
112
+ | `--pdf-password <password>` | Password for encrypted PDFs — same password applied to all. Requires `qpdf` installed (`brew install qpdf`) |
113
+ | `--api-key <key>` | API key for upload (or use `JAZ_API_KEY` env var) |
99
114
  | `--timeout <ms>` | Download timeout in milliseconds (default: 30000 for files, 120000 for folders) |
100
115
  | `--currency <code>` | Functional/reporting currency label |
101
116
  | `--json` | Structured JSON output with absolute file paths |
@@ -129,6 +144,7 @@ The `--json` output includes absolute file paths, classification, and size for e
129
144
  "uploadable": 1,
130
145
  "needClassification": 0,
131
146
  "skipped": 0,
147
+ "encrypted": 0,
132
148
  "byType": { "INVOICE": 1 }
133
149
  }
134
150
  }
@@ -136,6 +152,36 @@ The `--json` output includes absolute file paths, classification, and size for e
136
152
 
137
153
  For cloud sources, `localPath` points to the temp directory where files were downloaded.
138
154
 
155
+ ## Encrypted PDF Support
156
+
157
+ Bank statements and some government documents are often delivered as password-protected PDFs. The Magic API cannot process encrypted PDFs, so the tool detects them during scan and decrypts before upload.
158
+
159
+ ### Detection
160
+
161
+ During scan, each `.pdf` file is checked for a `/Encrypt` dictionary entry in the PDF binary. Encrypted files are flagged with `encrypted: true` in the plan and shown with a `(encrypted)` tag in the output.
162
+
163
+ ### Decryption
164
+
165
+ Decryption requires `qpdf` (a system CLI tool):
166
+
167
+ ```bash
168
+ # Install qpdf
169
+ brew install qpdf # macOS
170
+ sudo apt install qpdf # Ubuntu/Debian
171
+ choco install qpdf # Windows
172
+ ```
173
+
174
+ When `--upload` is used with `--pdf-password`, encrypted PDFs are decrypted to a temp file, uploaded, then cleaned up. The same password is applied to all encrypted files in the batch.
175
+
176
+ ### Error Handling (JSON mode)
177
+
178
+ If encrypted PDFs are found during `--upload` without the required dependencies, the tool outputs structured errors for agent consumption:
179
+
180
+ | Error Code | Condition | Action |
181
+ |------------|-----------|--------|
182
+ | `ENCRYPTED_PDF_NO_QPDF` | qpdf not installed | Install qpdf, then retry |
183
+ | `ENCRYPTED_PDF_NO_PASSWORD` | No `--pdf-password` provided | Retry with `--pdf-password <password>` |
184
+
139
185
  ## Phases (Blueprint)
140
186
 
141
187
  When run without `ingest` subcommand, produces a 4-phase blueprint:
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-recipes
3
- version: 4.2.1
3
+ version: 4.3.0
4
4
  description: 16 IFRS-compliant recipes for complex multi-step accounting in Jaz — prepaid amortization, deferred revenue, loan schedules, IFRS 16 leases, hire purchase, fixed deposits, asset disposal, FX revaluation, ECL provisioning, IAS 37 provisions, dividends, intercompany, and capital WIP. Each recipe includes journal entries, capsule structure, and verification steps. Paired with 10 financial calculators that produce execution-ready blueprints with workings.
5
5
  license: MIT
6
6
  compatibility: Works with Claude Code, Claude Cowork, Claude.ai, and any agent that reads markdown. For API payloads, load the jaz-api skill alongside this one.
@@ -2,7 +2,7 @@ import chalk from 'chalk';
2
2
  import { listAccounts, searchAccounts, createAccount, deleteAccount } from '../core/api/chart-of-accounts.js';
3
3
  import { apiAction } from './api-action.js';
4
4
  import { parsePositiveInt, parseNonNegativeInt, readBodyInput, requireFields } from './parsers.js';
5
- import { paginatedFetch } from './pagination.js';
5
+ import { paginatedFetch, paginatedJson, displaySlice } from './pagination.js';
6
6
  export function registerAccountsCommand(program) {
7
7
  const accounts = program
8
8
  .command('accounts')
@@ -14,18 +14,22 @@ export function registerAccountsCommand(program) {
14
14
  .option('--limit <n>', 'Max results (default 100)', parsePositiveInt)
15
15
  .option('--offset <n>', 'Offset for pagination', parseNonNegativeInt)
16
16
  .option('--all', 'Fetch all pages')
17
+ .option('--max-rows <n>', 'Max rows for --all (default 10000)', parsePositiveInt)
17
18
  .option('--api-key <key>', 'API key (overrides stored/env)')
18
19
  .option('--json', 'Output as JSON')
19
20
  .action(apiAction(async (client, opts) => {
20
- const { data, totalElements, totalPages } = await paginatedFetch(opts, (p) => listAccounts(client, p), { label: 'Fetching accounts' });
21
+ const result = await paginatedFetch(opts, (p) => listAccounts(client, p), { label: 'Fetching accounts' });
21
22
  if (opts.json) {
22
- console.log(JSON.stringify({ totalElements, totalPages, data }, null, 2));
23
+ console.log(paginatedJson(result, opts));
23
24
  }
24
25
  else {
25
- console.log(chalk.bold(`Accounts (${data.length} of ${totalElements}):\n`));
26
- for (const a of data) {
26
+ console.log(chalk.bold(`Accounts (${result.data.length} of ${result.totalElements}):\n`));
27
+ const { items, overflow } = displaySlice(result.data);
28
+ for (const a of items) {
27
29
  console.log(` ${chalk.cyan(a.resourceId)} ${a.code ?? ''} ${a.name} ${chalk.dim(a.accountType)}`);
28
30
  }
31
+ if (overflow > 0)
32
+ console.log(chalk.dim(` ... and ${overflow.toLocaleString()} more (use --json for full output)`));
29
33
  }
30
34
  }));
31
35
  // ── clio accounts search ────────────────────────────────────────
@@ -37,24 +41,28 @@ export function registerAccountsCommand(program) {
37
41
  .option('--limit <n>', 'Max results (default 20)', parsePositiveInt)
38
42
  .option('--offset <n>', 'Offset for pagination', parseNonNegativeInt)
39
43
  .option('--all', 'Fetch all pages')
44
+ .option('--max-rows <n>', 'Max rows for --all (default 10000)', parsePositiveInt)
40
45
  .option('--api-key <key>', 'API key (overrides stored/env)')
41
46
  .option('--json', 'Output as JSON')
42
47
  .action((query, opts) => apiAction(async (client) => {
43
48
  const filter = { or: { name: { contains: query }, code: { contains: query } } };
44
49
  const sort = { sortBy: [opts.sort ?? 'code'], order: (opts.order ?? 'ASC') };
45
- const { data, totalElements, totalPages } = await paginatedFetch(opts, ({ limit, offset }) => searchAccounts(client, { filter, limit, offset, sort }), { label: 'Searching accounts', defaultLimit: 20 });
50
+ const result = await paginatedFetch(opts, ({ limit, offset }) => searchAccounts(client, { filter, limit, offset, sort }), { label: 'Searching accounts', defaultLimit: 20 });
46
51
  if (opts.json) {
47
- console.log(JSON.stringify({ totalElements, totalPages, data }, null, 2));
52
+ console.log(paginatedJson(result, opts));
48
53
  }
49
54
  else {
50
- if (data.length === 0) {
55
+ if (result.data.length === 0) {
51
56
  console.log(chalk.yellow('No accounts found.'));
52
57
  return;
53
58
  }
54
- console.log(chalk.bold(`Found ${data.length} account(s):\n`));
55
- for (const a of data) {
59
+ console.log(chalk.bold(`Found ${result.data.length} account(s):\n`));
60
+ const { items, overflow } = displaySlice(result.data);
61
+ for (const a of items) {
56
62
  console.log(` ${chalk.cyan(a.resourceId)} ${a.code ?? ''} ${a.name} ${chalk.dim(a.accountType)}`);
57
63
  }
64
+ if (overflow > 0)
65
+ console.log(chalk.dim(` ... and ${overflow.toLocaleString()} more (use --json for full output)`));
58
66
  }
59
67
  })(opts));
60
68
  // ── clio accounts create ──────────────────────────────────────
@@ -1,7 +1,15 @@
1
1
  import chalk from 'chalk';
2
- import { listBankAccounts, getBankAccount, searchBankRecords, } from '../core/api/bank.js';
2
+ import { readFileSync } from 'node:fs';
3
+ import { basename, extname, resolve } from 'node:path';
4
+ import { listBankAccounts, getBankAccount, searchBankRecords, importBankStatement, } from '../core/api/bank.js';
3
5
  import { apiAction } from './api-action.js';
4
6
  import { parsePositiveInt } from './parsers.js';
7
+ const BANK_MIME_MAP = {
8
+ '.csv': 'text/csv',
9
+ '.ofx': 'application/x-ofx',
10
+ '.xls': 'application/vnd.ms-excel',
11
+ '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
12
+ };
5
13
  export function registerBankCommand(program) {
6
14
  const bank = program
7
15
  .command('bank')
@@ -92,4 +100,38 @@ export function registerBankCommand(program) {
92
100
  }
93
101
  }
94
102
  })(opts));
103
+ // ── clio bank import ──────────────────────────────────────────
104
+ bank
105
+ .command('import')
106
+ .description('Import a bank statement file (CSV, OFX, XLS, XLSX)')
107
+ .requiredOption('--file <path>', 'Bank statement file path')
108
+ .requiredOption('--account <resourceId>', 'Bank account resourceId')
109
+ .option('--api-key <key>', 'API key (overrides stored/env)')
110
+ .option('--json', 'Output as JSON')
111
+ .action(apiAction(async (client, opts) => {
112
+ const filePath = resolve(opts.file);
113
+ const ext = extname(filePath).toLowerCase();
114
+ const mime = BANK_MIME_MAP[ext];
115
+ if (!mime) {
116
+ console.error(chalk.red(`Error: unsupported file type "${ext}". Supported: ${Object.keys(BANK_MIME_MAP).join(', ')}`));
117
+ process.exit(1);
118
+ }
119
+ const buffer = readFileSync(filePath);
120
+ const blob = new Blob([buffer], { type: mime });
121
+ const fileName = basename(filePath);
122
+ const res = await importBankStatement(client, {
123
+ businessTransactionType: 'BANK_STATEMENT',
124
+ accountResourceId: opts.account,
125
+ sourceFile: blob,
126
+ sourceFileName: fileName,
127
+ });
128
+ if (opts.json) {
129
+ console.log(JSON.stringify(res.data, null, 2));
130
+ }
131
+ else {
132
+ console.log(chalk.green('Bank statement uploaded — processing started.'));
133
+ console.log(chalk.bold('File:'), fileName);
134
+ console.log(chalk.dim('Check status: clio magic search --type bank-statement'));
135
+ }
136
+ }));
95
137
  }