Update an expense
Needs a token with the write:expenses scope. Authentication
Try in Playground sends a real request to app.helvetly.com with a token you paste. It stays in this browser tab only (session storage) and goes to no one else.
Partial update of an expense: absent keys stay, null clears. A draft takes every ExpenseInput field (lines replaces all lines). A booked expense takes only the payment fields (payment_status, paid_on, payment_method, paid_by_member_id, reimbursed_on; a change is written to the audit log) and note; a void one only note. Anything else answers 409 expense_locked: void the expense and book a corrected copy (duplicate_expense). Changing a category used by booked expenses is not possible. 422 validation_failed with details.fields. Retrying the same body is safe.
bearerAuthAuthorizationBearer <token>A personal API token, hvt_…, with scopes such as read:* or write:invoices
id*stringThe expense's id.
application/json- body
Fields of an expense you may set. Every field is optional on create (a draft needs nothing) and on update; an absent key is left alone and null clears a nullable field. Totals, VAT breakdown and base-currency amounts are computed by Helvetly and never accepted. A booked expense takes only payment_status, paid_on, payment_method, paid_by_member_id, reimbursed_on and note (409 expense_locked for anything else); a void one only note.
supplier_name?|Up to 200 characters; required to book.
supplier_vat_number?|Swiss UID (checksum verified) or EU VAT number; up to 30 characters.
supplier_country?stringISO 3166 alpha-2, default CH. Anything but CH/LI defaults input_tax to none (foreign VAT is no Swiss input tax).
reference?|The supplier's invoice or receipt number, up to 100 characters.
description?|What was bought, up to 500 characters.
category_id?|An active category of the workspace (see list_expense_categories); required to book.
document_date?|The receipt's date, not in the future when booking; required to book.
datedue_date?|Not before document_date.
datecurrency?stringISO 4217 with two minor units; default the workspace currency.
amount_mode?stringWhether line amounts include VAT (gross, default) or not (net).
"gross""net"lines?array<>1 to 20 lines, one per VAT rate or cost item; on update the array replaces all lines.
items <= 20input_tax?stringInput tax (Vorsteuer) claim; default full when the workspace is VAT-registered, else none.
"full""partial""none"deductible_percent?stringShare of VAT reclaimable: 100 with full, 0 with none, above 0 and below 100 with partial.
^-?\d+(\.\d{1,2})?$exchange_rate?stringForeign currency to workspace currency; needed with exchange_rate_date and exchange_rate_source to book a foreign-currency expense.
^\d+(\.\d{1,6})?$exchange_rate_date?|dateexchange_rate_source?|"estv_monthly""estv_daily""bank""manual"nullpayment_status?stringSetting unpaid clears paid_on, payment_method, paid_by_member_id and reimbursed_on; paid without paid_on uses the document date (or today).
"unpaid""paid"paid_on?|Not in the future.
datepayment_method?|"bank_transfer""cash""card""twint""other"nullpaid_by_member_id?|A member of the workspace who paid out of pocket (makes it reimbursable and sets payment_status paid).
reimbursed_on?|When the member was paid back; needs paid_by_member_id; not in the future.
datenote?|Internal note, up to 1000 characters. Editable on booked expenses.
receipt_missing_reason?|Substitute-receipt reason (Ersatzbeleg), up to 500 characters; stands in for a receipt when booking.
reverse_charge?booleanAcquisition tax on a service from a supplier abroad; needs a supplier_country other than CH/LI.
input_tax_box?|VAT return box for the input tax.
"400""405"nullOK
application/json- response
data*curl -X PATCH 'https://app.helvetly.com/api/v1/expenses/string' \ -H 'Content-Type: application/json' \ -d '{ "supplier_name": "Swisscom (Schweiz) AG", "supplier_country": "CH", "reference": "INV-2026-0412", "category_id": "exc_01JABCDEF", "document_date": "2026-10-31", "currency": "CHF", "amount_mode": "gross", "lines": [ { "description": "Mobile subscription October", "amount": "89.90", "vat_rate": "8.1" } ], "payment_status": "paid", "payment_method": "card"}'{ "data": { "id": "string", "number": 0, "status": "draft", "source": "string", "supplier_name": "string", "supplier_vat_number": "string", "supplier_country": "string", "reference": "string", "duplicate_of": { "id": "string", "number": 0, "date": "2019-08-24" }, "description": "string", "category": { "id": "string", "name": "string", "account_code": "string" }, "document_date": "2019-08-24", "due_date": "2019-08-24", "currency": "string", "amount_mode": "string", "lines": [ { "id": "string", "position": 0, "description": "string", "amount": "1234.50", "vat_rate": "string" } ], "net_total": "1234.50", "vat_total": "1234.50", "total": "1234.50", "vat_breakdown": [ { "rate": "string", "taxable_amount": "1234.50", "vat_amount": "1234.50", "deductible_amount": "1234.50" } ], "input_tax": "string", "reverse_charge": true, "input_tax_box": "string", "deductible_percent": "string", "deductible_vat": "1234.50", "exchange_rate": "string", "exchange_rate_date": "2019-08-24", "exchange_rate_source": "string", "base": { "currency": "string", "net_total": "1234.50", "vat_total": "1234.50", "total": "1234.50", "deductible_vat": "1234.50" }, "payment_status": "string", "paid_on": "2019-08-24", "payment_method": "string", "paid_by_member": { "id": "string", "name": "string" }, "reimbursed_on": "2019-08-24", "is_overdue": true, "note": "string", "receipt_missing_reason": "string", "receipts_count": 0, "is_complete": true, "missing_fields": [ "string" ], "created_by": { "id": "string", "name": "string" }, "booked_at": "2019-08-24T14:15:22Z", "booked_by": { "id": "string", "name": "string" }, "voided_at": "2019-08-24T14:15:22Z", "voided_by": { "id": "string", "name": "string" }, "void_reason": "string", "created_at": "2019-08-24T14:15:22Z", "updated_at": "2019-08-24T14:15:22Z" }}