# Invitations

> Answer a meeting invitation that arrived as mail, and send one from a Pidgeon mailbox, with the iCalendar parts a real client expects.

A meeting invitation is an email with a `text/calendar` part. Pidgeon reads
them when they arrive and sends them when you ask, so an agent can both be
invited to something and invite somebody.

## When one arrives

Nothing to do. The message lands in the mailbox like any other, and the
invitation inside it becomes an event on that mailbox's calendar with
`source: "mail"` and `origin_message_id` pointing back at the mail it came
from.

That event is read-only in the sense that matters: Pidgeon never sends
updates for it. It belongs to whoever organised it.

## Answering one

There are two ways in because there are two things you might be holding.

**From the message**, which is where a webhook leaves you:

```bash
curl https://pidgeon.ai/api/v1/messages/257a80f5-5614-40af-ac9c-c5416986aed9/invitation/respond \
-H "Authorization: Bearer $PIDGEON_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "status": "accepted", "comment": "See you then." }'
```

```ts
await messagesRespondToInvitation({
path: { id: '257a80f5-5614-40af-ac9c-c5416986aed9' },
body: { status: 'accepted', comment: 'See you then.' },
});
```

```python
from pidgeon.api.messages import messages_respond_to_invitation

messages_respond_to_invitation.sync(
  client=client,
  id="257a80f5-5614-40af-ac9c-c5416986aed9",
  body=body,
)
```

**From the event**, which is where an agenda leaves you:

```sh
POST /v1/identities/{id}/calendar/events/{eventId}/respond
```

The same act from the other end. An agent working through what is on today
holds an event id and not a message id, and should not have to go and find
one.

`status` is `accepted`, `declined` or `tentative`. A `comment` rides along if
you have something to say.

## What answering actually does

**It sends mail.** An iCalendar `REPLY` goes back to the organiser, which is
how Google Calendar, Outlook and Calendly learn that somebody accepted — they
are not watching a database, they are reading their inbox.

This is the one call on these pages that changes something in somebody else's
calendar, so it is worth being deliberate about. Responding from the event
takes **`send_reply`**, default `true`; set it to `false` to record the answer
on your own calendar and tell nobody.

## Sending your own

An event you created is yours to organise, and `send_invites` on create —
or on an update that adds people — sends a real `REQUEST` from the mailbox:

```json
{
"title": "Invoice 4471",
"start": "2026-11-03T09:00:00",
"end": "2026-11-03T09:30:00",
"timezone": "Europe/London",
"attendees": [{ "email": "priya@example.com", "name": "Priya Raman" }],
"send_invites": true
}
```

What arrives is the kind of invitation a mail client puts an Accept button on,
and the replies come back to the mailbox and update the attendee list.

**Only for events Pidgeon owns.** An event with `source: "mail"` came from
somebody else's calendar, and sending a `REQUEST` for it would be forging an
invitation from an organiser who never sent one. Asking is refused rather than
quietly ignored.

## A series, and the time zone it carries

An invitation for a repeating meeting carries a `VTIMEZONE` — the rules for
the zone the meeting repeats in, so the recipient's client puts it at 09:00
local on every date rather than drifting an hour at the clock change. Pidgeon
generates one from the event's own zone. A one-off needs none: a single moment
is sent as UTC, which every client reads the same way.
