# PATCH /api/v2/events/{event}

Update an event

- MCP id: events.update
- Scope: api:write:events
- Permission: events::manage_self
- Rate cost: 1 unit, write bucket. A write costs the same whatever `view` you ask for.
- Cache-Control: private, no-store

Partially updates an event: absent keys are untouched, an empty body is rejected. ⚠ On a recurring master this edits the whole series (every occurrence moves), where plnnrly would have asked "this occurrence / this and following / all".

There is no per-occurrence write surface in this version: `parent_event_id` and `occurrence_date` are server-assigned. `start`, `end` and the two recurrence instants are stored exactly as sent, with no timezone conversion. Send `task_id: null` to unlink the task, which also stops time being logged on the event; setting a task additionally requires that the task be selectable by the event's owner, not merely by you, so re-pointing a colleague's event at your own task is refused. Note: `end` is only checked against `start` when both are sent in the same request, so sending `end` alone can leave it before the stored `start`; an event in that state measures zero planned minutes everywhere it is read. Changing the `@`-mentions in the description notifies the people newly mentioned. Send `If-Match` with the `ETag` from your last read to detect a concurrent edit, which answers `412`. Treat it as best-effort only: this resource carries no concurrency token, and `updated_at` is second-precision, so two writes inside the same second are indistinguishable.

## Parameters
- event (integer, path): The Event's numeric id. Accepts a soft-deleted record.
- view (string, optional): Which field set to render.
- trashed (string, optional): Whether soft-deleted rows are included.
- X-Request-Id (header, optional): A correlation id of your own. It is recorded in our logs next to the server's request id but never returned: the response header always carries the server's id. A value that does not match the pattern is discarded silently, never rejected.
- If-Match (header, optional): The ETag a write is conditional on. A stale validator is 412 precondition-failed.

## Request body
- title (string ≤ 255, optional): Event title, as shown on the calendar tile.
- start (timestamp (YYYY-MM-DDThh:mm:ssZ), optional): When the event starts. RFC 3339 UTC. Must be after -5 years.
- end (timestamp (YYYY-MM-DDThh:mm:ssZ), optional): When the event ends. RFC 3339 UTC. Must be on or after start. Must be before +5 years.
- is_all_day (boolean, optional): Whether the event occupies whole days rather than a time range. An all-day event is measured against the owner's working week rather than against start and end.
- description (string or null, optional): Free-text description, authored in the rich editor. HTML, sanitised on the way in and on the way out; `@`-mention spans are preserved, and changing them is what notifies the people mentioned.
- task_id (integer or null → Task.id, optional): Kanban task this event is planned against, or `null` for a plain calendar block. Only an event with a task can have time logged on it.
- is_recurring (boolean, optional): Whether this row is the master of a recurring series. When `true`, the recurrence fields describe how occurrences are generated.
- recurrence_interval (integer or null ≥ 1, optional): How many frequency units pass between occurrences: 2 with a weekly frequency is every other week.
- recurrence_frequency (string or null, optional): The unit the series repeats in.
- recurrence_pattern (string or null, optional): How a monthly or yearly series positions its occurrence within the month. Ignored for a daily or weekly frequency.
- recurrence_start (timestamp (YYYY-MM-DDThh:mm:ssZ) or null, optional): First moment the series may generate an occurrence.
- recurrence_end (timestamp (YYYY-MM-DDThh:mm:ssZ) or null, optional): Last moment the series may generate an occurrence; `null` for an open-ended series. Must be on or after recurrence_start.
- selected_weekdays (array of string or null, optional): Weekdays a recurring event falls on. Applies to a weekly frequency; ignored otherwise.
- recurrence_exceptions (array of string or null, optional): Dates on which this recurrence does not occur. plnnrly adds one whenever a single occurrence is separated or deleted.

Unknown keys are refused with `422` validation-failed, naming the key.

