---
title: "Update a calendar entry"
method: PATCH
path: "/v1/calendar-entries/{id}"
tags: ["Calendar"]
---

# Update a calendar entry

`PATCH /v1/calendar-entries/{id}`

## Request

- Base URL: `https://api.officina.it`
- URL: `https://api.officina.it/v1/calendar-entries/{id}`
- Auth: HTTP bearer

Three-state PATCH semantics (omitted = unchanged, `null` =
clear nullable, value = set).

Sending `null` on a non-nullable field (`title`, `color`,
`start_time`, `end_time`, `email_reminder`,
`auto_reminder_enabled`) returns 422 `parameter_invalid`. When
the patched pair of timestamps violates
`end_time > start_time` the request is rejected with 422.

`user_id` (ownership), `company_id`, `auto_reminder_sent_at`,
and the audit timestamps are not patchable through this
endpoint.

Foreign-key references in the body (`customer_id`, `vehicle_id`,
`worksheet_id`, `work_category_id`, `calendar_id`) must resolve
to resources owned by the authenticated user's company.
Unreachable refs return 422 `parameter_invalid` pointing at the
offending field.

`worksheet_id` links the appointment to a scheda lavoro and
`null` unlinks it. The customer and vehicle left after the patch
must be the worksheet's (a worksheet without a vehicle fits any
vehicle of its customer); the ones the entry is missing are
taken from the worksheet, and a mismatch returns 422
`parameter_invalid` on `/worksheet_id`. A patch that moves
`customer_id` or `vehicle_id` off the linked worksheet, without
sending `worksheet_id`, unlinks it: the response then carries
`worksheet_id: null`. Sending `worksheet_id` (value or `null`)
needs `worksheets.read` (the key's scope, or the app user's
permission); without it the request returns 403.

## Request body

- CalendarEntryPatchRequest: Body of `PATCH /v1/calendar-entries/{id}`. Three-state PATCH semantics: omitted = unchanged, explicit `null` = clear (only for nullable fields). Sending `null` on a non-nullable field (`title`, `color`, `start_time`, `end_time`, `email_reminder`, `auto_reminder_enabled`) returns 422 `parameter_invalid`. When `start_time` or `end_time` is patched the resulting pair must satisfy `end_time > start_time`. `worksheet_id` links the appointment to a scheda lavoro; `null` unlinks. The customer and vehicle after the patch must be the worksheet's (a worksheet without a vehicle fits any vehicle of its customer); the ones the entry is left without are taken from the worksheet, and a mismatch returns 422 `parameter_invalid` on `/worksheet_id`. A patch that moves `customer_id` or `vehicle_id` off the linked worksheet, without sending `worksheet_id`, unlinks it.
  - `title` string
  - `description` string, nullable
  - `start_time` string, date-time: RFC 3339 timestamp supplied by the client. Must fall between 1900-01-01 and 2100-12-31. A year outside that window is a typo (2025 mistyped as 0025 is the common one), and the app's own date pickers refuse the same range — a record written with one would not be editable in the web UI afterwards. Out of range → 422 `parameter_invalid` with `param` pointing at the offending field.
  - `end_time` string, date-time: RFC 3339 timestamp supplied by the client. Must fall between 1900-01-01 and 2100-12-31. A year outside that window is a typo (2025 mistyped as 0025 is the common one), and the app's own date pickers refuse the same range — a record written with one would not be editable in the web UI afterwards. Out of range → 422 `parameter_invalid` with `param` pointing at the offending field.
  - `color` string
  - `email_reminder` boolean
  - `customer_id` string, uuid, nullable
  - `vehicle_id` string, uuid, nullable
  - `worksheet_id` string, uuid, nullable
  - `work_category_id` string, uuid, nullable: Single-category form for clients that handle one category, accepted in every revision with the same meaning: a value replaces the first category of the list and keeps the others; `null` removes the first one. To set or clear the full list, send `work_category_ids` instead (not both).
  - `work_category_ids` string[], nullable: Replaces the document's whole category list, in the order given. Omitted → untouched; `[]` or `null` → removes every category; a list → becomes the new list. Same validation as on create (max 10, de-duplicated, company's categories only). Do not send together with `work_category_id`: both → `422 parameter_invalid`. Available since revision 2026-10-03.
  - `calendar_id` string, uuid, nullable
  - `auto_reminder_enabled` boolean
  - `auto_reminder_channel` 'email' | 'sms' | 'whatsapp' | 'null', nullable

