---
title: "Update Event"
method: PATCH
path: "/v0/inboxes/{inbox_id}/calendar/events/{event_id}"
tags: ["inboxes > calendar"]
---

# Update Event

`PATCH /v0/inboxes/{inbox_id}/calendar/events/{event_id}`

## Request

- Base URL: `https://api.agentmail.to`
- URL: `https://api.agentmail.to/v0/inboxes/{inbox_id}/calendar/events/{event_id}`
- Auth: HTTP bearer

Updates an event. Send only the fields to change. To make the update conditional, send the
event's current `etag` in `If-Match`: a stale value returns `412`. Without `If-Match` the
update applies to the event as it is; a change that lands while it runs returns `409`
`race_condition`, so retry. Send `If-Match` when replacing `attendees`, so you don't
overwrite a response that arrived in the meantime.

- **One-off or series UUID:** changes the event itself. For a series, the change applies to
  every date that has not been edited individually.
- **Dated ID (`<uuid>_<slot>`):** changes one date (`mode=single`, the default) or that date
  and every later date (`mode=future`). `all_day`, `timezone` and `recurrence` cannot be sent
  for a dated ID.

Once a date has started, its start can no longer change (409 `event_already_started`), but
its end and status can; for a one-off or series UUID, send the unchanged `start` with the new
`end`. Once it has ended, only `title`, `description`, `location`,
`metadata` and `attendees` can change. During the few seconds a date is starting, schedule
changes return 409 `event_starting`; retry shortly.

Sends `calendar.event.updated`. With `send_invites: true` the organizer inbox also emails the
updated invitation to every attendee. Requires the `calendar_event_update` permission.

Emailing attendees counts one send per attendee against the organization, pod and inbox
send limits, charged before the change is saved; an over-limit request returns `429`
`rate_limit_exceeded` and changes nothing.

Calendar is available in US production (`api.agentmail.to`) but not yet in EU production
(`api.agentmail.eu`), which returns `403`.

## Path parameters

