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.
- package/assets/skills/api/SKILL.md +162 -0
- package/assets/skills/api/references/dependencies.md +139 -0
- package/assets/skills/api/references/endpoints.md +1299 -0
- package/assets/skills/api/references/errors.md +751 -0
- package/assets/skills/api/references/feature-glossary.md +216 -0
- package/assets/skills/api/references/field-map.md +428 -0
- package/assets/skills/api/references/full-api-surface.md +699 -0
- package/assets/skills/api/references/search-reference.md +714 -0
- package/assets/skills/conversion/SKILL.md +130 -0
- package/assets/skills/conversion/references/edge-cases.md +174 -0
- package/assets/skills/conversion/references/file-analysis.md +120 -0
- package/assets/skills/conversion/references/file-types.md +501 -0
- package/assets/skills/conversion/references/mapping-rules.md +166 -0
- package/assets/skills/conversion/references/option1-full.md +145 -0
- package/assets/skills/conversion/references/option2-quick.md +197 -0
- package/assets/skills/conversion/references/verification.md +142 -0
- package/dist/commands/init.js +76 -0
- package/dist/commands/update.js +28 -0
- package/dist/commands/versions.js +33 -0
- package/dist/index.js +52 -0
- package/dist/types/index.js +5 -0
- package/dist/utils/github.js +81 -0
- package/dist/utils/logger.js +21 -0
- package/dist/utils/template.js +49 -0
- package/package.json +44 -0
|
@@ -0,0 +1,1299 @@
|
|
|
1
|
+
# Jaz API Endpoint Reference
|
|
2
|
+
|
|
3
|
+
> Full request/response examples for every Jaz API endpoint.
|
|
4
|
+
> See SKILL.md for rules, errors.md for troubleshooting, field-map.md for name lookups.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Base URL & Auth
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
Base URL: https://api.getjaz.com
|
|
12
|
+
Auth Header: x-jk-api-key: <key>
|
|
13
|
+
Content-Type: application/json
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Date Format (All Endpoints)
|
|
19
|
+
|
|
20
|
+
**All dates must be `YYYY-MM-DD` strings** (e.g., `"2026-02-08"`).
|
|
21
|
+
|
|
22
|
+
- ISO datetime strings (e.g., `"2026-02-08T00:00:00Z"`) are REJECTED — bill payment validation returns "does not match 2006-01-02 format"
|
|
23
|
+
- Epoch milliseconds are REJECTED
|
|
24
|
+
- The OAS may declare some date fields as `integer/int64` (e.g., cash journals) but `YYYY-MM-DD` strings work in practice
|
|
25
|
+
- production clients sends all dates as `YYYY-MM-DD` via Python `date` type
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Pagination (All List Endpoints)
|
|
30
|
+
|
|
31
|
+
All GET list endpoints and POST `/search` endpoints use **`limit`/`offset` pagination** — NOT `page`/`size`.
|
|
32
|
+
|
|
33
|
+
| Property | Value |
|
|
34
|
+
|----------|-------|
|
|
35
|
+
| **GET list endpoints** | `?limit=100&offset=0` (query params) |
|
|
36
|
+
| **POST /search endpoints** | `{ "limit": 100, "offset": 0 }` (JSON body) |
|
|
37
|
+
| **Default limit** | 100 (if omitted) |
|
|
38
|
+
| **Default offset** | 0 (if omitted) |
|
|
39
|
+
| **Max limit** | 1000 |
|
|
40
|
+
| **Min limit** | 1 |
|
|
41
|
+
| **Max offset** | 65536 |
|
|
42
|
+
| **Response shape** | `{ totalPages, totalElements, data: [...] }` |
|
|
43
|
+
|
|
44
|
+
**`page`/`size` are NOT supported** — sending `?page=0&size=100` is silently ignored (API returns default 100 items as if no params were sent). Always use `limit`/`offset`.
|
|
45
|
+
|
|
46
|
+
**POST /search sort requirement**: When `offset` is present in the body (even `offset: 0`), `sort` is required:
|
|
47
|
+
```json
|
|
48
|
+
{ "limit": 100, "offset": 0, "sort": { "sortBy": ["createdAt"], "order": "DESC" } }
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 1. Organization
|
|
54
|
+
|
|
55
|
+
### GET /api/v1/organization
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
// Response (returns a LIST, not single object):
|
|
59
|
+
{
|
|
60
|
+
"totalElements": 1,
|
|
61
|
+
"totalPages": 1,
|
|
62
|
+
"data": [{
|
|
63
|
+
"resourceId": "31eb050a-...",
|
|
64
|
+
"name": "Jaz Global SG",
|
|
65
|
+
"currency": "SGD",
|
|
66
|
+
"countryCode": "SG",
|
|
67
|
+
"status": "ACTIVE",
|
|
68
|
+
"lockDate": null
|
|
69
|
+
}]
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Access org via `data[0]`. Check `lockDate` — don't seed transactions before it.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## 2. Chart of Accounts
|
|
78
|
+
|
|
79
|
+
### GET /api/v1/chart-of-accounts?limit=200&offset=0
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
// Response (flat list, NOT double-nested):
|
|
83
|
+
{
|
|
84
|
+
"totalElements": 52,
|
|
85
|
+
"totalPages": 1,
|
|
86
|
+
"data": [{
|
|
87
|
+
"resourceId": "uuid",
|
|
88
|
+
"name": "Business Bank Account",
|
|
89
|
+
"status": "ACTIVE",
|
|
90
|
+
"accountClass": "Asset",
|
|
91
|
+
"accountType": "Bank Accounts",
|
|
92
|
+
"code": "90",
|
|
93
|
+
"currencyCode": "SGD",
|
|
94
|
+
"locked": false,
|
|
95
|
+
"controlFlag": true
|
|
96
|
+
}]
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### POST /api/v1/chart-of-accounts/bulk-upsert
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
// Request:
|
|
104
|
+
{
|
|
105
|
+
"accounts": [
|
|
106
|
+
{
|
|
107
|
+
"name": "Sales Revenue",
|
|
108
|
+
"currency": "SGD",
|
|
109
|
+
"classificationType": "Operating Revenue",
|
|
110
|
+
"code": "4000"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"name": "Office Expenses",
|
|
114
|
+
"currency": "SGD",
|
|
115
|
+
"classificationType": "Operating Expense",
|
|
116
|
+
"code": "5000"
|
|
117
|
+
}
|
|
118
|
+
]
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Response:
|
|
122
|
+
{ "data": { "resourceIds": ["uuid1", "uuid2"] } }
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Upsert matches by name — existing accounts updated, new ones created.
|
|
126
|
+
|
|
127
|
+
**Important**: The bulk-upsert endpoint does NOT return individual resourceIds for created/updated accounts. After a successful bulk-upsert, you MUST re-fetch the full CoA via `GET /api/v1/chart-of-accounts` to collect the new resourceIds.
|
|
128
|
+
|
|
129
|
+
**CRITICAL: CoA code mapping — match by NAME, not code**:
|
|
130
|
+
- Pre-existing accounts may have different codes than your templates
|
|
131
|
+
- Example: "Cost of Goods Sold" = code 310 in the API, but code 5000 in template
|
|
132
|
+
- "Accounts Receivable" can have `code: null` in the API
|
|
133
|
+
- Always map template accounts to resource IDs via **name matching**, not code matching
|
|
134
|
+
- Resource IDs are the universal identifier, not codes
|
|
135
|
+
- When building lookup maps, key by BOTH `name` AND `code` for maximum compatibility:
|
|
136
|
+
```javascript
|
|
137
|
+
ctx.coaIds[acct.name] = acct.resourceId;
|
|
138
|
+
if (acct.code) ctx.coaIds[acct.code] = acct.resourceId;
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## 3. Tax Profiles
|
|
144
|
+
|
|
145
|
+
### GET /api/v1/tax-profiles?limit=100&offset=0
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
// Response:
|
|
149
|
+
{
|
|
150
|
+
"totalElements": 10,
|
|
151
|
+
"totalPages": 1,
|
|
152
|
+
"data": [{
|
|
153
|
+
"resourceId": "d8a5afbb-...",
|
|
154
|
+
"taxTypeCode": "STANDARD_RATED_SUPPLIES",
|
|
155
|
+
"displayName": "Standard-Rated Supplies (SR)",
|
|
156
|
+
"vatValue": 9,
|
|
157
|
+
"status": "ACTIVE"
|
|
158
|
+
}]
|
|
159
|
+
}
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
READ-ONLY. Map `taxTypeCode` to `resourceId`. Never create.
|
|
163
|
+
|
|
164
|
+
SG defaults: SR (9%), TX (9%), OS (0%), ZR (0%), ES (0%), EP (0%), IM (9%), plus a few more.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## 4. Currencies
|
|
169
|
+
|
|
170
|
+
### GET /api/v1/organization/currencies
|
|
171
|
+
|
|
172
|
+
```json
|
|
173
|
+
// Response (flat):
|
|
174
|
+
{
|
|
175
|
+
"totalElements": 1,
|
|
176
|
+
"totalPages": 1,
|
|
177
|
+
"data": [{
|
|
178
|
+
"currencyCode": "SGD",
|
|
179
|
+
"currencyName": "Singapore Dollar",
|
|
180
|
+
"currencySymbol": "S$",
|
|
181
|
+
"baseCurrency": true
|
|
182
|
+
}]
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### POST /api/v1/organization/currencies
|
|
187
|
+
|
|
188
|
+
```json
|
|
189
|
+
// Request:
|
|
190
|
+
{ "currencies": ["USD", "EUR", "GBP"] }
|
|
191
|
+
|
|
192
|
+
// Response:
|
|
193
|
+
{ "data": { "resourceIds": ["uuid1", "uuid2", "uuid3"] } }
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Enable currencies first, then set rates via the **separate** rate endpoints below.
|
|
197
|
+
|
|
198
|
+
### Currency Rates — `/organization-currencies` (hyphenated path)
|
|
199
|
+
|
|
200
|
+
**CRITICAL path difference**: Enable uses `/organization/currencies` (nested). Rates use `/organization-currencies` (hyphenated). Using the wrong path returns 404.
|
|
201
|
+
|
|
202
|
+
#### POST /api/v1/organization-currencies/:currencyCode/rates
|
|
203
|
+
|
|
204
|
+
```json
|
|
205
|
+
// Request:
|
|
206
|
+
{ "rate": 0.74, "rateApplicableFrom": "2026-02-10" }
|
|
207
|
+
|
|
208
|
+
// Response:
|
|
209
|
+
{ "data": "Rate added successfully" }
|
|
210
|
+
// HTTP 201
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
**Required fields**:
|
|
214
|
+
- `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`.**
|
|
215
|
+
- `rateApplicableFrom` — `YYYY-MM-DD` string (NOT ISO datetime — `"2026-02-10T00:00:00Z"` is rejected with "does not match 2006-01-02 format")
|
|
216
|
+
|
|
217
|
+
**Optional fields**:
|
|
218
|
+
- `rateApplicableTo` — `YYYY-MM-DD` string. Must be after `rateApplicableFrom` (422 `INVALID_DATE_RANGE` otherwise).
|
|
219
|
+
|
|
220
|
+
#### GET /api/v1/organization-currencies/:currencyCode/rates
|
|
221
|
+
|
|
222
|
+
```json
|
|
223
|
+
// Response:
|
|
224
|
+
{
|
|
225
|
+
"data": {
|
|
226
|
+
"totalElements": 12,
|
|
227
|
+
"totalPages": 1,
|
|
228
|
+
"data": [{
|
|
229
|
+
"resourceId": "uuid",
|
|
230
|
+
"rateFunctionalToSource": 0.74,
|
|
231
|
+
"rateSourceToFunctional": 1.3514,
|
|
232
|
+
"rateApplicableFrom": "2026-01-01",
|
|
233
|
+
"rateApplicableTo": "2026-01-31",
|
|
234
|
+
"sourceCurrencyCode": "USD",
|
|
235
|
+
"functionalCurrencyCode": "SGD",
|
|
236
|
+
"notes": { "date": "2026-01-01", "name": "Admin" }
|
|
237
|
+
}]
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
#### PUT /api/v1/organization-currencies/:currencyCode/rates/:resourceId
|
|
243
|
+
|
|
244
|
+
```json
|
|
245
|
+
// Request:
|
|
246
|
+
{ "rate": 0.71, "rateApplicableFrom": "2026-02-10" }
|
|
247
|
+
|
|
248
|
+
// Response:
|
|
249
|
+
{ "data": "Rate updated successfully" }
|
|
250
|
+
// HTTP 200
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
#### DELETE /api/v1/organization-currencies/:currencyCode/rates/:resourceId
|
|
254
|
+
|
|
255
|
+
```json
|
|
256
|
+
// Response:
|
|
257
|
+
{ "data": { "message": "Rate deleted successfully" } }
|
|
258
|
+
// HTTP 200
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
**Boundary behavior**:
|
|
262
|
+
- Base currency rates → 400: `"Cannot set rate for organization base currency"` (GET also 400: `"Cannot lookup rate for organization base currency"`)
|
|
263
|
+
- Invalid ISO code (e.g., `"XYZ"`) → 422: `"validation_error"` (vs 404 for valid-but-not-enabled codes)
|
|
264
|
+
- `rate: 0` or negative → 422: `"rate must be greater than 0"`
|
|
265
|
+
- Very small rates (e.g., `0.0001`) are accepted
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## 5. Contacts
|
|
270
|
+
|
|
271
|
+
### GET /api/v1/contacts?limit=100&offset=0
|
|
272
|
+
|
|
273
|
+
```json
|
|
274
|
+
// Response item:
|
|
275
|
+
{
|
|
276
|
+
"resourceId": "uuid",
|
|
277
|
+
"name": "Sterling Enterprises",
|
|
278
|
+
"billingName": "Sterling Enterprises",
|
|
279
|
+
"customer": true,
|
|
280
|
+
"supplier": false,
|
|
281
|
+
"currency": "SGD",
|
|
282
|
+
"phone": "+6591234567",
|
|
283
|
+
"email": "accounts@sterling.sg",
|
|
284
|
+
"taxNumber": "201812345A"
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
### POST /api/v1/contacts
|
|
289
|
+
|
|
290
|
+
```json
|
|
291
|
+
// Request:
|
|
292
|
+
{
|
|
293
|
+
"name": "Sterling Enterprises",
|
|
294
|
+
"billingName": "Sterling Enterprises",
|
|
295
|
+
"customer": true,
|
|
296
|
+
"supplier": false,
|
|
297
|
+
"currency": "SGD",
|
|
298
|
+
"phone": "+6591234567",
|
|
299
|
+
"email": "accounts@sterling.sg",
|
|
300
|
+
"taxNumber": "201812345A",
|
|
301
|
+
"addressLine1": "100 Robinson Road",
|
|
302
|
+
"addressLine2": "#08-01",
|
|
303
|
+
"city": "Singapore",
|
|
304
|
+
"postalCode": "068902",
|
|
305
|
+
"countryCode": "SG"
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Response:
|
|
309
|
+
{ "data": { "resourceId": "uuid", ... } }
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## 6. Items
|
|
315
|
+
|
|
316
|
+
### GET /api/v1/items?limit=100&offset=0
|
|
317
|
+
|
|
318
|
+
```json
|
|
319
|
+
// Response item (includes both canonical and alias names):
|
|
320
|
+
{
|
|
321
|
+
"resourceId": "uuid",
|
|
322
|
+
"internalName": "Premium Coffee Beans",
|
|
323
|
+
"name": "Premium Coffee Beans",
|
|
324
|
+
"itemCode": "PREM-COFFEE",
|
|
325
|
+
"type": "PRODUCT",
|
|
326
|
+
"status": "ACTIVE"
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### POST /api/v1/items
|
|
331
|
+
|
|
332
|
+
`name` alias is accepted (resolves to `internalName`).
|
|
333
|
+
|
|
334
|
+
```json
|
|
335
|
+
// Request (either field name works):
|
|
336
|
+
{
|
|
337
|
+
"itemCode": "PREM-COFFEE",
|
|
338
|
+
"internalName": "Premium Coffee Beans",
|
|
339
|
+
"type": "PRODUCT",
|
|
340
|
+
"appliesToSale": true,
|
|
341
|
+
"saleItemName": "Premium Coffee Beans",
|
|
342
|
+
"salePrice": 45.00,
|
|
343
|
+
"saleAccountResourceId": "uuid",
|
|
344
|
+
"saleTaxProfileResourceId": "uuid",
|
|
345
|
+
"appliesToPurchase": true,
|
|
346
|
+
"purchaseItemName": "Premium Coffee Beans",
|
|
347
|
+
"purchasePrice": 25.00,
|
|
348
|
+
"purchaseAccountResourceId": "uuid",
|
|
349
|
+
"purchaseTaxProfileResourceId": "uuid"
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Response:
|
|
353
|
+
{ "data": { "resourceId": "uuid", ... } }
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## 7. Invoices
|
|
359
|
+
|
|
360
|
+
### POST /api/v1/invoices
|
|
361
|
+
|
|
362
|
+
```json
|
|
363
|
+
// Request:
|
|
364
|
+
{
|
|
365
|
+
"contactResourceId": "uuid",
|
|
366
|
+
"saveAsDraft": false,
|
|
367
|
+
"reference": "INV-001",
|
|
368
|
+
"valueDate": "2026-02-08",
|
|
369
|
+
"dueDate": "2026-03-10",
|
|
370
|
+
"currency": "SGD",
|
|
371
|
+
"lineItems": [{
|
|
372
|
+
"name": "Premium Coffee Beans x 50",
|
|
373
|
+
"unitPrice": 45.00,
|
|
374
|
+
"quantity": 50,
|
|
375
|
+
"accountResourceId": "uuid",
|
|
376
|
+
"taxProfileResourceId": "uuid",
|
|
377
|
+
"itemResourceId": "uuid"
|
|
378
|
+
}]
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Response:
|
|
382
|
+
{ "data": { "resourceId": "uuid", "reference": "INV-001" } }
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
**saveAsDraft**: Defaults to `false` — omitting it creates a finalized transaction. Sending `saveAsDraft: true` creates a draft.
|
|
386
|
+
|
|
387
|
+
**GET response note**: When fetching invoices via GET, line items use `organizationAccountResourceId` (not `accountResourceId`). POST uses `accountResourceId`. Request-side aliases resolve `issueDate` → `valueDate`, `bankAccountResourceId` → `accountResourceId`, etc.
|
|
388
|
+
|
|
389
|
+
**FX (foreign currency) invoices**: For invoices in a non-base currency, use the `currency` OBJECT form:
|
|
390
|
+
- **`currency: { sourceCurrency: "MYR" }`** — platform auto-fetches rate from ECB (FRANKFURTER). Response shows `rateSource: "EXTERNAL"`, `providerName: "FRANKFURTER"`.
|
|
391
|
+
- **`currency: { sourceCurrency: "MYR", exchangeRate: 3.15 }`** — custom rate. Response shows `rateSource: "INTERNAL_TRANSACTION"`, `providerName: "CUSTOM"`.
|
|
392
|
+
- **`currencyCode: "MYR"` (string) is SILENTLY IGNORED** — the invoice is created in the org's base currency (e.g., SGD) with rate 1:1. No error returned. This is a major gotcha.
|
|
393
|
+
- **`currency: "USD"` (string)** causes "Invalid request body" error (400).
|
|
394
|
+
|
|
395
|
+
**Rate hierarchy** (when using `currency: { sourceCurrency }` without `exchangeRate`):
|
|
396
|
+
1. Org-level rate (set via `/organization-currencies/:code/rates`) — auto-filled if exists
|
|
397
|
+
2. Platform rate (ECB via FRANKFURTER) — auto-fetched if no org rate
|
|
398
|
+
3. Transaction-level rate (via `exchangeRate` in the `currency` object) — overrides all
|
|
399
|
+
|
|
400
|
+
**Invoice payments**: `POST /invoices/{invoiceResourceId}/payments` works reliably as a standalone endpoint.
|
|
401
|
+
|
|
402
|
+
---
|
|
403
|
+
|
|
404
|
+
## 8. Bills
|
|
405
|
+
|
|
406
|
+
### POST /api/v1/bills
|
|
407
|
+
|
|
408
|
+
Same structure as invoices. All field names identical. FX currency rules also apply — use `currency` object form (see Section 7 FX notes).
|
|
409
|
+
|
|
410
|
+
**Bill payments**: The standalone `POST /bills/{id}/payments` endpoint was broken (nil pointer dereference in the API backend) — **fixed in backend PR #112**. Both standalone and embedded payment approaches now work. The embed-in-creation pattern remains a valid alternative:
|
|
411
|
+
|
|
412
|
+
```json
|
|
413
|
+
// Request — Bill with embedded payment:
|
|
414
|
+
{
|
|
415
|
+
"contactResourceId": "uuid",
|
|
416
|
+
"saveAsDraft": false,
|
|
417
|
+
"reference": "BILL-001",
|
|
418
|
+
"valueDate": "2026-02-08",
|
|
419
|
+
"dueDate": "2026-03-10",
|
|
420
|
+
"lineItems": [{
|
|
421
|
+
"name": "Office Supplies",
|
|
422
|
+
"unitPrice": 500.00,
|
|
423
|
+
"quantity": 1,
|
|
424
|
+
"accountResourceId": "uuid",
|
|
425
|
+
"taxProfileResourceId": "uuid"
|
|
426
|
+
}],
|
|
427
|
+
"payments": [{
|
|
428
|
+
"paymentAmount": 500.00,
|
|
429
|
+
"transactionAmount": 500.00,
|
|
430
|
+
"accountResourceId": "uuid-of-bank-account",
|
|
431
|
+
"paymentMethod": "BANK_TRANSFER",
|
|
432
|
+
"reference": "PAY-BILL-001",
|
|
433
|
+
"valueDate": "2026-02-08"
|
|
434
|
+
}]
|
|
435
|
+
}
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
The `payments` array uses the same 6-field structure as standalone payments. production clients uses embedded payments for bills.
|
|
439
|
+
|
|
440
|
+
**Note**: Bill payments do NOT support `TransactionFeeCollected` (model field missing). Only invoice payments support collected transaction fees.
|
|
441
|
+
|
|
442
|
+
### Withholding Tax on Line Items
|
|
443
|
+
|
|
444
|
+
Bills and supplier credit notes support withholding tax per line item:
|
|
445
|
+
|
|
446
|
+
```json
|
|
447
|
+
{
|
|
448
|
+
"lineItems": [{
|
|
449
|
+
"name": "Consulting services",
|
|
450
|
+
"unitPrice": 10000,
|
|
451
|
+
"quantity": 1,
|
|
452
|
+
"accountResourceId": "expense-uuid",
|
|
453
|
+
"withholdingTax": {
|
|
454
|
+
"code": "WC010",
|
|
455
|
+
"rate": 10,
|
|
456
|
+
"description": "Professional fees"
|
|
457
|
+
}
|
|
458
|
+
}]
|
|
459
|
+
}
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
**Retry pattern**: If the organization doesn't support withholding tax, the API returns `WITHHOLDING_CODE_NOT_FOUND`. On this error, remove the `withholdingTax` field from all line items and retry the request.
|
|
463
|
+
|
|
464
|
+
---
|
|
465
|
+
|
|
466
|
+
## 9. Journals
|
|
467
|
+
|
|
468
|
+
### POST /api/v1/journals
|
|
469
|
+
|
|
470
|
+
```json
|
|
471
|
+
// Request:
|
|
472
|
+
{
|
|
473
|
+
"saveAsDraft": false,
|
|
474
|
+
"reference": "JV-001",
|
|
475
|
+
"valueDate": "2026-02-08",
|
|
476
|
+
"journalEntries": [
|
|
477
|
+
{ "accountResourceId": "uuid", "amount": 500, "type": "DEBIT", "contactResourceId": "uuid" },
|
|
478
|
+
{ "accountResourceId": "uuid", "amount": 500, "type": "CREDIT" }
|
|
479
|
+
]
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Response:
|
|
483
|
+
{ "data": { "resourceId": "uuid" } }
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
**CRITICAL corrections from live testing**:
|
|
487
|
+
- Each entry uses `amount` (number) + `type`: `"DEBIT"` or `"CREDIT"` (UPPERCASE strings)
|
|
488
|
+
- Do NOT use `debit`/`credit` as separate number fields — that is WRONG
|
|
489
|
+
- Do NOT include `currency` at top level — causes "Invalid request body"
|
|
490
|
+
- Total DEBIT amounts MUST equal total CREDIT amounts
|
|
491
|
+
- `contactResourceId` is optional per entry
|
|
492
|
+
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
## 10. Cash Entries
|
|
496
|
+
|
|
497
|
+
### POST /api/v1/cash-in-journals
|
|
498
|
+
### POST /api/v1/cash-out-journals
|
|
499
|
+
|
|
500
|
+
```json
|
|
501
|
+
// Request — Cash-In example:
|
|
502
|
+
{
|
|
503
|
+
"saveAsDraft": false,
|
|
504
|
+
"reference": "CI-001",
|
|
505
|
+
"valueDate": "2026-02-08",
|
|
506
|
+
"accountResourceId": "uuid-of-bank-account",
|
|
507
|
+
"journalEntries": [
|
|
508
|
+
{ "accountResourceId": "uuid-of-revenue-account", "amount": 500, "type": "CREDIT" }
|
|
509
|
+
]
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
// Request — Cash-Out example:
|
|
513
|
+
{
|
|
514
|
+
"saveAsDraft": false,
|
|
515
|
+
"reference": "CO-001",
|
|
516
|
+
"valueDate": "2026-02-08",
|
|
517
|
+
"accountResourceId": "uuid-of-bank-account",
|
|
518
|
+
"journalEntries": [
|
|
519
|
+
{ "accountResourceId": "uuid-of-expense-account", "amount": 300, "type": "DEBIT" }
|
|
520
|
+
]
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// Response:
|
|
524
|
+
{ "data": { "resourceId": "uuid" } }
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
**CRITICAL corrections from live testing**:
|
|
528
|
+
- `saveAsDraft` is REQUIRED — omitting it causes validation failure
|
|
529
|
+
- `accountResourceId` at top level = the BANK account (NOT `bankAccountResourceId`)
|
|
530
|
+
- `journalEntries` array for the offset entries — same `amount` + `type` format as regular journals
|
|
531
|
+
- Do NOT use a flat structure with `amount`, `bankAccountResourceId`, `description` — that is WRONG
|
|
532
|
+
- The system auto-creates the bank-side entry; you only specify the offset entries in `journalEntries`
|
|
533
|
+
- For cash-in: offset entries are typically CREDIT (revenue/liability)
|
|
534
|
+
- For cash-out: offset entries are typically DEBIT (expense/asset)
|
|
535
|
+
|
|
536
|
+
---
|
|
537
|
+
|
|
538
|
+
## 11. Payments
|
|
539
|
+
|
|
540
|
+
### POST /api/v1/invoices/{invoiceResourceId}/payments (WORKS)
|
|
541
|
+
### POST /api/v1/bills/{billResourceId}/payments (FIXED in PR #112)
|
|
542
|
+
|
|
543
|
+
```json
|
|
544
|
+
// Request:
|
|
545
|
+
{
|
|
546
|
+
"payments": [{
|
|
547
|
+
"paymentAmount": 2250.00,
|
|
548
|
+
"transactionAmount": 2250.00,
|
|
549
|
+
"accountResourceId": "uuid-of-bank-account",
|
|
550
|
+
"paymentMethod": "BANK_TRANSFER",
|
|
551
|
+
"reference": "PAY-001",
|
|
552
|
+
"valueDate": "2026-02-05"
|
|
553
|
+
}]
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// Response:
|
|
557
|
+
{ "data": { "resourceIds": ["uuid"] } }
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
**CRITICAL corrections from live testing** — payments require 6 fields:
|
|
561
|
+
- `paymentAmount` — NOT `amount`. The **bank account currency** amount (actual cash moved from bank).
|
|
562
|
+
- `transactionAmount` — The **transaction document currency (invoice/bill/credit note)** amount (applied to the balance). Equal to `paymentAmount` for same-currency. For cross-currency (e.g., USD invoice paid from SGD bank at 1.35): `paymentAmount: 1350` (SGD), `transactionAmount: 1000` (USD).
|
|
563
|
+
- `accountResourceId` — NOT `bankAccountResourceId`. This IS the bank account UUID.
|
|
564
|
+
- `paymentMethod` — required string: `"BANK_TRANSFER"` (other values may exist but this works universally)
|
|
565
|
+
- `reference` — payment reference string (required)
|
|
566
|
+
- `valueDate` — NOT `paymentDate`. ISO date string.
|
|
567
|
+
|
|
568
|
+
Always wrap in `{ payments: [...] }` even for single payment.
|
|
569
|
+
|
|
570
|
+
**Bill payments standalone endpoint**: Was broken (nil pointer dereference) — **fixed in backend PR #112**. Now works for basic payments. Embed-in-creation pattern also remains valid (see Section 8). production clients uses embedded payments for bills.
|
|
571
|
+
|
|
572
|
+
**TransactionFeeCollected**: NOT supported on bill payments (model field missing in the API backend). Only invoice payments support collected transaction fees.
|
|
573
|
+
|
|
574
|
+
---
|
|
575
|
+
|
|
576
|
+
## 12. Credit Notes
|
|
577
|
+
|
|
578
|
+
### POST /api/v1/customer-credit-notes
|
|
579
|
+
### POST /api/v1/supplier-credit-notes
|
|
580
|
+
|
|
581
|
+
```json
|
|
582
|
+
// Request (same structure for both):
|
|
583
|
+
{
|
|
584
|
+
"contactResourceId": "uuid",
|
|
585
|
+
"saveAsDraft": false,
|
|
586
|
+
"reference": "CN-001",
|
|
587
|
+
"valueDate": "2026-02-08",
|
|
588
|
+
"lineItems": [{
|
|
589
|
+
"name": "Return - Defective items",
|
|
590
|
+
"unitPrice": 45.00,
|
|
591
|
+
"quantity": 5,
|
|
592
|
+
"accountResourceId": "uuid",
|
|
593
|
+
"taxProfileResourceId": "uuid"
|
|
594
|
+
}]
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Response:
|
|
598
|
+
{ "data": { "resourceId": "uuid" } }
|
|
599
|
+
```
|
|
600
|
+
|
|
601
|
+
### POST /api/v1/invoices/{invoiceResourceId}/credits
|
|
602
|
+
|
|
603
|
+
```json
|
|
604
|
+
// Request:
|
|
605
|
+
{
|
|
606
|
+
"credits": [
|
|
607
|
+
{ "creditNoteResourceId": "uuid-of-credit-note", "amountApplied": 225.00 }
|
|
608
|
+
]
|
|
609
|
+
}
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
**CRITICAL corrections from live testing**:
|
|
613
|
+
- Wrap in `credits` array (NOT a flat object)
|
|
614
|
+
- Use `amountApplied` (NOT `amount`)
|
|
615
|
+
- Can apply multiple credit notes in one call by adding more entries to the array
|
|
616
|
+
|
|
617
|
+
---
|
|
618
|
+
|
|
619
|
+
## 13. Tags
|
|
620
|
+
|
|
621
|
+
### GET /api/v1/tags?limit=100&offset=0
|
|
622
|
+
### POST /api/v1/tags
|
|
623
|
+
|
|
624
|
+
`name` alias is accepted (resolves to `tagName`).
|
|
625
|
+
|
|
626
|
+
```json
|
|
627
|
+
// Request (either field name works):
|
|
628
|
+
{ "tagName": "Department: Sales" }
|
|
629
|
+
// or: { "name": "Department: Sales" }
|
|
630
|
+
|
|
631
|
+
// Response (includes both canonical and alias names):
|
|
632
|
+
{ "data": { "tagName": "Department: Sales", "name": "Department: Sales", "status": "ACTIVE", "resourceId": "uuid" } }
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
---
|
|
636
|
+
|
|
637
|
+
## 14. Custom Fields
|
|
638
|
+
|
|
639
|
+
### GET /api/v1/custom-fields?limit=100&offset=0
|
|
640
|
+
### POST /api/v1/custom-fields
|
|
641
|
+
|
|
642
|
+
```json
|
|
643
|
+
// Request (TEXT type):
|
|
644
|
+
{ "name": "PO Number", "type": "TEXT", "printOnDocuments": false }
|
|
645
|
+
|
|
646
|
+
// Request (DROPDOWN type with options):
|
|
647
|
+
{ "name": "Priority", "type": "DROPDOWN", "printOnDocuments": false, "options": ["Low", "Medium", "High"] }
|
|
648
|
+
|
|
649
|
+
// Request (DATE type):
|
|
650
|
+
{ "name": "Delivery Date", "type": "DATE", "printOnDocuments": true }
|
|
651
|
+
|
|
652
|
+
// Response (includes both canonical and alias names):
|
|
653
|
+
{ "data": { "customFieldName": "PO Number", "name": "PO Number", "status": "ACTIVE", "resourceId": "uuid" } }
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
**CRITICAL notes from live testing**:
|
|
657
|
+
- POST uses `name`, GET returns both `customFieldName` and `name`
|
|
658
|
+
- Valid `type` values: `"TEXT"`, `"DATE"`, `"DROPDOWN"` (UPPERCASE)
|
|
659
|
+
- `printOnDocuments` is REQUIRED — omitting it causes 400
|
|
660
|
+
- Do NOT send `appliesTo` field — causes "Invalid request body"
|
|
661
|
+
- Only send: `name`, `type`, `printOnDocuments` (and `options` for DROPDOWN)
|
|
662
|
+
- For DROPDOWN type, `options` array works (without `appliesTo`)
|
|
663
|
+
|
|
664
|
+
---
|
|
665
|
+
|
|
666
|
+
## 14b. Inventory Items
|
|
667
|
+
|
|
668
|
+
### POST /api/v1/inventory-items
|
|
669
|
+
|
|
670
|
+
```json
|
|
671
|
+
// Request:
|
|
672
|
+
{
|
|
673
|
+
"internalName": "Widget A",
|
|
674
|
+
"itemCode": "WDG-A",
|
|
675
|
+
"unit": "pcs",
|
|
676
|
+
"appliesToSale": true,
|
|
677
|
+
"appliesToPurchase": true,
|
|
678
|
+
"saleItemName": "Widget A",
|
|
679
|
+
"salePrice": 50.00,
|
|
680
|
+
"saleAccountResourceId": "uuid-operating-revenue",
|
|
681
|
+
"saleTaxProfileResourceId": "uuid-tax",
|
|
682
|
+
"purchaseItemName": "Widget A",
|
|
683
|
+
"purchasePrice": 30.00,
|
|
684
|
+
"purchaseAccountResourceId": "uuid-inventory-account",
|
|
685
|
+
"purchaseTaxProfileResourceId": "uuid-tax",
|
|
686
|
+
"costingMethod": "WAC",
|
|
687
|
+
"cogsResourceId": "uuid-direct-costs",
|
|
688
|
+
"blockInsufficientDeductions": false,
|
|
689
|
+
"inventoryAccountResourceId": "uuid-inventory-account"
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// Response:
|
|
693
|
+
{ "data": { "resourceId": "uuid" } }
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
**CRITICAL notes from live testing**:
|
|
697
|
+
- `unit` is REQUIRED (e.g., `"pcs"`, `"box"`, `"kg"`) — omitting causes ITEM_UNIT_EMPTY_ERROR
|
|
698
|
+
- `costingMethod` must be `"FIXED"` or `"WAC"` (NOT `"FIXED_COST"`)
|
|
699
|
+
- `purchaseAccountResourceId` MUST point to an Inventory-type CoA account (NOT Direct Costs) — wrong type causes INVALID_ACCOUNT_TYPE_INVENTORY
|
|
700
|
+
- `inventoryAccountResourceId` also must be Inventory-type
|
|
701
|
+
- Delete inventory items via `DELETE /items/:id` (NOT `/inventory-items/:id`)
|
|
702
|
+
- `GET /inventory-item-balance/:id` returns balance per item
|
|
703
|
+
- `GET /inventory-balances/:status` currently returns 500 (known bug)
|
|
704
|
+
|
|
705
|
+
---
|
|
706
|
+
|
|
707
|
+
## 14c. Cash Transfer Journals
|
|
708
|
+
|
|
709
|
+
### POST /api/v1/cash-transfer-journals
|
|
710
|
+
|
|
711
|
+
```json
|
|
712
|
+
// Request:
|
|
713
|
+
{
|
|
714
|
+
"valueDate": "2026-02-09",
|
|
715
|
+
"saveAsDraft": false,
|
|
716
|
+
"reference": "XFER-001",
|
|
717
|
+
"cashOut": { "accountResourceId": "uuid-from-bank", "amount": 500 },
|
|
718
|
+
"cashIn": { "accountResourceId": "uuid-to-bank", "amount": 500 }
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// Response:
|
|
722
|
+
{ "data": { "resourceId": "uuid" } }
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
**CRITICAL**: Uses `cashOut`/`cashIn` sub-objects — NOT `fromAccountResourceId`/`toAccountResourceId`/`amount` flat fields. Each sub-object has `accountResourceId` and `amount`.
|
|
726
|
+
|
|
727
|
+
---
|
|
728
|
+
|
|
729
|
+
## 14d. Credit Note Refunds
|
|
730
|
+
|
|
731
|
+
### POST /api/v1/customer-credit-notes/{id}/refunds
|
|
732
|
+
|
|
733
|
+
```json
|
|
734
|
+
// Request:
|
|
735
|
+
{
|
|
736
|
+
"refunds": [{
|
|
737
|
+
"refundAmount": 75.00,
|
|
738
|
+
"refundMethod": "BANK_TRANSFER",
|
|
739
|
+
"transactionAmount": 75.00,
|
|
740
|
+
"accountResourceId": "uuid-bank",
|
|
741
|
+
"reference": "REFUND-001",
|
|
742
|
+
"valueDate": "2026-02-09"
|
|
743
|
+
}]
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// Response:
|
|
747
|
+
{ "data": { "resourceIds": ["uuid"] } }
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
**CRITICAL**: Uses `refunds` wrapper with `refundAmount`/`refundMethod` — NOT `payments` wrapper with `paymentAmount`/`paymentMethod`.
|
|
751
|
+
|
|
752
|
+
---
|
|
753
|
+
|
|
754
|
+
## 14e. Contact Groups
|
|
755
|
+
|
|
756
|
+
### POST /api/v1/contact-groups
|
|
757
|
+
|
|
758
|
+
```json
|
|
759
|
+
// Request:
|
|
760
|
+
{ "name": "VIP Clients", "description": "Top-tier customers" }
|
|
761
|
+
|
|
762
|
+
// Response:
|
|
763
|
+
{ "data": { "resourceId": "uuid" } }
|
|
764
|
+
```
|
|
765
|
+
|
|
766
|
+
**Known bug**: `PUT /contact-groups/:id` returns 500. Use create + delete as workaround.
|
|
767
|
+
|
|
768
|
+
---
|
|
769
|
+
|
|
770
|
+
## 14f. Organization Bookmarks
|
|
771
|
+
|
|
772
|
+
### POST /api/v1/organization/bookmarks
|
|
773
|
+
|
|
774
|
+
```json
|
|
775
|
+
// Request:
|
|
776
|
+
{
|
|
777
|
+
"items": [{
|
|
778
|
+
"name": "Company Policy",
|
|
779
|
+
"value": "https://example.com/policy",
|
|
780
|
+
"categoryCode": "GENERAL_INFORMATION",
|
|
781
|
+
"datatypeCode": "LINK"
|
|
782
|
+
}]
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
// Response:
|
|
786
|
+
{ "data": [{ "name": "Company Policy", "resourceId": "uuid" }] }
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
Valid `categoryCode`: `AUDIT_AND_ASSURANCE`, `BANKING_AND_FINANCE`, `BUDGETS_AND_CONTROLS`, `EMPLOYEES_AND_PAYROLL`, `EXTERNAL_DOCUMENTS`, `GENERAL_INFORMATION`, `OWNERS_AND_DIRECTORS`, `TAXATION_AND_COMPLIANCE`, `WORKFLOWS_AND_PROCESSES`.
|
|
790
|
+
|
|
791
|
+
Valid `datatypeCode`: `TEXT`, `NUMBER`, `BOOLEAN`, `DATE`, `LINK`.
|
|
792
|
+
|
|
793
|
+
---
|
|
794
|
+
|
|
795
|
+
## 15. Bank Records
|
|
796
|
+
|
|
797
|
+
### POST /api/v1/magic/importBankStatementFromAttachment (multipart)
|
|
798
|
+
|
|
799
|
+
The only endpoint for creating bank records. Uses multipart form upload:
|
|
800
|
+
|
|
801
|
+
```
|
|
802
|
+
POST /api/v1/magic/importBankStatementFromAttachment
|
|
803
|
+
Content-Type: multipart/form-data
|
|
804
|
+
|
|
805
|
+
Fields:
|
|
806
|
+
- sourceFile: CSV/OFX bank statement file (NOT "file")
|
|
807
|
+
- accountResourceId: UUID of the bank account CoA entry (NOT "bankAccountResourceId")
|
|
808
|
+
- businessTransactionType: "BANK_STATEMENT"
|
|
809
|
+
- sourceType: "FILE" (valid values: URL, FILE)
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
CSV format: `Date,Description,Debit,Credit` — maps to Date, Description, Cash-out, Cash-in.
|
|
813
|
+
|
|
814
|
+
Multipart import is the more reliable method. Use it when JSON POST returns errors.
|
|
815
|
+
|
|
816
|
+
---
|
|
817
|
+
|
|
818
|
+
## 16. Schedulers
|
|
819
|
+
|
|
820
|
+
### POST /api/v1/scheduled/invoices
|
|
821
|
+
|
|
822
|
+
```json
|
|
823
|
+
// Request:
|
|
824
|
+
{
|
|
825
|
+
"repeat": "MONTHLY",
|
|
826
|
+
"startDate": "2026-03-01",
|
|
827
|
+
"endDate": "2026-12-01",
|
|
828
|
+
"invoice": {
|
|
829
|
+
"contactResourceId": "uuid",
|
|
830
|
+
"saveAsDraft": false,
|
|
831
|
+
"reference": "SCH-INV-001",
|
|
832
|
+
"valueDate": "2026-03-01",
|
|
833
|
+
"dueDate": "2026-03-31",
|
|
834
|
+
"lineItems": [{
|
|
835
|
+
"name": "Monthly retainer",
|
|
836
|
+
"unitPrice": 3000.00,
|
|
837
|
+
"quantity": 1,
|
|
838
|
+
"accountResourceId": "uuid",
|
|
839
|
+
"taxProfileResourceId": "uuid"
|
|
840
|
+
}]
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
```
|
|
844
|
+
|
|
845
|
+
### POST /api/v1/scheduled/bills
|
|
846
|
+
|
|
847
|
+
Same but with `"bill"` wrapper instead of `"invoice"`.
|
|
848
|
+
|
|
849
|
+
### POST /api/v1/scheduled/journals
|
|
850
|
+
|
|
851
|
+
```json
|
|
852
|
+
// Request (FLAT structure — NOT nested in "journal" wrapper):
|
|
853
|
+
{
|
|
854
|
+
"reference": "SCHED-JNL-001",
|
|
855
|
+
"valueDate": "2026-03-01",
|
|
856
|
+
"saveAsDraft": false,
|
|
857
|
+
"schedulerEntries": [
|
|
858
|
+
{ "accountResourceId": "uuid", "amount": 100, "type": "DEBIT", "name": "Monthly accrual" },
|
|
859
|
+
{ "accountResourceId": "uuid", "amount": 100, "type": "CREDIT", "name": "Monthly accrual" }
|
|
860
|
+
],
|
|
861
|
+
"repeat": "MONTHLY",
|
|
862
|
+
"startDate": "2026-03-01",
|
|
863
|
+
"endDate": "2026-12-01"
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
// Response:
|
|
867
|
+
{ "data": { "resourceId": "uuid" } }
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
**CRITICAL**: Scheduled journals use FLAT structure with `schedulerEntries` — NOT a nested `journal` wrapper like scheduled invoices/bills use `invoice`/`bill` wrapper. `reference`, `valueDate`, `saveAsDraft` are at top level alongside `repeat`/`startDate`/`endDate`.
|
|
871
|
+
|
|
872
|
+
**CRITICAL notes from live testing**:
|
|
873
|
+
- Recurrence field is `repeat` — NOT `frequency` or `interval`. Using `frequency` or `interval` silently defaults to ONE_TIME.
|
|
874
|
+
- Valid `repeat` values: `"WEEKLY"`, `"MONTHLY"`, `"QUARTERLY"`, `"YEARLY"`
|
|
875
|
+
- `saveAsDraft: false` is REQUIRED on the wrapped invoice/bill. Using `saveAsDraft: true` causes `INVALID_SALE_STATUS` (invoices) or `INVALID_PURCHASE_STATUS` (bills).
|
|
876
|
+
- Since `saveAsDraft: false`, every line item MUST have `accountResourceId`.
|
|
877
|
+
- Response uses `interval` field (not `repeat`): `{ "status": "ACTIVE", "interval": "MONTHLY", ... }`
|
|
878
|
+
|
|
879
|
+
---
|
|
880
|
+
|
|
881
|
+
## 17. Reports
|
|
882
|
+
|
|
883
|
+
### POST /api/v1/generate-reports/trial-balance
|
|
884
|
+
|
|
885
|
+
```json
|
|
886
|
+
// Request:
|
|
887
|
+
{ "startDate": "2025-11-10", "endDate": "2026-02-08" }
|
|
888
|
+
```
|
|
889
|
+
|
|
890
|
+
Both dates required.
|
|
891
|
+
|
|
892
|
+
### POST /api/v1/generate-reports/balance-sheet
|
|
893
|
+
|
|
894
|
+
```json
|
|
895
|
+
// Request:
|
|
896
|
+
{ "primarySnapshotDate": "2026-02-28" }
|
|
897
|
+
```
|
|
898
|
+
|
|
899
|
+
Uses `primarySnapshotDate` — NOT `endDate`. Optional: `secondarySnapshotDates` array for comparison periods.
|
|
900
|
+
|
|
901
|
+
### POST /api/v1/generate-reports/profit-and-loss
|
|
902
|
+
|
|
903
|
+
```json
|
|
904
|
+
// Request:
|
|
905
|
+
{ "primarySnapshotDate": "2026-02-28", "secondarySnapshotDate": "2026-01-01" }
|
|
906
|
+
```
|
|
907
|
+
|
|
908
|
+
Both `primarySnapshotDate` and `secondarySnapshotDate` required. NOT `startDate`/`endDate`.
|
|
909
|
+
|
|
910
|
+
### POST /api/v1/generate-reports/general-ledger
|
|
911
|
+
|
|
912
|
+
```json
|
|
913
|
+
// Request:
|
|
914
|
+
{ "startDate": "2026-01-01", "endDate": "2026-02-28", "groupBy": "ACCOUNT" }
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
`groupBy` is required. Valid values: `"ACCOUNT"`. Uses `startDate`/`endDate` like trial balance.
|
|
918
|
+
|
|
919
|
+
### POST /api/v1/generate-reports/cashflow
|
|
920
|
+
|
|
921
|
+
```json
|
|
922
|
+
{ "primaryStartDate": "2026-01-01", "primaryEndDate": "2026-02-28" }
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
Uses `primaryStartDate`/`primaryEndDate` — NOT `primarySnapshotDate`.
|
|
926
|
+
|
|
927
|
+
### POST /api/v1/generate-reports/cash-balance
|
|
928
|
+
|
|
929
|
+
```json
|
|
930
|
+
{ "reportDate": "2026-02-28" }
|
|
931
|
+
```
|
|
932
|
+
|
|
933
|
+
Single date field `reportDate`.
|
|
934
|
+
|
|
935
|
+
### POST /api/v1/generate-reports/ar-report
|
|
936
|
+
### POST /api/v1/generate-reports/ap-report
|
|
937
|
+
|
|
938
|
+
```json
|
|
939
|
+
{ "endDate": "2026-02-28" }
|
|
940
|
+
```
|
|
941
|
+
|
|
942
|
+
Single date field `endDate`.
|
|
943
|
+
|
|
944
|
+
### POST /api/v1/generate-reports/ar-summary-report
|
|
945
|
+
### POST /api/v1/generate-reports/ap-summary-report
|
|
946
|
+
|
|
947
|
+
```json
|
|
948
|
+
{ "startDate": "2026-01-01", "endDate": "2026-02-28" }
|
|
949
|
+
```
|
|
950
|
+
|
|
951
|
+
Both `startDate` and `endDate` required.
|
|
952
|
+
|
|
953
|
+
### POST /api/v1/generate-reports/bank-balance-summary
|
|
954
|
+
|
|
955
|
+
```json
|
|
956
|
+
{ "primarySnapshotDate": "2026-02-28" }
|
|
957
|
+
```
|
|
958
|
+
|
|
959
|
+
### POST /api/v1/generate-reports/equity-movement
|
|
960
|
+
|
|
961
|
+
```json
|
|
962
|
+
{ "primarySnapshotStartDate": "2026-01-01", "primarySnapshotEndDate": "2026-02-28" }
|
|
963
|
+
```
|
|
964
|
+
|
|
965
|
+
Uses `primarySnapshotStartDate`/`primarySnapshotEndDate` — yet another pair of field names.
|
|
966
|
+
|
|
967
|
+
### Data Exports
|
|
968
|
+
|
|
969
|
+
Data exports use SIMPLER field names than generate-reports:
|
|
970
|
+
|
|
971
|
+
| Export | Fields |
|
|
972
|
+
|--------|--------|
|
|
973
|
+
| `/data-exports/trial-balance` | `startDate`, `endDate` |
|
|
974
|
+
| `/data-exports/profit-and-loss` | `startDate`, `endDate` |
|
|
975
|
+
| `/data-exports/general-ledger` | `startDate`, `endDate`, `groupBy: "ACCOUNT"` |
|
|
976
|
+
| `/data-exports/ar-report` | `endDate` |
|
|
977
|
+
|
|
978
|
+
**Note**: P&L export uses `startDate`/`endDate` (NOT `primarySnapshotDate`/`secondarySnapshotDate` like generate-reports).
|
|
979
|
+
|
|
980
|
+
---
|
|
981
|
+
|
|
982
|
+
## 18. Cashflow Transactions Search
|
|
983
|
+
|
|
984
|
+
### POST /api/v1/cashflow-transactions/search
|
|
985
|
+
|
|
986
|
+
Searches across ALL cashflow transactions (invoices, bills, credit notes, journals, cash entries, payments). This is the unified transaction ledger.
|
|
987
|
+
|
|
988
|
+
```json
|
|
989
|
+
// Request:
|
|
990
|
+
{
|
|
991
|
+
"filter": {
|
|
992
|
+
"businessTransactionType": { "eq": "SALE" },
|
|
993
|
+
"valueDate": { "gte": "2026-01-01" }
|
|
994
|
+
},
|
|
995
|
+
"sort": { "sortBy": ["valueDate"], "order": "DESC" },
|
|
996
|
+
"limit": 100
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
// Response (flat, same as all other search endpoints):
|
|
1000
|
+
{
|
|
1001
|
+
"totalElements": 1228,
|
|
1002
|
+
"totalPages": 13,
|
|
1003
|
+
"data": [{
|
|
1004
|
+
"resourceId": "uuid",
|
|
1005
|
+
"direction": "PAYIN",
|
|
1006
|
+
"totalAmount": 2250.00,
|
|
1007
|
+
"balanceAmount": 0,
|
|
1008
|
+
"grossAmount": 2250.00,
|
|
1009
|
+
"feeAmount": 0,
|
|
1010
|
+
"valueDate": 1706227200000,
|
|
1011
|
+
"matchDate": 1706313600000,
|
|
1012
|
+
"businessTransactionType": "SALE",
|
|
1013
|
+
"businessTransactionReference": "INV-001",
|
|
1014
|
+
"businessTransactionStatus": "POSTED",
|
|
1015
|
+
"currencyCode": "SGD",
|
|
1016
|
+
"currencySymbol": "S$",
|
|
1017
|
+
"functionalCurrencyCode": "SGD",
|
|
1018
|
+
"crossCurrency": false,
|
|
1019
|
+
"contact": { "name": "Acme Corp", "resourceId": "uuid" },
|
|
1020
|
+
"account": { "name": "Accounts Receivable", "resourceId": "uuid" },
|
|
1021
|
+
"organizationAccountResourceId": "uuid",
|
|
1022
|
+
"tags": ["Department: Sales"]
|
|
1023
|
+
}]
|
|
1024
|
+
}
|
|
1025
|
+
```
|
|
1026
|
+
|
|
1027
|
+
**Response shape**: Standard flat `{ totalElements, totalPages, data: [...] }` (same as all search/list endpoints).
|
|
1028
|
+
|
|
1029
|
+
**CRITICAL response dates**: `valueDate` and `matchDate` are `int64` epoch milliseconds (e.g., `1706227200000`), NOT `YYYY-MM-DD` strings. Convert: `new Date(epochMs).toISOString().slice(0, 10)`.
|
|
1030
|
+
|
|
1031
|
+
**Valid `businessTransactionType` values**: `SALE`, `PURCHASE`, `SALE_CREDIT_NOTE`, `PURCHASE_CREDIT_NOTE`, `JOURNAL_MANUAL`, `JOURNAL_DIRECT_CASH_IN`, `JOURNAL_DIRECT_CASH_OUT`, `JOURNAL_CASH_TRANSFER`, `FIXED_ASSET`.
|
|
1032
|
+
|
|
1033
|
+
**Valid `direction` values**: `PAYIN`, `PAYOUT`.
|
|
1034
|
+
|
|
1035
|
+
For full filter/sort field reference, see `references/search-reference.md` section 10.
|
|
1036
|
+
|
|
1037
|
+
---
|
|
1038
|
+
|
|
1039
|
+
## 19. Bank Records Search
|
|
1040
|
+
|
|
1041
|
+
### POST /api/v1/bank-records/:accountResourceId/search
|
|
1042
|
+
|
|
1043
|
+
Searches bank statement entries for a specific bank account. The `accountResourceId` path parameter is the UUID of a bank-type CoA account — find it via `POST /chart-of-accounts/search` with `{ "filter": { "accountType": { "eq": "Bank Accounts" } } }`.
|
|
1044
|
+
|
|
1045
|
+
```json
|
|
1046
|
+
// Request:
|
|
1047
|
+
{
|
|
1048
|
+
"filter": {
|
|
1049
|
+
"status": { "eq": "UNRECONCILED" },
|
|
1050
|
+
"valueDate": { "gte": "2026-01-01" }
|
|
1051
|
+
},
|
|
1052
|
+
"sort": { "sortBy": ["valueDate"], "order": "DESC" },
|
|
1053
|
+
"limit": 100
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
// Response:
|
|
1057
|
+
{
|
|
1058
|
+
"totalElements": 280,
|
|
1059
|
+
"totalPages": 3,
|
|
1060
|
+
"data": [{
|
|
1061
|
+
"resourceId": "uuid",
|
|
1062
|
+
"description": "Payment from Acme Corp",
|
|
1063
|
+
"netAmount": 2500.00,
|
|
1064
|
+
"valueDate": 1706227200000,
|
|
1065
|
+
"status": "UNRECONCILED",
|
|
1066
|
+
"extContactName": "Acme Corp",
|
|
1067
|
+
"extReference": "TXN-12345",
|
|
1068
|
+
"extAccountNumber": "****1234"
|
|
1069
|
+
}]
|
|
1070
|
+
}
|
|
1071
|
+
```
|
|
1072
|
+
|
|
1073
|
+
**Valid `status` values**: `RECONCILED`, `UNRECONCILED`, `ARCHIVED`, `POSSIBLE_DUPLICATE`.
|
|
1074
|
+
|
|
1075
|
+
For full filter/sort field reference, see `references/search-reference.md` section 11.
|
|
1076
|
+
|
|
1077
|
+
---
|
|
1078
|
+
|
|
1079
|
+
## 20. Bank Records — JSON POST (Alternative)
|
|
1080
|
+
|
|
1081
|
+
### POST /api/v1/bank-records/:accountResourceId
|
|
1082
|
+
|
|
1083
|
+
In addition to multipart import (Section 15), bank records can be created via JSON POST:
|
|
1084
|
+
|
|
1085
|
+
```json
|
|
1086
|
+
// Request:
|
|
1087
|
+
{
|
|
1088
|
+
"records": [{
|
|
1089
|
+
"description": "Payment from client",
|
|
1090
|
+
"payerOrPayee": "Acme Corp",
|
|
1091
|
+
"reference": "TXN-001",
|
|
1092
|
+
"amount": 2500.00,
|
|
1093
|
+
"transactionDate": "2026-02-10",
|
|
1094
|
+
"metadata": []
|
|
1095
|
+
}]
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
// Response:
|
|
1099
|
+
{ "data": { "resourceIds": ["uuid1"] } }
|
|
1100
|
+
```
|
|
1101
|
+
|
|
1102
|
+
**Fields**:
|
|
1103
|
+
- `records` (required): Array of 1-100 records
|
|
1104
|
+
- `amount` (required): Positive = cash-in, negative = cash-out
|
|
1105
|
+
- `transactionDate` (required): `YYYY-MM-DD` format
|
|
1106
|
+
- `description`, `payerOrPayee`, `reference`: Optional strings (max 65536 chars)
|
|
1107
|
+
- `metadata`: Optional array of `{ index, name, value }` objects (max 100)
|
|
1108
|
+
|
|
1109
|
+
**When to use**: JSON POST is best for programmatic creation. Multipart import (`POST /magic/importBankStatementFromAttachment`) is best for CSV/OFX file uploads.
|
|
1110
|
+
|
|
1111
|
+
---
|
|
1112
|
+
|
|
1113
|
+
## Advanced Search (POST /*/search)
|
|
1114
|
+
|
|
1115
|
+
All resources support `POST /api/v1/{resource}/search` with filter syntax. **For per-endpoint filter/sort field lists, see `references/search-reference.md`.**
|
|
1116
|
+
|
|
1117
|
+
### Request Example
|
|
1118
|
+
```json
|
|
1119
|
+
POST /api/v1/invoices/search
|
|
1120
|
+
{
|
|
1121
|
+
"filter": {
|
|
1122
|
+
"status": { "eq": "POSTED" },
|
|
1123
|
+
"valueDate": { "between": ["2026-01-01", "2026-12-31"] }
|
|
1124
|
+
},
|
|
1125
|
+
"sort": {
|
|
1126
|
+
"sortBy": ["valueDate"],
|
|
1127
|
+
"order": "DESC"
|
|
1128
|
+
},
|
|
1129
|
+
"limit": 100,
|
|
1130
|
+
"offset": 0
|
|
1131
|
+
}
|
|
1132
|
+
```
|
|
1133
|
+
|
|
1134
|
+
### Filter Operators
|
|
1135
|
+
| Type | Operators |
|
|
1136
|
+
|------|----------|
|
|
1137
|
+
| String | `eq`, `neq`, `contains`, `in` (max 100), `reg` (max 100), `likeIn` (max 100), `isNull` |
|
|
1138
|
+
| Numeric | `eq`, `gt`, `gte`, `lt`, `lte`, `in` (max 100) |
|
|
1139
|
+
| Date | `eq`, `gt`, `gte`, `lt`, `lte`, `between` (exactly 2 YYYY-MM-DD values) |
|
|
1140
|
+
| DateTime | Same as Date but RFC3339 format (for `createdAt`/`updatedAt`) |
|
|
1141
|
+
| Boolean | `eq` |
|
|
1142
|
+
| Logical | `and`, `or`, `not` (nested objects), `andGroup`, `orGroup` (arrays, invoices/bills/journals only) |
|
|
1143
|
+
|
|
1144
|
+
### Pagination
|
|
1145
|
+
- `limit`: max 1000 per page (default 100)
|
|
1146
|
+
- `offset`: skip N results (max 65536)
|
|
1147
|
+
- `sort`: **REQUIRED when `offset` is present** (even `offset: 0`)
|
|
1148
|
+
- Response includes `totalElements` and `totalPages`
|
|
1149
|
+
|
|
1150
|
+
---
|
|
1151
|
+
|
|
1152
|
+
## Catalogs (Experimental)
|
|
1153
|
+
|
|
1154
|
+
> Endpoint availability varies by organization. Use try/catch — if all requests fail, the endpoint may not be enabled.
|
|
1155
|
+
|
|
1156
|
+
### Create Catalog
|
|
1157
|
+
POST /api/v1/catalogs
|
|
1158
|
+
```json
|
|
1159
|
+
{
|
|
1160
|
+
"name": "Premium Products",
|
|
1161
|
+
"itemResourceIds": ["uuid-1", "uuid-2"],
|
|
1162
|
+
"description": "Curated product catalog for VIP customers"
|
|
1163
|
+
}
|
|
1164
|
+
```
|
|
1165
|
+
|
|
1166
|
+
### Response
|
|
1167
|
+
```json
|
|
1168
|
+
{ "data": { "resourceId": "catalog-uuid" } }
|
|
1169
|
+
```
|
|
1170
|
+
|
|
1171
|
+
---
|
|
1172
|
+
|
|
1173
|
+
## Deposits (Experimental)
|
|
1174
|
+
|
|
1175
|
+
> Endpoint availability varies by organization. Use try/catch.
|
|
1176
|
+
|
|
1177
|
+
### Create Deposit
|
|
1178
|
+
POST /api/v1/deposits
|
|
1179
|
+
```json
|
|
1180
|
+
{
|
|
1181
|
+
"contactResourceId": "contact-uuid",
|
|
1182
|
+
"depositDate": "2026-02-01",
|
|
1183
|
+
"amount": 5000.00,
|
|
1184
|
+
"type": "AR",
|
|
1185
|
+
"bankAccountResourceId": "bank-account-uuid",
|
|
1186
|
+
"currencyCode": "SGD"
|
|
1187
|
+
}
|
|
1188
|
+
```
|
|
1189
|
+
|
|
1190
|
+
- `type`: `"AR"` (accounts receivable / customer deposit) or `"AP"` (accounts payable / supplier deposit)
|
|
1191
|
+
- `depositDate`: YYYY-MM-DD format
|
|
1192
|
+
- `bankAccountResourceId`: Must be a CoA entry with accountType "Bank Accounts"
|
|
1193
|
+
|
|
1194
|
+
### Response
|
|
1195
|
+
```json
|
|
1196
|
+
{ "data": { "resourceId": "deposit-uuid" } }
|
|
1197
|
+
```
|
|
1198
|
+
|
|
1199
|
+
---
|
|
1200
|
+
|
|
1201
|
+
## Fixed Assets (Experimental)
|
|
1202
|
+
|
|
1203
|
+
> Endpoint availability varies by organization. Use try/catch.
|
|
1204
|
+
|
|
1205
|
+
### Create Fixed Asset
|
|
1206
|
+
POST /api/v1/fixed-assets
|
|
1207
|
+
```json
|
|
1208
|
+
{
|
|
1209
|
+
"name": "Office Laptop - MacBook Pro",
|
|
1210
|
+
"purchaseDate": "2026-01-15",
|
|
1211
|
+
"purchaseCost": 3500.00,
|
|
1212
|
+
"depreciationMethod": "STRAIGHT_LINE",
|
|
1213
|
+
"usefulLifeMonths": 36,
|
|
1214
|
+
"assetAccountResourceId": "fixed-asset-coa-uuid",
|
|
1215
|
+
"depreciationAccountResourceId": "depreciation-coa-uuid",
|
|
1216
|
+
"expenseAccountResourceId": "expense-coa-uuid",
|
|
1217
|
+
"currencyCode": "SGD"
|
|
1218
|
+
}
|
|
1219
|
+
```
|
|
1220
|
+
|
|
1221
|
+
- `depreciationMethod`: `"STRAIGHT_LINE"` (uppercase)
|
|
1222
|
+
- `usefulLifeMonths`: Integer
|
|
1223
|
+
- All three account fields must reference valid CoA entries
|
|
1224
|
+
|
|
1225
|
+
### Response
|
|
1226
|
+
```json
|
|
1227
|
+
{ "data": { "resourceId": "asset-uuid" } }
|
|
1228
|
+
```
|
|
1229
|
+
|
|
1230
|
+
---
|
|
1231
|
+
|
|
1232
|
+
## Inventory Adjustments (Experimental)
|
|
1233
|
+
|
|
1234
|
+
> Endpoint availability varies by organization. Use try/catch.
|
|
1235
|
+
|
|
1236
|
+
### Create Adjustment
|
|
1237
|
+
POST /api/v1/inventory/adjustments
|
|
1238
|
+
```json
|
|
1239
|
+
{
|
|
1240
|
+
"itemResourceId": "inventory-item-uuid",
|
|
1241
|
+
"quantity": 50,
|
|
1242
|
+
"adjustmentDate": "2026-02-01",
|
|
1243
|
+
"reason": "Initial stock count",
|
|
1244
|
+
"accountResourceId": "inventory-coa-uuid"
|
|
1245
|
+
}
|
|
1246
|
+
```
|
|
1247
|
+
|
|
1248
|
+
- `quantity`: Positive integer (adjustment amount)
|
|
1249
|
+
- `adjustmentDate`: YYYY-MM-DD format
|
|
1250
|
+
- `itemResourceId`: Must reference an inventory-type item (not service)
|
|
1251
|
+
|
|
1252
|
+
### Response
|
|
1253
|
+
```json
|
|
1254
|
+
{ "data": { "resourceId": "adjustment-uuid" } }
|
|
1255
|
+
```
|
|
1256
|
+
|
|
1257
|
+
---
|
|
1258
|
+
|
|
1259
|
+
---
|
|
1260
|
+
|
|
1261
|
+
## Field Aliases (Create/Update Endpoints)
|
|
1262
|
+
|
|
1263
|
+
Middleware on create and update endpoints transparently maps alias field names to canonical names. Both forms are accepted — the alias is only applied if the canonical field is absent.
|
|
1264
|
+
|
|
1265
|
+
| Alias | Canonical | Endpoints |
|
|
1266
|
+
|-------|-----------|-----------|
|
|
1267
|
+
| `issueDate` | `valueDate` | Invoices, bills, credit notes, journals, cash entries, cash transfers, all scheduled create/update endpoints |
|
|
1268
|
+
| `date` | `valueDate` | Same as above (including scheduled endpoints) |
|
|
1269
|
+
| `paymentDate` | `valueDate` | Payments (invoice/bill payments) |
|
|
1270
|
+
| `bankAccountResourceId` | `accountResourceId` | Payments |
|
|
1271
|
+
| `paymentAmount` | `refundAmount` | Credit note refunds |
|
|
1272
|
+
| `paymentMethod` | `refundMethod` | Credit note refunds |
|
|
1273
|
+
| `name` | `tagName` | Tags (create, update) |
|
|
1274
|
+
| `name` | `internalName` | Items (create) |
|
|
1275
|
+
| `accountType` | `classificationType` | Chart of accounts (create, update, bulk-upsert) |
|
|
1276
|
+
| `currencyCode` | `currency` | Chart of accounts bulk-upsert |
|
|
1277
|
+
|
|
1278
|
+
**Note**: Aliases apply only to POST/PUT request bodies. Search filter fields use their canonical names (e.g., `tagName` not `name` in `POST /tags/search`).
|
|
1279
|
+
|
|
1280
|
+
---
|
|
1281
|
+
|
|
1282
|
+
## Auto-Wrapping (NormalizeToArray)
|
|
1283
|
+
|
|
1284
|
+
Middleware on payment, credit, and refund endpoints automatically wraps a flat JSON object into an array. Both formats work:
|
|
1285
|
+
|
|
1286
|
+
| Endpoint | Array format (preferred) | Flat format (auto-wrapped) |
|
|
1287
|
+
|----------|------------------------|---------------------------|
|
|
1288
|
+
| `POST /invoices/:id/payments` | `{ "payments": [{...}] }` | `{ "paymentAmount": ..., ... }` → auto-wrapped to `{ "payments": [{...}] }` |
|
|
1289
|
+
| `POST /bills/:id/payments` | `{ "payments": [{...}] }` | Same |
|
|
1290
|
+
| `POST /invoices/:id/credits` | `{ "credits": [{...}] }` | Same |
|
|
1291
|
+
| `POST /bills/:id/credits` | `{ "credits": [{...}] }` | Same |
|
|
1292
|
+
| `POST /customer-credit-notes/:id/refunds` | `{ "refunds": [{...}] }` | Same |
|
|
1293
|
+
| `POST /supplier-credit-notes/:id/refunds` | `{ "refunds": [{...}] }` | Same |
|
|
1294
|
+
|
|
1295
|
+
**Recommendation**: Always use the array format for clarity and consistency.
|
|
1296
|
+
|
|
1297
|
+
---
|
|
1298
|
+
|
|
1299
|
+
*Last updated: 2026-02-14 — All search/list responses standardized to flat shape. Scheduled endpoints support date aliases. Sections: cashflow-transactions/search (18), bank-records search (19), bank-records JSON POST (20). Complete filter operator reference. Per-endpoint details in search-reference.md.*
|