## Response `200`

Calendar entry updated.

- CalendarEntry: A workshop appointment. Belongs to one company (the caller's) and is owned by the user who created it. May reference an optional Customer, Vehicle, scheda lavoro (`worksheet_id`), work category, and parent Calendar. While `worksheet_id` is set, `customer_id` is the worksheet's and so is `vehicle_id` whenever the worksheet has one.
  - `object` 'calendar_entry', required
  - `id` string, uuid, required
  - `title` string, required
  - `description` string, nullable
  - `user_id` string, uuid, required
  - `start_time` string, date-time, required
  - `end_time` string, date-time, required
  - `color` string, required: Hex colour, e.g. #2563eb
  - `email_reminder` boolean, required
  - `customer_id` string, uuid, nullable
  - `vehicle_id` string, uuid, nullable
  - `worksheet_id` string, uuid, nullable
  - `quote_id` string, uuid, nullable
  - `checkin_id` string, uuid, nullable
  - `work_category_id` string, uuid, nullable
  - `work_category_name` string, nullable
  - `work_category_ids` string[], required: Every work category ("categoria") of the document, in the order chosen. `work_category_id` (kept for clients that handle a single category) always equals the first. `[]` when the document has none, never null. Available since revision 2026-10-03.
  - `work_categories` WorkCategory[], required: The same list as `work_category_ids` with each category's name, same order. `work_category_name` is the first one's name. Available since revision 2026-10-03.
    - `object` 'work_category', required
    - `id` string, uuid, required
    - `name` string, required
  - `calendar_id` string, uuid, nullable
  - `auto_reminder_enabled` boolean, required
  - `auto_reminder_channel` 'email' | 'sms' | 'whatsapp' | 'null', nullable
  - `auto_reminder_sent_at` string, date-time, nullable
  - `customer` CalendarEntryCustomer: Slim denormalized view of the linked customer, embedded inside a CalendarEntry. The full Customer resource lives at `/v1/customers/{id}`.
    - `object` 'customer', required
    - `id` string, uuid, required
    - `full_name` string, nullable
    - `email` string, nullable
    - `phone` string, nullable
  - `vehicle` CalendarEntryVehicle: Slim denormalized view of the linked vehicle, embedded inside a CalendarEntry. The full Vehicle resource lives at `/v1/vehicles/{id}`.
    - `object` 'vehicle', required
    - `id` string, uuid, required
    - `license` string, nullable
    - `model` string, required
    - `manufacturer_name` string, required
  - `user` CalendarEntryUser: Slim denormalized view of the owning user, embedded inside a CalendarEntry.
    - `object` 'user', required
    - `id` string, uuid, required
    - `name` string, required
  - `calendar` CalendarEntryCalendar: Slim denormalized view of the parent calendar, embedded inside a CalendarEntry. The Calendar container itself is not exposed on this tier.
    - `object` 'calendar', required
    - `id` string, uuid, required
    - `name` string, required
  - `created_at` string, date-time, required
  - `updated_at` string, date-time, required
  - `deleted_at` string, date-time, nullable

## Other responses

- `400`: Request body is unparseable or X-API-Version is missing.
- `401`: Missing, malformed, expired, or wrong-audience bearer token.
- `402`: The company has no active subscription, or its plan does not include API access. API access is included in the Business and Illimitato plans.
- `403`: The authenticated user lacks the required permission.
- `404`: Resource does not exist OR belongs to a different company. The two cases are intentionally indistinguishable.
- `415`: Content-Type is not `application/json`, or Accept rejects JSON.
- `422`: A request field is missing or invalid (fast-fail).
- `500`: Unhandled server error. Detail is logged, not exposed.

---

[API](https://skmtc.dev/officina/apis/officina-it-api.md) · [All operations](https://skmtc.dev/officina/apis/officina-it-api/llms.txt) · [OpenAPI document](https://skmtc.dev/officina/apis/officina-it-api/revisions/7da3fccfe551?raw)
