Skip to Content
HelvetlyDocs
API referenceExpenses

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.

PATCH
/api/v1/expenses/{id}

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.

Authorization

bearerAuth
headerAuthorizationBearer <token>

A personal API token, hvt_…, with scopes such as read:* or write:invoices

Path Parameters

id*string

The expense's id.

Request Body

application/json
  1. 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?string

ISO 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.

Formatdate
due_date?|

Not before document_date.

Formatdate
currency?string

ISO 4217 with two minor units; default the workspace currency.

amount_mode?string

Whether line amounts include VAT (gross, default) or not (net).

Value in"gross""net"
lines?array<>

1 to 20 lines, one per VAT rate or cost item; on update the array replaces all lines.

Itemsitems <= 20
input_tax?string

Input tax (Vorsteuer) claim; default full when the workspace is VAT-registered, else none.

Value in"full""partial""none"
deductible_percent?string

Share of VAT reclaimable: 100 with full, 0 with none, above 0 and below 100 with partial.

Match^-?\d+(\.\d{1,2})?$
exchange_rate?string

Foreign currency to workspace currency; needed with exchange_rate_date and exchange_rate_source to book a foreign-currency expense.

Match^\d+(\.\d{1,6})?$
exchange_rate_date?|
Formatdate
exchange_rate_source?|
Value in"estv_monthly""estv_daily""bank""manual"null
payment_status?string

Setting unpaid clears paid_on, payment_method, paid_by_member_id and reimbursed_on; paid without paid_on uses the document date (or today).

Value in"unpaid""paid"
paid_on?|

Not in the future.

Formatdate
payment_method?|
Value in"bank_transfer""cash""card""twint""other"null
paid_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.

Formatdate
note?|

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?boolean

Acquisition 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.

Value in"400""405"null

Response Body

OK

application/json
  1. response
data*
Example Request
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"}'
Example Response
{  "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"  }}