Jaz API Skill
You are working with the Jaz/Juan REST API — the backend for Jaz (Singapore) and Juan (Philippines) accounting platforms.
When to Use This Skill
- •Writing or modifying any code that calls the Jaz API
- •Building API clients, integrations, or data pipelines
- •Debugging API errors (422, 400, 404, 500)
- •Adding support for new Jaz API endpoints
- •Reviewing code that constructs Jaz API request payloads
Quick Reference
Base URL: https://api.getjaz.com
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.
Content-Type: application/json for all POST/PUT/PATCH (except multipart endpoints: createBusinessTransactionFromAttachment FILE mode, importBankStatementFromAttachment, and attachment uploads)
All paths are prefixed: /api/v1/ (e.g., https://api.getjaz.com/api/v1/invoices)
Critical Rules
Identifiers & Dates
- •All IDs are
resourceId— neverid. References use<resource>ResourceIdsuffix. - •All transaction dates are
valueDate— notissueDate,invoiceDate,date. This is an accounting term meaning "date of economic effect." - •All dates are
YYYY-MM-DDstrings — ISO datetime and epoch ms are rejected.
Payments (Cross-Currency Aware)
- •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). - •Payment date is
valueDate— notpaymentDate, notdate. - •Payment bank account is
accountResourceId— notbankAccountResourceId. - •Payments require 6 fields:
paymentAmount,transactionAmount,accountResourceId,paymentMethod,reference,valueDate. - •Payments wrapped in
{ payments: [...] }— array recommended. Flat objects are now auto-wrapped by the API, but array format is preferred for clarity.
Names & Fields
- •Line item descriptions use
name— notdescription. - •Item names: canonical field is
internalName, butnamealias is accepted on POST. GET responses return bothinternalNameandname. - •Tag names: canonical field is
tagName, butnamealias is accepted on POST. GET responses return bothtagNameandname. - •Custom field names: POST uses
name, GET returns bothcustomFieldNameandname. - •Invoice/bill number is
reference— notreferenceNumber.
Transaction Creation
- •
saveAsDraftdefaults tofalse— omitting it creates a finalized transaction. Explicitly sendingsaveAsDraft: truecreates a draft. - •If
saveAsDraft: false(or omitted), every lineItem MUST haveaccountResourceId. - •Phones MUST be E.164 —
+65XXXXXXXX(SG),+63XXXXXXXXXX(PH). No spaces.
Chart of Accounts
- •Tax profiles pre-exist — NEVER create them. Only GET and map.
- •Bank accounts are CoA entries with
accountType: "Bank Accounts". No separate endpoint. - •CoA bulk-upsert wrapper is
accounts— notchartOfAccounts. - •CoA POST uses
currency— notcurrencyCode. (Asymmetry — GET returnscurrencyCode.) - •CoA POST uses
classificationType— GET returnsaccountType. Same values. - •CoA code mapping: match by NAME, not code — pre-existing accounts may have different codes. Resource IDs are the universal identifier.
Journals & Cash
- •Journals use
journalEntrieswithamount+type: "DEBIT"|"CREDIT"— NOTdebit/creditnumber fields. - •Journals support multi-currency via
currencyobject — same format as invoices/bills:"currency": { "sourceCurrency": "USD" }(auto-fetch platform rate) or"currency": { "sourceCurrency": "USD", "exchangeRate": 1.35 }(custom rate). Must be enabled for the org. Omit for base currency. Three restrictions apply to foreign currency journals: (a) no controlled accounts — accounts withcontrolFlag(AR, AP) are off-limits (use invoices/bills instead), (b) no FX accounts — FX Unrealized Gain/Loss/Rounding are system-managed, (c) bank accounts must match — can only post to bank accounts in the same currency as the journal (e.g., USD journal → USD bank account only, not SGD bank account). All other non-controlled accounts (expenses, revenue, assets, liabilities) are available. - •
currencyobject is the SAME everywhere — invoices, bills, credit notes, AND journals all usecurrency: { sourceCurrency: "USD", exchangeRate?: number }. Never usecurrencyCode: "USD"(silently ignored on invoices/bills) orcurrency: "USD"(string — causes 400 on invoices/bills). - •Cash entries use
accountResourceIdat top level for the BANK account +journalEntriesarray for offsets.
Credit Notes & Refunds
- •Credit note application wraps in
creditsarray withamountApplied— not flat. - •CN refunds use
refundswrapper withrefundAmount+refundMethod— NOTpayments/paymentAmount/paymentMethod.
Inventory Items
- •Inventory items require:
unit(e.g.,"pcs"),costingMethod("FIXED"or"WAC"),cogsResourceId,blockInsufficientDeductions,inventoryAccountResourceId.purchaseAccountResourceIdMUST be Inventory-type CoA. - •Delete inventory items via
DELETE /items/:id— not/inventory-items/:id.
Cash Transfers
- •Cash transfers use
cashOut/cashInsub-objects — NOT flatfromAccountResourceId/toAccountResourceId. Each:{ accountResourceId, amount }.
Schedulers
- •Scheduled invoices/bills wrap in
{ invoice: {...} }or{ bill: {...} }— not flat. Recurrence field isrepeat(NOTfrequency/interval).saveAsDraft: falserequired. - •Scheduled journals use FLAT structure with
schedulerEntries— not nested injournalwrapper.
Bookmarks
- •Bookmarks use
itemsarray wrapper withname,value,categoryCode,datatypeCode.
Custom Fields
- •Do NOT send
appliesToon custom field POST — causes "Invalid request body". Only sendname,type,printOnDocuments.
Reports
- •Report field names differ by type — this is the most error-prone area:
| Report | Required Fields |
|---|---|
| Trial balance | startDate, endDate |
| Balance sheet | primarySnapshotDate |
| P&L | primarySnapshotDate, secondarySnapshotDate |
| General ledger | startDate, endDate, groupBy: "ACCOUNT" |
| Cashflow | primaryStartDate, primaryEndDate |
| Cash balance | reportDate |
| AR/AP report | endDate |
| AR/AP summary | startDate, endDate |
| Bank balance summary | primarySnapshotDate |
| Equity movement | primarySnapshotStartDate, primarySnapshotEndDate |
- •Data exports use simpler field names: P&L export uses
startDate/endDate(NOTprimarySnapshotDate). AR/AP export usesendDate.
Pagination
- •All list/search endpoints use
limit/offsetpagination — NOTpage/size. Default limit=100, offset=0. Max limit=1000, max offset=65536.page/sizeparams are silently ignored. Response shape:{ totalPages, totalElements, data: [...] }.
Other
- •Currency rates use
/organization-currencies/:code/rates— note the HYPHENATED path (NOT/organization/currencies). Enable currencies first viaPOST /organization/currencies, then set rates viaPOST /organization-currencies/:code/rateswith 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. - •FX invoices/bills MUST use
currencyobject —currencyCode: "USD"(string) is silently ignored (transaction created in base currency!). Usecurrency: { sourceCurrency: "USD" }to auto-fetch platform rate (ECB/FRANKFURTER), orcurrency: { sourceCurrency: "USD", exchangeRate: 1.35 }for a custom rate. Rate hierarchy: org rate → platform/ECB → transaction-level. - •Invoice GET uses
organizationAccountResourceIdfor line item accounts — POST usesaccountResourceId. Request-side aliases resolveissueDate→valueDate,bankAccountResourceId→accountResourceId, etc. - •Scheduler GET returns
interval— POST usesrepeat. (Response-side asymmetry remains.) - •Search sort is an object —
{ sort: { sortBy: ["valueDate"], order: "DESC" } }. Required whenoffsetis present (evenoffset: 0). - •Bank records: two import methods — Multipart CSV/OFX via
POST /magic/importBankStatementFromAttachment(fields:sourceFile,accountResourceId,businessTransactionType: "BANK_STATEMENT",sourceType: "FILE"). JSON viaPOST /bank-records/:accountResourceIdwith{ records: [{description, netAmount, valueDate, ...}] }. - •Withholding tax on bills/supplier CNs only. Retry pattern: if
WITHHOLDING_CODE_NOT_FOUND, strip field and retry. - •Known API bugs (500s): Contact groups PUT, custom fields PUT, capsules POST, catalogs POST, inventory balances GET — all return 500.
- •Non-existent endpoints:
POST /depositsandPOST /inventory/adjustmentsreturn 404 — these endpoints are not implemented. - •Attachments require PDF/PNG:
POST /:type/:id/attachmentsuses multipartfilefield but rejectstext/plain. Useapplication/pdforimage/png. - •Currency rate direction:
rate= functionalToSource (1 base = X foreign) — POSTrate: 0.74for 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) andrateSourceToFunctional(the inverse).
Search & Filter
- •Search endpoint universal pattern — All 28
POST /*/searchendpoints share identical structure:{ filter?, sort: { sortBy: ["field"], order: "ASC"|"DESC" }, limit: 1-1000, offset: 0-65536 }. Sort is REQUIRED when offset is present (evenoffset: 0). Default limit: 100.sortByis always an array on all endpoints (no exceptions). Seereferences/search-reference.mdfor per-endpoint filter/sort fields. - •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 withand/or/notobjects, or useandGroup/orGrouparrays (invoices, bills, journals, credit notes). - •Date format asymmetry (CRITICAL) — Request dates:
YYYY-MM-DDstrings (all create/update and DateExpression filters). Request datetimes: RFC3339 strings (DateTimeExpression filters forcreatedAt,updatedAt,approvedAt,submittedAt). ALL response dates:int64epoch milliseconds — includingvalueDate,createdAt,updatedAt,approvedAt,submittedAt,matchDate. Convert:new Date(epochMs).toISOString().slice(0,10). - •Field aliases on create endpoints — Middleware transparently maps:
issueDate/date→valueDate(invoices, bills, credit notes, journals).name→tagName(tags) orinternalName(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. - •All search/list responses are flat — every search and list endpoint returns
{ totalElements, totalPages, data: [...] }directly (no outerdatawrapper). Access the array viaresponse.data, pagination viaresponse.totalElements. - •Scheduled endpoints support date aliases —
txnDateAliasesmiddleware (mappingissueDate/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. - •Kebab-case URL aliases —
capsuleTypesendpoints also accept kebab-case paths:/capsule-types(list, search, CRUD).moveTransactionCapsulesalso accepts/move-transaction-capsules. Both camelCase and kebab-case work identically.
Jaz Magic — Extraction & Autofill
- •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 aPOST /invoicesorPOST /billspayload 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 usePOST /invoicesorPOST /billswhen building transactions from structured data (JSON, CSV, database rows) where the fields are already known. - •Two upload modes with different content types —
sourceType: "FILE"requires multipart/form-data withsourceFileblob (JSON body fails with 400 "sourceFile is a required field").sourceType: "URL"accepts application/json withsourceURLstring. The OAS only documents URL mode — FILE mode (the common case) is undocumented. - •Three required fields:
sourceFile(multipart blob — NOTfile),businessTransactionType("INVOICE"or"BILL"only —EXPENSErejected),sourceType("FILE"or"URL"). All three are validated server-side. - •Response maps transaction types: Request
BILL→ responsebusinessTransactionType: "PURCHASE". RequestINVOICE→ responsebusinessTransactionType: "SALE". S3 paths follow:/purchases/vs/sales/. - •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
subscriptionFBPathin the response (e.g.,magic_transactions/{orgId}/purchase/{fileId}) is a Firebase Realtime Database path for subscribing to extraction status updates. - •Accepts PDF and JPG/JPEG — both file types confirmed working. Handwritten documents are accepted at upload stage (extraction quality varies).
fileTypein response reflects actual format:"PDF","JPEG".
Supporting Files
For detailed reference, read these files in this skill directory:
- •references/search-reference.md — Complete search/filter/sort reference for all 28 search endpoints — per-endpoint filter fields, sort fields, operator types
- •references/endpoints.md — Full API endpoint reference with request/response examples
- •references/errors.md — Complete error catalog: every error, cause, and fix
- •references/field-map.md — Complete field name mapping (what you'd guess vs actual), date format matrix, middleware aliases
- •references/dependencies.md — Resource creation dependencies and required order
- •references/full-api-surface.md — Complete endpoint catalog (80+ endpoints), enums, search filters, limits
- •references/feature-glossary.md — Business context per feature — what each feature does and why, extracted from help.jaz.ai
- •help-center-mirror/ — Full help center content split by section (auto-generated from help.jaz.ai)
DX Overhaul (Implemented)
The backend DX overhaul is live. Key improvements now available:
- •Request-side field aliases:
name→tagName/internalName,issueDate→valueDate,bankAccountResourceId→accountResourceId, and more. Both canonical and alias names are accepted. - •Response-side aliases: Tags, items, and custom fields return
namealongside canonical field names (tagName,internalName,customFieldName). - •
saveAsDraftdefaults tofalse: Omitting it creates a finalized transaction. No longer required on POST. - •
POST /items/searchavailable: Advanced search with filters now works for items. - •NormalizeToArray: Flat payment/refund/credit objects are auto-wrapped into arrays. Array format is still recommended.
- •Nil-safe deletes: Delete endpoints return 404 (not 500) when resource not found.
Recommended Client Patterns
- •Starting from an attachment? → Use Jaz Magic (
POST /magic/createBusinessTransactionFromAttachment). Never manually parse a PDF/JPG to constructPOST /invoicesorPOST /bills— let the extraction & autofill pipeline handle it. - •Starting from structured data? → Use
POST /invoicesorPOST /billsdirectly with the known field values. - •Serialization (Python):
model_dump(mode="json", by_alias=True, exclude_unset=True, exclude_none=True) - •Field names: All request bodies use camelCase
- •Date serialization: Python
datetype →YYYY-MM-DDstrings - •Bill payments: Embed in bill creation body (safest). Standalone
POST /bills/{id}/paymentsalso works. - •Bank records: Use multipart
POST /magic/importBankStatementFromAttachment - •Scheduled bills: Wrap as
{ status, startDate, endDate, repeat, bill: {...} } - •FX currency (invoices, bills, credit notes, AND journals):
currency: { sourceCurrency: "USD" }(auto-fetches platform rate) orcurrency: { sourceCurrency: "USD", exchangeRate: 1.35 }(custom rate). Same object form on all transaction types. Never usecurrencyCodestring — silently ignored.