---
title: "Correct a recorded expense"
method: PATCH
path: "/api/v1/rest/spese/expenses/{id}"
tags: ["Expenses"]
---

# Correct a recorded expense

`PATCH /api/v1/rest/spese/expenses/{id}`

## Request

- Base URL: `https://api.custodo.ai`
- URL: `https://api.custodo.ai/api/v1/rest/spese/expenses/{id}`
- Auth: HTTP bearer

Corrects one line — the day, the supplier, the amount, the category, the payment channel, the note, the category’s extra fields or a journey’s distance — while it may still be edited: a line in no nota spese, or in one that is `draft` or `rejected`. A line inside a report that has already left is refused, and can be corrected only once that report comes back. Name only what changes; `null` clears a field that may be empty. The caller corrects their own lines, and whoever manages the company’s expenses anybody’s. If the line sits in an open report, that report’s totals follow it in the same call. ⚠ A CAR JOURNEY (`km`) IS PRICED BY THE SERVER: give `distance_km` and never an amount or a currency — it is re-priced at the rate frozen onto the line when it was filed. ⚠ `metadata` REPLACES the extra fields as a whole: send every field that should stay (spese_get_expense with `detailed` shows them). The kind of a line cannot change.

## Path parameters

- `id` string, uuid, required

## Headers

- `X-Tenant-Id` string, uuid

## Request body

- object
  - `spent_on` string: The day the money was spent, YYYY-MM-DD.
  - `supplier` string, nullable: Who was paid, as written on the receipt. `null` clears it.
  - `amount_cents` integer, nullable: Integer cents in the line’s currency (48,00 € is 4800). `null` clears it and the line is incomplete again. Refused on a `km` line.
  - `currency` string: ISO-4217, three letters. The line is converted again at the rate published for its day. Refused on a `km` line.
  - `category_id` string, uuid, nullable: From spese_list_categories. A TOP-LEVEL one. `null` clears it — send `subcategory_id: null` with it when the line has a sub-category.
  - `subcategory_id` string, uuid, nullable: A child of `category_id`, which must be given alongside it. `null` clears it.
  - `payment_channel_id` string, uuid, nullable: From spese_list_channels. Changing it decides again, and freezes again, whether the company owes the money back. `null` clears it.
  - `note` string, nullable: Anything worth recording. `null` clears it.
  - `distance_km` number: Kilometres driven, on a `km` line only. Refused on every other kind.
  - `metadata` object: The category’s extra fields, keyed by their `name` from spese_list_categories. It REPLACES the whole set — a field left out is cleared — and anything the category does not declare is refused.

## Response `200`

Done. `data` is the tool’s own answer.

- RestResult
  - `ok` true, required
  - `data` unknown, required

## Other responses

- `400`: The arguments did not validate: `error.message` names the field, what it expects and what arrived.
- `401`: No API key, or one that is malformed, unknown, revoked or expired.
- `403`: The call never ran: the module is off for this organisation, the credential lacks the permission or the module, the tier does not cover it, or the tool is destructive and the key was not minted for that.
- `404`: Nothing with that id in this organisation.
- `409`: The record is in a state that does not allow it — under legal hold, already sent, already closed. `error.code` says which.
- `422`: The tool ran and refused, in a sentence of its own (`TOOL_REFUSED`): the message says what to change. Retrying the same call will not help.
- `429`: The API key has spent its minute, or its day. Every request a key makes counts. See `Retry-After`.

---

[API](https://skmtc.dev/custodo/apis/custodo-integrazioni.md) · [All operations](https://skmtc.dev/custodo/apis/custodo-integrazioni/llms.txt) · [OpenAPI document](https://skmtc.dev/custodo/apis/custodo-integrazioni/revisions/16d336d3ec2a?raw)
