jaz-cli 2.0.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.
@@ -0,0 +1,162 @@
1
+ ---
2
+ name: api
3
+ version: 2.0.0
4
+ description: Complete reference for the Jaz/Juan 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
+ license: MIT
6
+ compatibility: Requires Jaz/Juan API key (x-jk-api-key header). Works with Claude Code, Claude Cowork, Claude.ai, and any agent that reads markdown.
7
+ ---
8
+
9
+ # Jaz API Skill
10
+
11
+ You are working with the **Jaz/Juan REST API** — the backend for Jaz (Singapore) and Juan (Philippines) accounting platforms.
12
+
13
+ ## When to Use This Skill
14
+
15
+ - Writing or modifying any code that calls the Jaz API
16
+ - Building API clients, integrations, or data pipelines
17
+ - Debugging API errors (422, 400, 404, 500)
18
+ - Adding support for new Jaz API endpoints
19
+ - Reviewing code that constructs Jaz API request payloads
20
+
21
+ ## Quick Reference
22
+
23
+ **Base URL**: `https://api.getjaz.com`
24
+ **Auth**: `x-jk-api-key: <key>` header on every request — key has `jk-` prefix (e.g., `jk-a1b2c3...`). NOT `Authorization: Bearer` or `x-api-key`.
25
+ **Content-Type**: `application/json` for all POST/PUT/PATCH
26
+ **All paths are prefixed**: `/api/v1/` (e.g., `https://api.getjaz.com/api/v1/invoices`)
27
+
28
+ ## Critical Rules
29
+
30
+ ### Identifiers & Dates
31
+ 1. **All IDs are `resourceId`** — never `id`. References use `<resource>ResourceId` suffix.
32
+ 2. **All transaction dates are `valueDate`** — not `issueDate`, `invoiceDate`, `date`. This is an accounting term meaning "date of economic effect."
33
+ 3. **All dates are `YYYY-MM-DD` strings** — ISO datetime and epoch ms are rejected.
34
+
35
+ ### Payments (Cross-Currency Aware)
36
+ 4. **Payment amounts have two fields**: `paymentAmount` = bank account currency (actual cash moved), `transactionAmount` = transaction document currency (invoice/bill/credit note — amount applied to balance). For same-currency, both are equal. For FX (e.g., USD invoice paid from SGD bank at 1.35): `paymentAmount: 1350` (SGD), `transactionAmount: 1000` (USD).
37
+ 5. **Payment date is `valueDate`** — not `paymentDate`, not `date`.
38
+ 6. **Payment bank account is `accountResourceId`** — not `bankAccountResourceId`.
39
+ 7. **Payments require 6 fields**: `paymentAmount`, `transactionAmount`, `accountResourceId`, `paymentMethod`, `reference`, `valueDate`.
40
+ 8. **Payments wrapped in `{ payments: [...] }`** — array recommended. Flat objects are now auto-wrapped by the API, but array format is preferred for clarity.
41
+
42
+ ### Names & Fields
43
+ 9. **Line item descriptions use `name`** — not `description`.
44
+ 10. **Item names**: canonical field is `internalName`, but `name` alias is accepted on POST. GET responses return both `internalName` and `name`.
45
+ 11. **Tag names**: canonical field is `tagName`, but `name` alias is accepted on POST. GET responses return both `tagName` and `name`.
46
+ 12. **Custom field names**: POST uses `name`, GET returns both `customFieldName` and `name`.
47
+ 13. **Invoice/bill number is `reference`** — not `referenceNumber`.
48
+
49
+ ### Transaction Creation
50
+ 14. **`saveAsDraft`** defaults to `false` — omitting it creates a finalized transaction. Explicitly sending `saveAsDraft: true` creates a draft.
51
+ 15. **If `saveAsDraft: false`** (or omitted), every lineItem MUST have `accountResourceId`.
52
+ 16. **Phones MUST be E.164** — `+65XXXXXXXX` (SG), `+63XXXXXXXXXX` (PH). No spaces.
53
+
54
+ ### Chart of Accounts
55
+ 17. **Tax profiles pre-exist** — NEVER create them. Only GET and map.
56
+ 18. **Bank accounts are CoA entries** with `accountType: "Bank Accounts"`. No separate endpoint.
57
+ 19. **CoA bulk-upsert wrapper is `accounts`** — not `chartOfAccounts`.
58
+ 20. **CoA POST uses `currency`** — not `currencyCode`. (Asymmetry — GET returns `currencyCode`.)
59
+ 21. **CoA POST uses `classificationType`** — GET returns `accountType`. Same values.
60
+ 22. **CoA code mapping: match by NAME, not code** — pre-existing accounts may have different codes. Resource IDs are the universal identifier.
61
+
62
+ ### Journals & Cash
63
+ 23. **Journals use `journalEntries`** with `amount` + `type: "DEBIT"|"CREDIT"` — NOT `debit`/`credit` number fields.
64
+ 24. **Journals have NO `currency` field** at top level — sending it causes "Invalid request body".
65
+ 25. **Cash entries use `accountResourceId`** at top level for the BANK account + `journalEntries` array for offsets.
66
+
67
+ ### Credit Notes & Refunds
68
+ 26. **Credit note application wraps in `credits` array** with `amountApplied` — not flat.
69
+ 27. **CN refunds use `refunds` wrapper** with `refundAmount` + `refundMethod` — NOT `payments`/`paymentAmount`/`paymentMethod`.
70
+
71
+ ### Inventory Items
72
+ 28. **Inventory items require**: `unit` (e.g., `"pcs"`), `costingMethod` (`"FIXED"` or `"WAC"`), `cogsResourceId`, `blockInsufficientDeductions`, `inventoryAccountResourceId`. `purchaseAccountResourceId` MUST be Inventory-type CoA.
73
+ 29. **Delete inventory items via `DELETE /items/:id`** — not `/inventory-items/:id`.
74
+
75
+ ### Cash Transfers
76
+ 30. **Cash transfers use `cashOut`/`cashIn` sub-objects** — NOT flat `fromAccountResourceId`/`toAccountResourceId`. Each: `{ accountResourceId, amount }`.
77
+
78
+ ### Schedulers
79
+ 31. **Scheduled invoices/bills wrap in `{ invoice: {...} }` or `{ bill: {...} }`** — not flat. Recurrence field is `repeat` (NOT `frequency`/`interval`). `saveAsDraft: false` required.
80
+ 32. **Scheduled journals use FLAT structure** with `schedulerEntries` — not nested in `journal` wrapper.
81
+
82
+ ### Bookmarks
83
+ 33. **Bookmarks use `items` array wrapper** with `name`, `value`, `categoryCode`, `datatypeCode`.
84
+
85
+ ### Custom Fields
86
+ 34. **Do NOT send `appliesTo` on custom field POST** — causes "Invalid request body". Only send `name`, `type`, `printOnDocuments`.
87
+
88
+ ### Reports
89
+ 35. **Report field names differ by type** — this is the most error-prone area:
90
+
91
+ | Report | Required Fields |
92
+ |--------|----------------|
93
+ | Trial balance | `startDate`, `endDate` |
94
+ | Balance sheet | `primarySnapshotDate` |
95
+ | P&L | `primarySnapshotDate`, `secondarySnapshotDate` |
96
+ | General ledger | `startDate`, `endDate`, `groupBy: "ACCOUNT"` |
97
+ | Cashflow | `primaryStartDate`, `primaryEndDate` |
98
+ | Cash balance | `reportDate` |
99
+ | AR/AP report | `endDate` |
100
+ | AR/AP summary | `startDate`, `endDate` |
101
+ | Bank balance summary | `primarySnapshotDate` |
102
+ | Equity movement | `primarySnapshotStartDate`, `primarySnapshotEndDate` |
103
+
104
+ 36. **Data exports use simpler field names**: P&L export uses `startDate`/`endDate` (NOT `primarySnapshotDate`). AR/AP export uses `endDate`.
105
+
106
+ ### Pagination
107
+ 37. **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
+
109
+ ### Other
110
+ 38. **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 48 for direction). Cannot set rates for org base currency. Full CRUD: POST (create), GET (list), GET/:id, PUT/:id, DELETE/:id.
111
+ 39. **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.
112
+ 40. **Invoice GET uses `organizationAccountResourceId`** for line item accounts — POST uses `accountResourceId`. Request-side aliases resolve `issueDate` → `valueDate`, `bankAccountResourceId` → `accountResourceId`, etc.
113
+ 41. **Scheduler GET returns `interval`** — POST uses `repeat`. (Response-side asymmetry remains.)
114
+ 42. **Search sort is an object** — `{ sort: { sortBy: ["valueDate"], order: "DESC" } }`. Required when `offset` is present (even `offset: 0`).
115
+ 43. **Bank records: two import methods** — Multipart CSV/OFX via `POST /magic/importBankStatementFromAttachment` (fields: `sourceFile`, `accountResourceId`, `businessTransactionType: "BANK_STATEMENT"`, `sourceType: "FILE"`). JSON via `POST /bank-records/:accountResourceId` with `{ records: [{description, netAmount, valueDate, ...}] }`.
116
+ 44. **Withholding tax** on bills/supplier CNs only. Retry pattern: if `WITHHOLDING_CODE_NOT_FOUND`, strip field and retry.
117
+ 45. **Known API bugs (500s)**: Contact groups PUT, custom fields PUT, capsules POST, catalogs POST, inventory balances GET — all return 500.
118
+ 46. **Non-existent endpoints**: `POST /deposits` and `POST /inventory/adjustments` return 404 — these endpoints are not implemented.
119
+ 47. **Attachments require PDF/PNG**: `POST /:type/:id/attachments` uses multipart `file` field but rejects `text/plain`. Use `application/pdf` or `image/png`.
120
+ 48. **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).
121
+
122
+ ### Search & Filter
123
+ 49. **Search endpoint universal pattern** — All 28 `POST /*/search` endpoints share identical structure: `{ filter?, sort: { sortBy: ["field"], order: "ASC"|"DESC" }, limit: 1-1000, offset: 0-65536 }`. 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.
124
+ 50. **Filter operator reference** — String: `eq`, `neq`, `contains`, `in` (array, max 100), `likeIn` (array, max 100), `reg` (regex array, max 100), `isNull` (bool). Numeric: `eq`, `gt`, `gte`, `lt`, `lte`, `in`. Date (YYYY-MM-DD): `eq`, `gt`, `gte`, `lt`, `lte`, `between` (exactly 2 values). DateTime (RFC3339): same operators, converted to epoch ms internally. Boolean: `eq`. JSON: `jsonIn`, `jsonNotIn`. Logical: nest with `and`/`or`/`not` objects, or use `andGroup`/`orGroup` arrays (invoices, bills, journals, credit notes).
125
+ 51. **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)`.
126
+ 52. **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.
127
+ 53. **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`.
128
+ 54. **Scheduled endpoints support date aliases** — `txnDateAliases` middleware (mapping `issueDate`/`date` → `valueDate`) now applies to all scheduled create/update endpoints: `POST/PUT /scheduled/invoices`, `POST/PUT /scheduled/bills`, `POST/PUT /scheduled/journals`, `POST/PUT /scheduled/subscriptions`.
129
+ 55. **Kebab-case URL aliases** — `capsuleTypes` endpoints also accept kebab-case paths: `/capsule-types` (list, search, CRUD). `moveTransactionCapsules` also accepts `/move-transaction-capsules`. Both camelCase and kebab-case work identically.
130
+
131
+ ## Supporting Files
132
+
133
+ For detailed reference, read these files in this skill directory:
134
+
135
+ - **[references/search-reference.md](./references/search-reference.md)** — Complete search/filter/sort reference for all 28 search endpoints — per-endpoint filter fields, sort fields, operator types
136
+ - **[references/endpoints.md](./references/endpoints.md)** — Full API endpoint reference with request/response examples
137
+ - **[references/errors.md](./references/errors.md)** — Complete error catalog: every error, cause, and fix
138
+ - **[references/field-map.md](./references/field-map.md)** — Complete field name mapping (what you'd guess vs actual), date format matrix, middleware aliases
139
+ - **[references/dependencies.md](./references/dependencies.md)** — Resource creation dependencies and required order
140
+ - **[references/full-api-surface.md](./references/full-api-surface.md)** — Complete endpoint catalog (80+ endpoints), enums, search filters, limits
141
+ - **[references/feature-glossary.md](./references/feature-glossary.md)** — Business context per feature — what each feature does and why, extracted from [help.jaz.ai](https://help.jaz.ai)
142
+ - **[help-center-mirror/](./help-center-mirror/)** — Full help center content split by section (auto-generated from [help.jaz.ai](https://help.jaz.ai))
143
+
144
+ ## DX Overhaul (Implemented)
145
+
146
+ The backend DX overhaul is live. Key improvements now available:
147
+ - **Request-side field aliases**: `name` → `tagName`/`internalName`, `issueDate` → `valueDate`, `bankAccountResourceId` → `accountResourceId`, and more. Both canonical and alias names are accepted.
148
+ - **Response-side aliases**: Tags, items, and custom fields return `name` alongside canonical field names (`tagName`, `internalName`, `customFieldName`).
149
+ - **`saveAsDraft` defaults to `false`**: Omitting it creates a finalized transaction. No longer required on POST.
150
+ - **`POST /items/search` available**: Advanced search with filters now works for items.
151
+ - **NormalizeToArray**: Flat payment/refund/credit objects are auto-wrapped into arrays. Array format is still recommended.
152
+ - **Nil-safe deletes**: Delete endpoints return 404 (not 500) when resource not found.
153
+
154
+ ## Recommended Client Patterns
155
+
156
+ - **Serialization (Python)**: `model_dump(mode="json", by_alias=True, exclude_unset=True, exclude_none=True)`
157
+ - **Field names**: All request bodies use camelCase
158
+ - **Date serialization**: Python `date` type → `YYYY-MM-DD` strings
159
+ - **Bill payments**: Embed in bill creation body (safest). Standalone `POST /bills/{id}/payments` also works.
160
+ - **Bank records**: Use multipart `POST /magic/importBankStatementFromAttachment`
161
+ - **Scheduled bills**: Wrap as `{ status, startDate, endDate, repeat, bill: {...} }`
162
+ - **FX currency**: `currency: { sourceCurrency: "USD" }` (auto-fetches platform rate) or `currency: { sourceCurrency: "USD", exchangeRate: 1.35 }` (custom rate). **Never use `currencyCode` string** — silently ignored.
@@ -0,0 +1,139 @@
1
+ # Jaz API Resource Dependencies
2
+
3
+ > Required creation order and dependency chain for Jaz API resources.
4
+ > Understanding these dependencies prevents cascading failures.
5
+
6
+ ---
7
+
8
+ ## Dependency Graph
9
+
10
+ ```
11
+ Organization ──┐
12
+ ├── CoA ──────────────────┐
13
+ Tax Profiles ──┤ ├── Items ──┐
14
+ Currencies ────┤ │ │
15
+ Tags ──────────┘ │ │
16
+ │ │
17
+ ├── Contacts ──┐
18
+ │ │
19
+ │ ├── Invoices ──┬── Payments
20
+ │ ├── Bills ─────┤── Bill Payments
21
+ │ ├── Journals ├── Credit Notes
22
+ │ ├── Cash-In └── Credit Applications
23
+ │ └── Cash-Out
24
+
25
+ └── Bank Records (need bankAccountId from CoA)
26
+ Schedulers (need contactId + accountId)
27
+ ```
28
+
29
+ ---
30
+
31
+ ## Required Creation Order
32
+
33
+ Resources MUST be created in this order. Steps at the same level can run in parallel.
34
+
35
+ ### Level 0: Discovery (read-only)
36
+ - `GET /organization` → org name, base currency, country, lockDate
37
+ - `GET /chart-of-accounts` → existing accounts (keyed by name AND code)
38
+ - `GET /tax-profiles` → pre-existing tax profiles (map taxTypeCode → resourceId)
39
+ - `GET /organization/currencies` → enabled currencies
40
+ - `GET /contacts` → existing contacts
41
+ - `GET /items` → existing items
42
+ - `GET /tags` → existing tags
43
+
44
+ ### Level 1: Foundation (parallel)
45
+ - `POST /chart-of-accounts/bulk-upsert` → create/update accounts
46
+ - `POST /organization/currencies` → enable FX currencies
47
+ - `POST /tags` → create tags
48
+ - `POST /custom-fields` → create custom fields
49
+
50
+ **Tax profiles are READ-ONLY** — discovered in Level 0, never created.
51
+
52
+ ### Level 2: Entities (parallel)
53
+ - `POST /contacts` → create customers and suppliers
54
+ - `POST /items` → create items (needs CoA + tax profile IDs from Levels 0-1)
55
+
56
+ ### Level 3: Transactions (parallel)
57
+ - `POST /invoices` → create invoices (needs contacts + CoA + tax profiles)
58
+ - `POST /bills` → create bills (same deps, can embed payments)
59
+ - `POST /journals` → create journal entries (needs CoA)
60
+ - `POST /cash-in-journals` → cash receipts (needs bank account from CoA)
61
+ - `POST /cash-out-journals` → cash payments (needs bank account from CoA)
62
+
63
+ ### Level 4: Settlements (parallel)
64
+ - `POST /invoices/{id}/payments` → record invoice payments (needs invoice + bank account)
65
+ - `POST /bills/{id}/payments` → record bill payments (needs bill + bank account; fixed in PR #112)
66
+ - `POST /customer-credit-notes` → create customer CNs (needs contacts)
67
+ - `POST /supplier-credit-notes` → create supplier CNs (needs contacts)
68
+ - `POST /invoices/{id}/credits` → apply CNs to invoices (needs invoice + CN IDs)
69
+
70
+ ### Level 5: Bank & Recurring (parallel)
71
+ - `POST /magic/importBankStatementFromAttachment` → import bank records (needs bank account from CoA)
72
+ - `POST /scheduled/invoices` → create invoice schedulers (needs contacts + CoA)
73
+ - `POST /scheduled/bills` → create bill schedulers (needs contacts + CoA)
74
+
75
+ ### Level 5b: Optional/Experimental (Parallel)
76
+ - POST /api/v1/catalogs (needs Items from Level 2)
77
+ - POST /api/v1/deposits (needs Contacts + CoA-Bank from Level 0-1)
78
+ - POST /api/v1/fixed-assets (needs CoA from Level 0-1)
79
+ - POST /api/v1/inventory/adjustments (needs Items from Level 2)
80
+
81
+ These endpoints may not be available on all organizations. Use try/catch with graceful fallback.
82
+
83
+ ### Level 6: Verification
84
+ - `POST /generate-reports/trial-balance` → verify data integrity
85
+
86
+ ---
87
+
88
+ ## Key Dependencies Explained
89
+
90
+ | Step | Needs from... | Why |
91
+ |------|--------------|-----|
92
+ | Items | CoA (Level 1) | `saleAccountResourceId`, `purchaseAccountResourceId` |
93
+ | Items | Tax Profiles (Level 0) | `saleTaxProfileResourceId`, `purchaseTaxProfileResourceId` |
94
+ | Contacts | Nothing | Standalone — only name, currency, phone |
95
+ | Invoices | Contacts (Level 2) | `contactResourceId` |
96
+ | Invoices | CoA (Level 1) | `lineItems[].accountResourceId` (if saveAsDraft: false) |
97
+ | Invoices | Tax Profiles (Level 0) | `lineItems[].taxProfileResourceId` |
98
+ | Invoice Payments | Invoices (Level 3) | URL: `/invoices/{invoiceResourceId}/payments` |
99
+ | Bill Payments | Bills (Level 3) | URL: `/bills/{billResourceId}/payments` (or embedded) |
100
+ | Payments | CoA-Bank (Level 0) | `accountResourceId` (the bank account) |
101
+ | Credit Notes | Contacts (Level 2) | `contactResourceId` |
102
+ | Credit Applications | Invoices + CNs (Level 3-4) | Both `invoiceResourceId` and `creditNoteResourceId` |
103
+ | Bank Records | CoA-Bank (Level 0) | Multipart: `/magic/importBankStatementFromAttachment` |
104
+ | Schedulers | Contacts + CoA (Level 1-2) | Full invoice/bill payload inside |
105
+
106
+ ---
107
+
108
+ ## What Happens If You Break The Order
109
+
110
+ | Mistake | Error You'll See |
111
+ |---------|-----------------|
112
+ | Create invoices before CoA | `"lineItems[0].accountResourceId is required"` |
113
+ | Create invoices before contacts | `"contactResourceId is a required field"` |
114
+ | Create payments before invoices | 404 on `/invoices/{id}/payments` |
115
+ | Create bank records before CoA probe | Bank account ID unknown |
116
+ | Create items before CoA | Items created but with no sale/purchase accounts |
117
+ | Create credit apps before CNs | `"creditNoteResourceId must be a valid UUID"` |
118
+ | Skip tax profile discovery | Invoices with no tax |
119
+ | Map CoA by code not name | Account lookup fails (pre-existing codes differ) |
120
+
121
+ ---
122
+
123
+ ## CoA Code Mapping Strategy
124
+
125
+ Pre-existing accounts may have different codes than your templates:
126
+ - "Cost of Goods Sold" = code 310 in the API, code 5000 in template
127
+ - "Accounts Receivable" can have `code: null` in the API
128
+
129
+ **Always map by NAME, not code**. When building lookup maps, key by both:
130
+ ```javascript
131
+ coaIds[acct.name] = acct.resourceId;
132
+ if (acct.code) coaIds[acct.code] = acct.resourceId;
133
+ ```
134
+
135
+ Resource IDs are the universal identifier, not codes.
136
+
137
+ ---
138
+
139
+ *Last updated: 2026-02-08 — Extracted from chronology.md, stripped of project-specific seeder logic for portability*