Create Expense
Create a new expense record with minimal required fields and smart defaults for optional fields
Documentation Index
Fetch the complete documentation index at: https://docs.contazen.ro/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The Create Expense endpoint allows you to add a new expense to your firm’s records with just 4 required fields. The API provides smart defaults for all optional fields, making expense creation quick and simple. Three save shapes are supported:- Flat — send
amount+vatfor a single-line, single-rate expense. Simplest path; covers most utility bills and single-rate receipts. - Itemized — send
items[]when the receipt has multiple line items, each carrying its own rate. The server derivesamount/vat/vat_breakdownfrom the lines. See Expense Lines & VAT Breakdown for the full model. - Mix VAT (manual) — send
vat: "mix"+amount(net subtotal) +amount_vat_manual(total VAT in RON) when the receipt mixes multiple VAT rates and per-product detail isn’t reliable (typical for noisy bon fiscal OCR). Optionally includevat_breakdownso per-rate aggregates persist. The server reconstructstotal = amount + amount_vat_manualexactly — no derivation, no drift.
amount + vat are required on the flat and mix paths. On the itemized path they’re ignored in favor of line-derived totals.
Duplicate Detection
Before persisting, the API scans your firm’s existing expenses for a likely duplicate (same supplier + reference, or same supplier + date + amount within a small tolerance). If a match is found, the request is rejected with HTTP 409 and a payload describing the existing expense — no row is created. To override and force creation anyway, send?force=1 on the request URL.
Clients that want to surface a confirmation dialog to the user before POSTing can pre-check with POST /expenses/check-duplicate using the same supplier / reference / amount fields; it returns the same duplicate shape without writing anything.
Query Parameters
1 to bypass duplicate detection and persist the expense even when a likely duplicate exists.Request Body
Required Fields
supplier_data to auto-create.items is sent.0, 5, 9, 11, 19, 21) or the literal string "mix" for a multi-rate receipt where the caller supplies the total VAT manually (see Mix VAT mode). Required on the flat and mix paths; when items is sent the server sets this to the dominant rate.vat = "mix", ignored otherwise. Must be >= 0. The server stores amount_wvat = amount, amount_vat = amount_vat_manual, amount_total = amount_wvat + amount_vat_manual exactly — no rounding inference.Optional Fields with Defaults
- Defaults to RON if not provided
- Defaults to 30 days from today if not provided
- Optional, defaults to 0 (uncategorized)
- Defaults to false if not provided
- Defaults to false if not provided
- Defaults to today if is_paid is true and paid_date not provided
- Optional even when is_paid is true
- Defaults to 1 (Cash) if is_paid is true and payment_type not provided
- Optional even when is_paid is true
1- Cash2- Bank transfer3- Card4- Check5- Promissory note6- Other7- Compensation
Itemized Lines
amount / vat / vat_breakdown from them —
the header-level amount and vat request fields are ignored.Each item is an object with:name(string, required) — display name for the linequantity(number, required, > 0) — supports fractional quantitiesunit_price(number, required) — net unit price (excluding VAT). Negative values are accepted for discount rows (e.g. a Romanian bon fiscalDISCOUNT 4,40-Arebate); the sign is preserved through to the line’s gross.vat_rate(integer, required) — one of 0, 5, 9, 11, 19, 21description(string) — optionalunit_code(string) — optional UN/ECE code (e.g.H87,KGM)category_id(string) — optional per-line category overrideproduct_id(string) — optional link to firm’s product catalog
items). Each entry: { rate, net, vat_amount, gross }. The server
stores the array as JSON on the row when two or more distinct rates are
present. Ignored when items is supplied — in that case the breakdown is
derived from the lines.On the mix path, supplying vat_breakdown with at least two
rate tranches makes the server synthesize one persisted line per rate
group (Cheltuială (21%), Cheltuială (11%), Cheltuială (0%)) so
reports and exports keep accurate per-rate totals. Without a breakdown,
mix mode falls back to a single line with a synthetic
vat_rate = (amount_vat_manual / amount) × 100.supplier_id is not supplied. Useful when
ingesting supplier data from external sources where the firm hasn’t
billed with the supplier before.Shape: { name: string, cui?: string }. If a supplier with the same CUI
(or name when CUI is missing) already exists on the firm, it’s reused
instead of creating a duplicate.Response
Returns the created expense object with all computed fields including VAT calculations and currency conversions.Validation Rules
- Supplier: Must exist and be owned by your firm (required)
- Amount: Must be a positive number (required)
- VAT: Must be one of: 0, 5, 9, 11, 19, 21, or the string
"mix"(required) - amount_vat_manual: Required (>= 0) when
vat = "mix", ignored otherwise - Date: Must be in YYYY-MM-DD format (required)
- Category: If provided, must be a valid expense category for your firm
- Currency: If provided, must be a valid 3-letter ISO code
- Dates: All dates must be in YYYY-MM-DD format
- Payment Info: paid_date and payment_type are optional with smart defaults
VAT Calculation
The system automatically calculates VAT amounts based on your input:- If
vat = "mix":amountis treated as the net subtotal,amount_vat_manualis the total VAT, andtotal = amount + amount_vat_manualexactly.with_vatis forced tofalse. See Mix VAT mode below. - If
with_vatis true: The amount includes VAT, and VAT is extracted from the total - If
with_vatis false: The amount excludes VAT, and VAT is added to get the total
Mix VAT mode
Romanian fiscal receipts (bon fiscal) routinely mix multiple VAT rates on a single document — e.g. food items at 11% alongside non-food at 21% and packaging deposits at 0%. When per-product detail isn’t reliable, mix mode lets the caller record the printed totals exactly without inferring a single rate. Request shape:amount_wvat = 147.53,amount_vat = 24.15,amount_total = 171.68(exact, no derivation)vat_breakdownJSON when two or more rate entries are supplied; per-rate aggregates persist for reports. Presence of this JSON with ≥2 entries is what marks the row as multi-rate — the legacyvat_percentfield is filled with the dominant rate for back-compat with single-rate consumers.- Without
vat_breakdown, the receipt is stored as a single-rate row withvat_percent = (amount_vat_manual / amount) × 100
- Use itemized (
items[]) when you have reliable per-product names, quantities, unit prices, and rates. - Use mix (
vat = "mix") when only the printed totals are reliable.
Currency Exchange
Ifwith_currency_exchange is enabled:
- Original amounts are kept in the expense currency
- Account amounts are calculated using the provided exchange rate
- Exchange details are stored for audit purposes
Authorizations
Use your API key (sk_live_xxx or sk_test_xxx)
Body
Either supplier_id or supplier_data must be provided. Either
(amount + vat) or items[] must be provided — when items is
sent the header amount/vat request fields are ignored and the
server derives them from the lines.
Expense date (required, Y-m-d format)
Supplier CzUid. Omit when sending supplier_data to auto-create.
Auto-create a supplier when supplier_id is missing. If a
supplier with the same CUI (or name when CUI is missing) already
exists on the firm, it is reused instead of creating a duplicate.
Amount (positive number). Required on the flat path; ignored when items is sent.
x >= 0.01VAT percentage. Either a numeric percent (0/5/9/11/19/21) or the
string "mix" for multi-rate receipts where the caller supplies
the total VAT manually via amount_vat_manual. Required on the
flat and mix paths; ignored when items is sent.
0, 5, 9, 11, 19, 21 Total VAT amount in receipt currency. Required when vat = "mix",
ignored otherwise. Server stores amount_wvat = amount,
amount_vat = amount_vat_manual, amount_total = amount + amount_vat_manual
exactly — no rounding inference.
x >= 0Line rows. When present and non-empty, the server persists real
line records and derives amount / vat / vat_breakdown from
them. See the Expense Lines concept for rounding rules.
Per-rate VAT tranches for multi-VAT receipts saved on the flat
or mix paths. Supply two or more entries. On the mix path the
server synthesizes one persisted line per rate group from the
breakdown so reports keep accurate per-rate totals. Ignored when
items is sent.
Currency code (optional, defaults to RON)
Due date (optional, defaults to 30 days from today)
Payment date (optional, defaults to today if is_paid=true)
Payment type ID 1-7 (optional, defaults to 1 if is_paid=true): 1 = Cash 2 = Bank transfer 3 = Card 4 = Check 5 = Promissory note 6 = Other 7 = Compensation
1 <= x <= 7Category ID (optional, defaults to 0)
Reference number (optional)
Description (optional)
Whether amount includes VAT (optional, defaults to false)
Whether expense is paid (optional, defaults to false)
Whether to apply currency exchange (optional)
Target currency (if with_currency_exchange)
Exchange rate (if with_currency_exchange)