# Events

> Create, change and remove what is on a Pidgeon calendar, and the wall-clock time that is the truth of an event rather than the timezone it was written in.

`title`, `start` and `end` are the whole of what is required. Everything else
has a default worth having.

```bash
curl https://pidgeon.ai/api/v1/identities/0c939bd7-2d69-4150-a2ac-de994f0e4887/calendar/events \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{
  "title": "Invoice 4471",
  "start": "2026-11-03T09:00:00",
  "end": "2026-11-03T09:30:00",
  "timezone": "Europe/London",
  "location": "A call",
  "attendees": [{ "email": "priya@example.com", "name": "Priya Raman" }]
}'
```

```ts
const { data } = await calendarCreateEvent({
path: { id: '0c939bd7-2d69-4150-a2ac-de994f0e4887' },
body: {
  title: 'Invoice 4471',
  start: '2026-11-03T09:00:00',
  end: '2026-11-03T09:30:00',
  timezone: 'Europe/London',
  location: 'A call',
  attendees: [{ email: 'priya@example.com', name: 'Priya Raman' }],
},
});
```

```python
from pidgeon.api.calendar import calendar_create_event

event = calendar_create_event.sync(
  client=client,
  id="0c939bd7-2d69-4150-a2ac-de994f0e4887",
  body=body,
)
```

**`timezone` is optional and you should send it.** Without one the calendar's
default is used, which is right for an event somebody is adding to their own
day and wrong for one you are scheduling on their behalf from a server
somewhere else.

## Inviting people

`attendees` is a list of addresses, up to 200. Adding them does not send
anything: **`send_invites` does**, and it is a separate field because putting
a meeting on a calendar and telling five people about it are different acts,
and only one of them is undoable.

```json
{
"title": "Invoice 4471",
"attendees": [{ "email": "priya@example.com" }],
"send_invites": true
}
```

With it, Pidgeon sends a real `REQUEST` from the mailbox — the kind Gmail and
Outlook put an Accept button on. [Invitations](https://pidgeon.ai/docs/calendar/invitations.md) has
what arrives and what comes back.

## Changing one

`PATCH` the event. **An omitted field survives**, so a change to the title is
a body with a title in it and nothing else.

```sh
PATCH /v1/identities/{id}/calendar/events/{eventId}
```

Two things are worth knowing before you write.

**`sequence` rises only when the change is one attendees see** — the time, the
title, the place, the status, the list of people. Editing `metadata` is yours
and does not bump it, so it does not make everybody's calendar client announce
an update about nothing.

**`If-Match` makes the write conditional.** Every event carries an `etag`; send
it back and the write is refused if somebody changed the event in between
rather than silently overwriting them.

```bash
curl -X PATCH https://pidgeon.ai/api/v1/identities/0c939bd7-2d69-4150-a2ac-de994f0e4887/calendar/events/a093d804-b045-4909-a9ab-b359db151a7d \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H 'If-Match: "7c2ae104"' \
-H "Content-Type: application/json" \
-d '{ "location": "Moved to the big room" }'
```

```ts
await calendarUpdateEvent({
path: {
  id: '0c939bd7-2d69-4150-a2ac-de994f0e4887',
  eventId: 'a093d804-b045-4909-a9ab-b359db151a7d',
},
headers: { 'If-Match': event.etag },
body: { location: 'Moved to the big room' },
});
```

```python
calendar_update_event.sync(
  client=client,
  id="0c939bd7-2d69-4150-a2ac-de994f0e4887",
  event_id="a093d804-b045-4909-a9ab-b359db151a7d",
  if_match=event.etag,
  body=body,
)
```

A mismatch answers `412`. Read the event again, decide what to do with the
change somebody else made, and write again — the same shape as any other
optimistic write.

## Taking it off

`DELETE` removes the event. For one date of a repeating series it does
something subtler, because the rule would simply generate that date again —
[Recurring events](https://pidgeon.ai/docs/calendar/recurring.md) has `mode` and what each value
means.