## Response 200
- id (integer, required): Unique identifier.
- user_id (integer, required, server-assigned): Whose agenda the event is on. Always the authenticated user at creation time; not writable.
- task_id (integer or null, required): Kanban task this event is planned against, or `null` for a plain calendar block. Only an event with a task can have time logged on it.
- parent_event_id (integer or null, required, server-assigned): Master event, when this row is a separated occurrence of a recurring series. Set by the server; not writable.
- occurrence_date (date (YYYY-MM-DD) or null, required, server-assigned): The date of the series occurrence this row replaces. Set by the server; not writable.
- start (timestamp (YYYY-MM-DDThh:mm:ssZ), required): When the event starts. RFC 3339 UTC.
- end (timestamp (YYYY-MM-DDThh:mm:ssZ), required): When the event ends. RFC 3339 UTC.
- title (string ≤ 255, required): Event title, as shown on the calendar tile.
- description (html or null, required, detail only): Free-text description, authored in the rich editor. HTML, sanitised on the way in and on the way out; `@`-mention spans are preserved, and changing them is what notifies the people mentioned.
- is_all_day (boolean, required): Whether the event occupies whole days rather than a time range. An all-day event is measured against the owner's working week rather than against start and end.
- is_recurring (boolean, required): Whether this row is the master of a recurring series. When `true`, the recurrence fields describe how occurrences are generated.
- recurrence_interval (integer or null, required): How many frequency units pass between occurrences: 2 with a weekly frequency is every other week.
- recurrence_frequency (string or null, required): The unit the series repeats in.
- recurrence_pattern (string or null, required): How a monthly or yearly series positions its occurrence within the month. Ignored for a daily or weekly frequency.
- recurrence_start (timestamp (YYYY-MM-DDThh:mm:ssZ) or null, required): First moment the series may generate an occurrence.
- recurrence_end (timestamp (YYYY-MM-DDThh:mm:ssZ) or null, required): Last moment the series may generate an occurrence; `null` for an open-ended series.
- selected_weekdays (array of string or null, required, detail only): Weekdays a recurring event falls on. Applies to a weekly frequency; ignored otherwise.
- recurrence_exceptions (array of string or null, required, detail only): Dates on which this recurrence does not occur. plnnrly adds one whenever a single occurrence is separated or deleted.
- created_at (timestamp (YYYY-MM-DDThh:mm:ssZ), required): Creation timestamp.
- updated_at (timestamp (YYYY-MM-DDThh:mm:ssZ), required): Last-write timestamp; use for incremental sync.
- deleted_at (timestamp (YYYY-MM-DDThh:mm:ssZ) or null, required): When the event was deleted, or `null` while it is live. Reach a deleted event with `trashed=with` and undo it with POST `events/{event}/actions/restore`.
- user (Ref or UserSummary, required): The user whose agenda the event is on. Renders as an `{"id": N}` reference unless expanded.
- links (EventLinks, required)
  - self (url, required): The canonical URI of this record.

## Errors
- 403 forbidden-by-policy | insufficient-permission | insufficient-token-scope: Forbidden.
  - `forbidden-by-policy`: The event is your own and you hold `events::manage_for_others` but not `events::manage_self`, or the `task_id` you send is a task the event's owner cannot select. Another user's event answers `404` instead when you do not hold `events::manage_for_others`, because you cannot see it.
- 412 precondition-failed: Precondition failed.
- 415 unsupported-media-type: Unsupported media type.
- 422 unprocessable-relationship | validation-failed: Unprocessable content.
- 400 idempotency-key-required | invalid-cursor | invalid-query-parameter | malformed-request | unsupported-parameter: Bad request.
- 401 token-expired | unauthenticated: Unauthenticated.
- 404 endpoint-not-found | resource-not-found: Not found.
- 406 not-acceptable: Not acceptable.
- 429 rate-limit-exceeded: Too many requests.
- 500 internal-error: Server error.
- 503 tenant-unavailable: Service unavailable.

## Example
```bash
curl -X PATCH \
  'https://<your-url>/api/v2/events/1' \
  -H 'Authorization: Bearer <token>' \
  -H 'Accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'If-Match: *' \
  -d '{
    "start": "2026-03-03T10:00:00Z",
    "end": "2026-03-03T11:30:00Z"
  }'
```