- `inbox_id` string, required: The ID of the inbox.
- `event_id` string, required: ID of a calendar event. A one-off or recurring event is addressed by its UUID. One date of a recurring event is addressed by a dated ID, `<uuid>_<slot>`: `<uuid>_t20261015T090000` for a timed date (the original wall-clock start in the event's time zone) or `<uuid>_d20261015` for an all-day date. Dated IDs are stable: moving a date keeps its original ID.

## Query parameters

- `mode` 'single' | 'future': Which dates a change to a dated event applies to. `single` (default) changes only this date. `future` changes this date and every later date of the series. Applies only to dated event IDs; sending `mode` with a one-off or series UUID is a `400`.

## Headers

- `If-Match` string

## Request body

- object
  - `title` string
  - `description` string, nullable: Set to `null` to clear.
  - `location` string, nullable: Set to `null` to clear.
  - `metadata` unknown
  - `status` 'confirmed' | 'tentative' | 'cancelled': Status of the event. A `cancelled` event or date stays readable but sends no `calendar.event.starting` or `calendar.event.ending` webhooks (cancelling a date that is already running still sends its `calendar.event.ending`). A date cancelled by deleting its dated ID is removed from the series instead, so reading it can return `404` or `410`.
  - `all_day` boolean: Changing `all_day` requires `start`, `end` and `timezone` in the same request. Not accepted for dated event IDs.
  - `start` string: A local wall-clock time without an offset, interpreted in the event's `timezone`. Timed events use `YYYY-MM-DDTHH:mm:ss` (for example `2026-10-15T09:00:00`). All-day events use `YYYY-MM-DD`. Years must be between 1900 and 9999.
  - `end` string: A local wall-clock time without an offset, interpreted in the event's `timezone`. Timed events use `YYYY-MM-DDTHH:mm:ss` (for example `2026-10-15T09:00:00`). All-day events use `YYYY-MM-DD`. Years must be between 1900 and 9999.
  - `timezone` string: IANA time zone name, for example `America/New_York`, `Europe/London` or `UTC`. Requests accept any capitalization and IANA aliases such as `US/Eastern`. The calendar stores and returns the canonical name, which for some zones is an older spelling: `Asia/Kolkata` is returned as `Asia/Calcutta`. Abbreviations such as `PST` and Windows names such as `Pacific Standard Time` are rejected.
  - `duration_mode` 'nominal' | 'exact': How the event's length is kept when a date crosses a daylight-saving change. - `exact`: the event lasts the same elapsed time. Default for one-off timed events. - `nominal`: the event keeps its wall-clock end time (a 09:00 to 10:00 meeting stays 09:00 to 10:00 local time). Default for recurring timed events. All-day events are always `nominal`.
  - `recurrence` CalendarRecurrenceInput: Repeats the event. `rule` is an RFC 5545 RRULE, evaluated in the event's `timezone` from the event's `start`. Each date keeps its wall-clock time across daylight-saving changes.
    - `rule` string, required: RFC 5545 RRULE body, with or without the `RRULE:` prefix, for example `FREQ=WEEKLY;BYDAY=MO,WE,FR` or `FREQ=MONTHLY;BYDAY=-1FR;COUNT=12`. Supported parts: `FREQ`, `INTERVAL` (1 to 366), `COUNT` (1 to 10,000), `UNTIL` (`YYYYMMDD` for all-day events, `YYYYMMDDTHHMMSSZ` for timed events), `WKST`, `BYMONTH`, `BYWEEKNO`, `BYYEARDAY`, `BYMONTHDAY`, `BYDAY`, `BYHOUR`, `BYMINUTE`, `BYSECOND` and `BYSETPOS`. `COUNT` and `UNTIL` are mutually exclusive. The rule is stored in a normalized form, so a response can differ textually from what you sent. At most 512 characters after normalization.
    - `exdates` CalendarWallTime[]: Dates to skip, matching the original start of a date: `YYYY-MM-DDTHH:mm:ss` for timed events, `YYYY-MM-DD` for all-day events. At most 366.
    - `rdates` CalendarRecurrenceDate[]: Extra dates to add. Timed events take date-times or periods; all-day events take dates. At most 100.
      - union: A wall-clock date or date-time, or (timed events only) a period.
        - string: A local wall-clock time without an offset, interpreted in the event's `timezone`. Timed events use `YYYY-MM-DDTHH:mm:ss` (for example `2026-10-15T09:00:00`). All-day events use `YYYY-MM-DD`. Years must be between 1900 and 9999.
        - CalendarRecurrencePeriod: An extra date (RDATE) given as a period. Specify exactly one of `end` or `duration`.
          - `start` string, required: A local wall-clock time without an offset, interpreted in the event's `timezone`. Timed events use `YYYY-MM-DDTHH:mm:ss` (for example `2026-10-15T09:00:00`). All-day events use `YYYY-MM-DD`. Years must be between 1900 and 9999.
          - `end` string: A local wall-clock time without an offset, interpreted in the event's `timezone`. Timed events use `YYYY-MM-DDTHH:mm:ss` (for example `2026-10-15T09:00:00`). All-day events use `YYYY-MM-DD`. Years must be between 1900 and 9999.
          - `duration` string: ISO 8601 duration, for example `PT30M`.
  - `attendees` CalendarAttendee[]: Replaces the attendee list.
    - `email` string, required: Email address of the attendee. Stored in lowercase. Must be unique within the event.
    - `name` string: Display name of the attendee. At most 256 characters.
    - `status` 'needs_action' | 'accepted' | 'declined' | 'tentative': The attendee's response to the invitation.
    - `role` 'required' | 'optional': Whether the attendee's presence is required.
    - `comment` string: Comment the attendee sent with their response. At most 4,096 UTF-8 bytes; on create or update, a longer comment is dropped rather than rejected.
    - `responded_at` string, date-time: Time at which the attendee last responded.
  - `send_invites` boolean: When `true`, emails the updated invitation to every attendee. Only the organizer (an `api` event) can send. Defaults to `false`.

## Response `200`

Response with status 200

- CalendarCalendarEventMutationResponse
  - `event` CalendarCalendarEvent, required: A calendar event. The same shape is used by every response, list item and webhook. `kind` says whether it is a one-off event, the definition of a recurring series, or one date of a series.
    - `event_id` string, required: ID of a calendar event. A one-off or recurring event is addressed by its UUID. One date of a recurring event is addressed by a dated ID, `<uuid>_<slot>`: `<uuid>_t20261015T090000` for a timed date (the original wall-clock start in the event's time zone) or `<uuid>_d20261015` for an all-day date. Dated IDs are stable: moving a date keeps its original ID.
    - `kind` 'single' | 'series' | 'instance', required: What the event object represents. - `single`: a one-off event, addressed by its UUID. - `series`: the definition of a recurring event, addressed by its UUID. It carries `recurrence`; `start` and `end` describe its first date. - `instance`: one date of a recurring event, addressed by its dated ID. It carries `series_id`, `original_start` and `is_exception`.
    - `series_id` string: For `instance` events, the UUID of the series this date belongs to.
    - `original_start` string: A local wall-clock time without an offset, interpreted in the event's `timezone`. Timed events use `YYYY-MM-DDTHH:mm:ss` (for example `2026-10-15T09:00:00`). All-day events use `YYYY-MM-DD`. Years must be between 1900 and 9999.
    - `original_start_at` string, date-time: For `instance` events, `original_start` as a UTC instant.
    - `is_exception` boolean: For `instance` events, `true` when this date was edited or moved, individually or by a `mode=future` change.
    - `title` string, required: Title of the event.
    - `description` string: Description of the event. Omitted from agenda and instance list items.
    - `location` string: Location of the event.
    - `metadata` unknown
    - `status` 'confirmed' | 'tentative' | 'cancelled', required: Status of the event. A `cancelled` event or date stays readable but sends no `calendar.event.starting` or `calendar.event.ending` webhooks (cancelling a date that is already running still sends its `calendar.event.ending`). A date cancelled by deleting its dated ID is removed from the series instead, so reading it can return `404` or `410`.
    - `all_day` boolean, required: Whether this is an all-day event.
    - `start` string, required: A local wall-clock time without an offset, interpreted in the event's `timezone`. Timed events use `YYYY-MM-DDTHH:mm:ss` (for example `2026-10-15T09:00:00`). All-day events use `YYYY-MM-DD`. Years must be between 1900 and 9999.
    - `end` string, required: A local wall-clock time without an offset, interpreted in the event's `timezone`. Timed events use `YYYY-MM-DDTHH:mm:ss` (for example `2026-10-15T09:00:00`). All-day events use `YYYY-MM-DD`. Years must be between 1900 and 9999.
    - `timezone` string, required: IANA time zone name, for example `America/New_York`, `Europe/London` or `UTC`. Requests accept any capitalization and IANA aliases such as `US/Eastern`. The calendar stores and returns the canonical name, which for some zones is an older spelling: `Asia/Kolkata` is returned as `Asia/Calcutta`. Abbreviations such as `PST` and Windows names such as `Pacific Standard Time` are rejected.
    - `duration_mode` 'nominal' | 'exact', required: How the event's length is kept when a date crosses a daylight-saving change. - `exact`: the event lasts the same elapsed time. Default for one-off timed events. - `nominal`: the event keeps its wall-clock end time (a 09:00 to 10:00 meeting stays 09:00 to 10:00 local time). Default for recurring timed events. All-day events are always `nominal`.
    - `duration_value` integer, required: Length of the event, in milliseconds for timed events and in days for all-day events.
    - `start_at` string, date-time, required: Start as a UTC instant. This is when `calendar.event.starting` is due.
    - `end_at` string, date-time, required: End as a UTC instant. This is when `calendar.event.ending` is due.
    - `recurrence` CalendarRecurrence: The recurrence of a series, as stored.
      - `rule` string, required: Normalized RRULE body, without the `RRULE:` prefix.
      - `exdates` CalendarWallTime[]
      - `rdates` CalendarRecurrenceDate[]
        - union: A wall-clock date or date-time, or (timed events only) a period.
          - string: A local wall-clock time without an offset, interpreted in the event's `timezone`. Timed events use `YYYY-MM-DDTHH:mm:ss` (for example `2026-10-15T09:00:00`). All-day events use `YYYY-MM-DD`. Years must be between 1900 and 9999.
          - CalendarRecurrencePeriod: An extra date (RDATE) given as a period. Specify exactly one of `end` or `duration`.
            - `start` string, required: A local wall-clock time without an offset, interpreted in the event's `timezone`. Timed events use `YYYY-MM-DDTHH:mm:ss` (for example `2026-10-15T09:00:00`). All-day events use `YYYY-MM-DD`. Years must be between 1900 and 9999.
            - `end` string: A local wall-clock time without an offset, interpreted in the event's `timezone`. Timed events use `YYYY-MM-DDTHH:mm:ss` (for example `2026-10-15T09:00:00`). All-day events use `YYYY-MM-DD`. Years must be between 1900 and 9999.
            - `duration` string: ISO 8601 duration, for example `PT30M`.
      - `truncate_before` string: A local wall-clock time without an offset, interpreted in the event's `timezone`. Timed events use `YYYY-MM-DDTHH:mm:ss` (for example `2026-10-15T09:00:00`). All-day events use `YYYY-MM-DD`. Years must be between 1900 and 9999.
    - `attendees` CalendarAttendee[]: Attendees of the event. Omitted from agenda and instance list items; `attendee_count` is always present.
      - `email` string, required: Email address of the attendee. Stored in lowercase. Must be unique within the event.
      - `name` string: Display name of the attendee. At most 256 characters.
      - `status` 'needs_action' | 'accepted' | 'declined' | 'tentative': The attendee's response to the invitation.
      - `role` 'required' | 'optional': Whether the attendee's presence is required.
      - `comment` string: Comment the attendee sent with their response. At most 4,096 UTF-8 bytes; on create or update, a longer comment is dropped rather than rejected.
      - `responded_at` string, date-time: Time at which the attendee last responded.
    - `attendee_count` integer, required: Number of attendees.
    - `response_status` 'needs_action' | 'accepted' | 'declined' | 'tentative': The attendee's response to the invitation.
    - `uid` string, required: iCalendar UID of the event. Invitations about this event carry the same UID.
    - `sequence` integer, required: iCalendar SEQUENCE number. It increases when a change to a one-off or series event affects what attendees see (time, status, title, description, location, recurrence or attendees). A change to a dated event increases it only when the change is sent with `send_invites`.
    - `source` 'api' | 'email', required: Where the event came from. `api` events were created through this API and the inbox is their organizer. `email` events were created from a calendar invitation (iMIP) received by the inbox; their organizer is the sender of the invitation.
    - `organizer_email` string: Email address of the organizer. For `api` events, the inbox.
    - `origin_message_id` string: For `email` events, the ID of the message whose invitation last created or updated the event.
    - `resource_revision` integer: Revision of the resource. It increases on every change. The resource's `etag` is `"rv-<resource_revision>"`.
    - `created_at` string, date-time, required: Time at which the event (or, for a date, the definition governing it) was created.
    - `updated_at` string, date-time, required: Time at which the event (or, for a date, the definition governing it) was last updated.
    - `etag` string: The event's or date's current version, also sent as the `ETag` response header. Send it in `If-Match` to make an update, delete or response conditional. Present on Get, Create, Update, Delete and Respond responses; list items and webhooks omit it. Treat it as opaque: a dated event's `etag` (`"occ-..."`) is not derived from `resource_revision`.
  - `operation_id` string, required: ID of the change this request made. The `calendar.event.created`, `calendar.event.updated`, `calendar.event.deleted` or `calendar.event.responded` webhook for the change carries the same value as its `event_id`, so you can match webhooks to your own requests.

## Other responses

- `400`: Error response with status 400
- `403`: Calendar isn't available in this region (EU production), or the API key lacks the required calendar permission.
- `404`: Error response with status 404
- `409`: Error response with status 409
- `410`: The event's date is outside the retained history window and can no longer be read or changed.
- `412`: The resource changed since the `If-Match` value was read. Fetch it again, reapply the change, and retry with the new `etag`.
- `413`: The request body is too large (8 KB for calendar settings, 128 KB for events).
- `429`: Too many requests, or the invitation emails this request would send exceed an organization, pod or inbox send limit. Nothing was changed. Honor the `Retry-After` header when present.

## Changes

- **2026-10-16** `3f5c7830c952`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/agentmail-to/apis/api/changes/v0/inboxes/:inbox_id/calendar/events/:event_id/patch.md)

---

[API](https://skmtc.dev/agentmail-to/apis/api.md) - [All operations](https://skmtc.dev/agentmail-to/apis/api/llms.txt) - [OpenAPI document](https://skmtc.dev/agentmail-to/apis/api/revisions/a9792efe7bdf?raw)
