---
title: "nextmail.calendar"
description: "Every method in this namespace: its signature, its parameters, what it returns and an example."
url: "https://nextmail.sh/docs/python/reference/calendar"
area: "Python"
category: "Reference"
---

# nextmail.calendar

Every method in this namespace: its signature, its parameters, what it returns and an example.

## Methods

Calendar events found in mail, expanded into occurrences.

### `calendar.list_events()`

List one page of event occurrences inside a time window

```python
def list_events(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    from_: datetime | str,
    to: datetime | str,
    timezone: str | None = None,
    include_team: bool | None = None,
    q: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> Page[CalendarOccurrenceResource]
```

Expands your events into their occurrences between `from_=` and `to=` and returns one page of them, sorted by start time and then by event id. A recurring series gives one row per instance in the window, with its excluded dates left out, and every row points back to its event through `eventId`. The window is required because a repeating series has no end to list up to. Follow `nextCursor` while `hasMore` is `True`, with the same `from_=`, `to=` and `timezone=`, to read every occurrence, or let `list_all_events` and `iterate_events` do that walk.

`to=` must be after `from_=` and at most 366 days later. A missing bound is a 422 `invalid_parameter`, while a value that does not parse, a reversed or overlong window, or an unknown zone is a 400 `invalid_parameter` naming the field. Both bounds take a `datetime` or an ISO 8601 string. The SDK sends a `datetime` as a UTC instant, and reads a naive one as local time.

`timezone=` is an IANA zone and defaults to `UTC` on the server. It is used to expand any event stored without a zone of its own, which is what decides the day an all day event lands on.

Scopes: `calendar:read`.

**Parameters**

- `limit` (`int`): Occurrences per page, a whole number from 1 to 100. The server defaults to 25.
- `cursor` (`str`): The `nextCursor` of the previous page, sent with the same window. It is opaque, so never build one yourself.
- `from_` (`datetime | str`, required): Start of the window, a `datetime` or an ISO 8601 string, sent as the `from` query parameter.
- `to` (`datetime | str`, required): End of the window, after `from_=` and at most 366 days later.
- `timezone` (`str`): IANA zone such as `Europe/London`, at most 64 characters. Defaults to `UTC`.
- `include_team` (`bool`): `True` adds the events of the rest of the workspace, apart from the ones marked private. Only a key of the workspace owner sees more with it. Defaults to `False`.
- `q` (`str`): Words to look for in the title, the place and the organiser, at most 200 characters. Only events that hold every word come back, so pass a long window to find an event by name.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`Page[CalendarOccurrenceResource]`, a dict with `items`, `hasMore` and `nextCursor`. Each item has `eventId`, `uid`, `start`, `end`, `allDay`, `summary`, `location`, `status`, `transparency`, `organizerEmail`, `recurring`, `myPartstat` and `attendeeCount`, and `occurrence['isOrganizer']` is `True` when you organise the event.

**Example**

```python
from datetime import datetime, timedelta, timezone

from nextmail import nextmail

start = datetime.now(timezone.utc)
page = nextmail.calendar.list_events(
    from_=start, to=start + timedelta(days=7), timezone='Europe/London', limit=50
)

for occurrence in page['items']:
    print(occurrence['start'], occurrence['summary'], occurrence['myPartstat'] or 'not invited')

print(page['hasMore'], page['nextCursor'])
```

**Notes**

- The list holds your own events: the ones you made and the ones an address of yours organises or attends. For the workspace owner and their keys, an address counts as theirs unless it was given to a teammate, and `include_team=True` adds the rest of the workspace, apart from events somebody marked private. A member's app and a narrowed key always get their own view.
- `myPartstat` is the response recorded for one of your own addresses, or for the addresses a narrowed key covers, and `None` when none of them is an attendee.
- A narrowed key only sees occurrences whose organiser is one of the addresses it covers or where one of them is an attendee.
- Cancelled events are not filtered out. Check `status` for `CANCELLED`.
- Nothing in the window is dropped. A series repeats at most once a day, so one series gives at most one row per day of the window, and every one of them is reachable through the pages.
- The cursor holds the start time and event id of the last row, so an event deleted or moved between pages never breaks the walk. A cursor this list did not hand out is a 400 `invalid_cursor`.

Also available in: API [`GET /calendar/events`](https://nextmail.sh/docs/api/reference/calendar#get-calendar-events); TypeScript [`nextmail.calendar.listEvents()`](https://nextmail.sh/docs/sdk/reference/calendar#listEvents); Ruby [`calendar.list_events`](https://nextmail.sh/docs/ruby/reference/calendar#listEvents); PHP [`calendar->listEvents`](https://nextmail.sh/docs/php/reference/calendar#listEvents); Go [`Calendar.ListEvents`](https://nextmail.sh/docs/go/reference/calendar#listEvents); Java [`calendar().listEvents`](https://nextmail.sh/docs/java/reference/calendar#listEvents); C# [`Calendar.ListEventsAsync`](https://nextmail.sh/docs/csharp/reference/calendar#listEvents); CLI [`nextmail calendar list-events`](https://nextmail.sh/docs/cli/reference/calendar#calendar-list-events).

### `calendar.list_all_events()`

Collect every occurrence inside a time window into one list

```python
def list_all_events(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    from_: datetime | str,
    to: datetime | str,
    timezone: str | None = None,
    include_team: bool | None = None,
    q: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> builtins.list[CalendarOccurrenceResource]
```

Walks every page of `list_events` for one window and returns all of its occurrences in one list, sorted by start time and then by event id. It follows `nextCursor` until `hasMore` is `False`, sending the same `from_=`, `to=` and `timezone=` with each request.

A window of up to 366 days of a busy calendar can hold thousands of occurrences, so prefer `iterate_events` when you can stop early.

Scopes: `calendar:read`.

**Parameters**

- `limit` (`int`): Page size for each request, 1 to 100. The server defaults to 25.
- `cursor` (`str`): Starts the walk from this cursor instead of the start of the window.
- `from_` (`datetime | str`, required): Start of the window, a `datetime` or an ISO 8601 string, sent as the `from` query parameter.
- `to` (`datetime | str`, required): End of the window, after `from_=` and at most 366 days later.
- `timezone` (`str`): IANA zone such as `Europe/London`, at most 64 characters. Defaults to `UTC`.
- `include_team` (`bool`): `True` adds the events of the rest of the workspace, apart from the ones marked private. Only a key of the workspace owner sees more with it. Defaults to `False`.
- `q` (`str`): Words to look for in the title, the place and the organiser, at most 200 characters. Only events that hold every word come back, so pass a long window to find an event by name.
- `api_key` (`str`): Overrides the client's API key for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`list[CalendarOccurrenceResource]` holding every occurrence in the window, earliest first.

**Example**

```python
from nextmail import nextmail

occurrences = nextmail.calendar.list_all_events(
    from_='2026-10-01T00:00:00Z', to='2026-11-01T00:00:00Z', limit=100
)

busy = [occurrence for occurrence in occurrences if occurrence['transparency'] == 'OPAQUE']
print(len(busy), 'busy blocks in October')
```

**Notes**

- If any page fails the call raises, and the occurrences already fetched are discarded.

Also available in: API [`GET /calendar/events`](https://nextmail.sh/docs/api/reference/calendar#get-calendar-events); TypeScript [`nextmail.calendar.listAllEvents()`](https://nextmail.sh/docs/sdk/reference/calendar#listAllEvents); Ruby [`calendar.list_all_events`](https://nextmail.sh/docs/ruby/reference/calendar#listAllEvents); PHP [`calendar->listAllEvents`](https://nextmail.sh/docs/php/reference/calendar#listAllEvents); Go [`Calendar.ListAllEvents`](https://nextmail.sh/docs/go/reference/calendar#listAllEvents); Java [`calendar().listAllEvents`](https://nextmail.sh/docs/java/reference/calendar#listAllEvents); C# [`Calendar.ListAllEventsAsync`](https://nextmail.sh/docs/csharp/reference/calendar#listAllEvents).

### `calendar.iterate_events()`

Stream the occurrences inside a time window one at a time

```python
def iterate_events(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    from_: datetime | str,
    to: datetime | str,
    timezone: str | None = None,
    include_team: bool | None = None,
    q: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> Iterator[CalendarOccurrenceResource]
```

Returns a generator over the occurrences in one window that yields them one at a time, earliest first, and fetches the next page only when the current one is used up. Nothing is requested until the loop starts, and leaving the loop with `break` stops further requests, which makes it the way to find the next free slot or the next meeting without reading the whole window.

The walk ends when `hasMore` is `False`, when a page has no `nextCursor`, or when the server repeats a cursor.

Scopes: `calendar:read`.

**Parameters**

- `limit` (`int`): Page size per request, 1 to 100. The server defaults to 25.
- `cursor` (`str`): Starts the walk from this cursor instead of the start of the window.
- `from_` (`datetime | str`, required): Start of the window, a `datetime` or an ISO 8601 string, sent as the `from` query parameter.
- `to` (`datetime | str`, required): End of the window, after `from_=` and at most 366 days later.
- `timezone` (`str`): IANA zone such as `Europe/London`, at most 64 characters. Defaults to `UTC`.
- `include_team` (`bool`): `True` adds the events of the rest of the workspace, apart from the ones marked private. Only a key of the workspace owner sees more with it. Defaults to `False`.
- `q` (`str`): Words to look for in the title, the place and the organiser, at most 200 characters. Only events that hold every word come back, so pass a long window to find an event by name.
- `api_key` (`str`): Overrides the client's API key for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`Iterator[CalendarOccurrenceResource]`, a generator yielding one occurrence per step.

**Example**

```python
from datetime import datetime, timedelta, timezone

from nextmail import nextmail

start = datetime.now(timezone.utc)

for occurrence in nextmail.calendar.iterate_events(from_=start, to=start + timedelta(days=30)):
    if occurrence['myPartstat'] is None:
        continue

    print('Next meeting:', occurrence['start'], occurrence['summary'])
    break
```

**Notes**

- `timeout=` bounds each page request on its own, not the whole walk. A page that fails raises out of the `for` loop, after the occurrences already yielded.

Also available in: API [`GET /calendar/events`](https://nextmail.sh/docs/api/reference/calendar#get-calendar-events); TypeScript [`nextmail.calendar.iterateEvents()`](https://nextmail.sh/docs/sdk/reference/calendar#iterateEvents); Ruby [`calendar.iterate_events`](https://nextmail.sh/docs/ruby/reference/calendar#iterateEvents); PHP [`calendar->iterateEvents`](https://nextmail.sh/docs/php/reference/calendar#iterateEvents); Go [`Calendar.IterateEvents`](https://nextmail.sh/docs/go/reference/calendar#iterateEvents); Java [`calendar().iterateEvents`](https://nextmail.sh/docs/java/reference/calendar#iterateEvents); C# [`Calendar.IterateEventsAsync`](https://nextmail.sh/docs/csharp/reference/calendar#iterateEvents).

### `calendar.get_event()`

Read an event with its attendees

```python
def get_event(
    id: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> CalendarEventResource
```

Returns a stored event with its attendees and their responses. What the server understood from the iCalendar data is structured here: `recurrence` holds the `RRULE` text, `exdates` the excluded instances, `organizer` the organiser and `alarms` the reminders, each a dict with `minutesBefore` and `action`. The raw iCalendar source is not included, and `get_event_ics` serves the event as a document.

`id` is the event id, the `eventId` on an occurrence, and not the iCalendar `uid`. A recurring series is one event however many occurrences it has, so `start` and `end` describe its first instance. `messageId` and `threadId` link the event to mail in the mailbox when there is any.

Attendees come back in the order they were added, each with `partstat`, `role`, `rsvp`, `cutype` and `respondedAt`. A narrowed key gets a 404 unless one of the addresses it covers organises the event or is on the attendee list, and a key that holds a whole domain covers every address on it.

Scopes: `calendar:read`.

**Parameters**

- `id` (`str`, required): Event id, the `eventId` of an occurrence, such as `cal_` followed by 24 hex characters.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`CalendarEventResource` with `id`, `uid`, `sequence`, `summary`, `description`, `location`, `url`, `start`, `end`, `allDay`, `timezone`, `recurrence`, `exdates`, `status`, `transparency`, `visibility`, `organizer`, `source`, `messageId`, `threadId`, `alarms`, `attendees`, `createdAt` and `updatedAt`. `event['isOrganizer']` is `True` when you organise it, and so may change it.

**Example**

```python
from nextmail import nextmail

event = nextmail.calendar.get_event('cal_3f9a1c7e5b2d48a06c1e9f4b')

print(event['summary'], event['recurrence'] or 'one off')

for attendee in event['attendees']:
    print(attendee['email'], attendee['partstat'])
```

**Notes**

- `status`, `transparency` and `visibility` carry iCalendar values, and default to `CONFIRMED`, `OPAQUE` and `PUBLIC`.
- An attendee's `partstat` defaults to `NEEDS-ACTION` and `role` to `REQ-PARTICIPANT` until a response is recorded.

Also available in: API [`GET /calendar/events/{id}`](https://nextmail.sh/docs/api/reference/calendar#get-calendar-events-id); TypeScript [`nextmail.calendar.getEvent()`](https://nextmail.sh/docs/sdk/reference/calendar#getEvent); Ruby [`calendar.get_event`](https://nextmail.sh/docs/ruby/reference/calendar#getEvent); PHP [`calendar->getEvent`](https://nextmail.sh/docs/php/reference/calendar#getEvent); Go [`Calendar.GetEvent`](https://nextmail.sh/docs/go/reference/calendar#getEvent); Java [`calendar().getEvent`](https://nextmail.sh/docs/java/reference/calendar#getEvent); C# [`Calendar.GetEventAsync`](https://nextmail.sh/docs/csharp/reference/calendar#getEvent); CLI [`nextmail calendar get-event`](https://nextmail.sh/docs/cli/reference/calendar#calendar-get-event).

### `calendar.get_event_ics()`

Download an event as an iCalendar document

```python
def get_event_ics(
    id: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> str
```

Returns the event serialised as an `.ics` document, as a `str` rather than parsed JSON. The server sends it as `text/calendar; charset=utf-8` with a download filename built from the event's `uid`, and the SDK asks for that type and returns the body as text.

The document declares `METHOD:PUBLISH`, and the content type carries no `method` parameter. That is deliberate: a `REQUEST` document makes a calendar client offer accept and decline and reply to the organiser, and this endpoint has no authority to invite anyone. Import it to show what the event currently is, not to send an invitation.

It holds one `VEVENT` with the attendees and their participation status, the recurrence rule, excluded dates and alarms. Lines end in CRLF and long lines are folded. `DTSTAMP` is the time of the request, so two downloads of an unchanged event differ on that line.

Scopes: `calendar:read`.

**Parameters**

- `id` (`str`, required): Event id, the `eventId` of an occurrence.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`str`, the iCalendar text from `BEGIN:VCALENDAR` to `END:VCALENDAR`.

**Example**

```python
from pathlib import Path

from nextmail import nextmail

ics = nextmail.calendar.get_event_ics('cal_8d3f0a2b9c4e41f7a6b5c2d1')
Path('planning.ics').write_text(ics, newline='')

summary = next((line for line in ics.splitlines() if line.startswith('SUMMARY:')), None)
print(summary)
```

**Notes**

- Errors still come back as JSON and raise `NextmailApiError`, so a failure never comes back as a string.
- Timed events are written in the zone they were created in, as local times with a `TZID` and a `VTIMEZONE` that carries the rules of that zone, and all day events as `DATE` values.
- Save it with `newline=''`, as in `Path('event.ics').write_text(ics, newline='')`, so the CRLF line endings are written as they are on every platform.
- A narrowed key gets a 404 on the same terms as `get_event`.

Also available in: API [`GET /calendar/events/{id}/ics`](https://nextmail.sh/docs/api/reference/calendar#get-calendar-events-id-ics); TypeScript [`nextmail.calendar.getEventIcs()`](https://nextmail.sh/docs/sdk/reference/calendar#getEventIcs); Ruby [`calendar.get_event_ics`](https://nextmail.sh/docs/ruby/reference/calendar#getEventIcs); PHP [`calendar->getEventIcs`](https://nextmail.sh/docs/php/reference/calendar#getEventIcs); Go [`Calendar.GetEventIcs`](https://nextmail.sh/docs/go/reference/calendar#getEventIcs); Java [`calendar().getEventIcs`](https://nextmail.sh/docs/java/reference/calendar#getEventIcs); C# [`Calendar.GetEventIcsAsync`](https://nextmail.sh/docs/csharp/reference/calendar#getEventIcs); CLI [`nextmail calendar get-event-ics`](https://nextmail.sh/docs/cli/reference/calendar#calendar-get-event-ics).

### `calendar.create_event()`

Put an event on the calendar

```python
def create_event(
    body: CalendarEventCreate,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> CalendarEventResource
```

Creates an event, as New event on the Calendar page of the app does. `start` and `end` take a `datetime` or an ISO 8601 string with a zone, and `timezone` is the IANA zone the event belongs to, which decides what an all-day event means. The SDK sends a `datetime` as a UTC instant, and reads a naive one as local time.

With `attendees`, an invitation goes to each of them by email from `from`, which has to be an address the key may send as, so the call also needs `emails:send`. Send `'sendInvites': False` to keep it to your own calendar.

Scopes: `calendar:write`.

**Parameters**

- `body['summary']` (`str`, required): The title, 1 to 255 characters.
- `body['start']` (`datetime | str`, required): When it starts, a `datetime` or an ISO 8601 string with a zone.
- `body['end']` (`datetime | str`, required): When it ends, after `start` and at most two years later.
- `body['description']` (`str`): Notes about the event, up to 8,000 characters.
- `body['location']` (`str`): Where it is: a room, an address or a link.
- `body['url']` (`str`): A link that belongs to the event.
- `body['meetingUrl']` (`str`): The link to the video call, such as a Zoom, Meet or Teams address. The invitation carries it, so guests get a Join button in their calendar too.
- `body['allDay']` (`bool`): Whether it fills whole days. `start` and `end` then mark the days.
- `body['timezone']` (`str`): The IANA zone the event belongs to, `UTC` unless you say.
- `body['recurrence']` (`str`): An RRULE such as `FREQ=WEEKLY;BYDAY=MO` when it repeats.
- `body['attendees']` (`list[CalendarAttendeeInput]`): Up to 200 people to invite, each a dict with `email` and optionally `name` and `optional`, such as `{'email': 'ada@example.com', 'name': 'Ada Lovelace'}`.
- `body['reminders']` (`list[CalendarReminderInput]`): Up to five reminders, each a dict with `minutesBefore` and optionally `action`, one of `DISPLAY`, `EMAIL` and `AUDIO`, such as `{'minutesBefore': 15}`.
- `body['transparency']` (`CalendarTransparency`): `OPAQUE` shows you busy and `TRANSPARENT` free.
- `body['visibility']` (`CalendarVisibility`): `PUBLIC`, `PRIVATE` or `CONFIDENTIAL`.
- `body['sendInvites']` (`bool`): `False` keeps the event to your own calendar and sends nothing.
- `body['from']` (`str`): The address the invitations come from. It has to be one the key may send as.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`CalendarEventResource` for the new event, with its attendees and their responses.

**Example**

```python
from datetime import datetime, timedelta, timezone

from nextmail import nextmail

start = datetime(2026, 10, 12, 9, 0, tzinfo=timezone.utc)
event = nextmail.calendar.create_event(
    {
        'summary': 'Planning',
        'start': start,
        'end': start + timedelta(minutes=30),
        'attendees': [{'email': 'ada@example.com', 'name': 'Ada Lovelace'}],
        'reminders': [{'minutesBefore': 15}],
        'from': 'team@acme.com',
    }
)

print(event['id'], event['start'], len(event['attendees']))
```

**Notes**

- The SDK does not retry it, because a second call makes a second event and invites everybody again.
- A key that may not send as `from` is refused with 403 `from_address_forbidden`, and an event with attendees and no `from` with 422 `invalid_parameter`. Invitations without `emails:send` are a 403 `insufficient_scope`, which `is_scope_missing` on the error reports.

Also available in: API [`POST /calendar/events`](https://nextmail.sh/docs/api/reference/calendar#post-calendar-events); TypeScript [`nextmail.calendar.createEvent()`](https://nextmail.sh/docs/sdk/reference/calendar#createEvent); Ruby [`calendar.create_event`](https://nextmail.sh/docs/ruby/reference/calendar#createEvent); PHP [`calendar->createEvent`](https://nextmail.sh/docs/php/reference/calendar#createEvent); Go [`Calendar.CreateEvent`](https://nextmail.sh/docs/go/reference/calendar#createEvent); Java [`calendar().createEvent`](https://nextmail.sh/docs/java/reference/calendar#createEvent); C# [`Calendar.CreateEventAsync`](https://nextmail.sh/docs/csharp/reference/calendar#createEvent); CLI [`nextmail calendar create-event`](https://nextmail.sh/docs/cli/reference/calendar#calendar-create-event).

### `calendar.update_event()`

Change an event

```python
def update_event(
    id: str,
    patch: CalendarEventPatch,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> CalendarEventResource
```

Changes an event you organise. A field you leave out keeps its value, and `attendees` replaces the whole list. Everyone invited gets the updated invitation by email from `from`, so a change to an event with attendees needs `emails:send` too, unless `sendInvites` is `False`.

For a repeating event, `scope` says which dates change, as the app asks when you edit one date: `occurrence` changes only the date that starts at `occurrenceStart`, `following` that date and every one after it, and `series` all of them. With `following` the later dates become a new series with its own `id`, which is what comes back, and the old one ends the day before.

Scopes: `calendar:write`.

**Parameters**

- `id` (`str`, required): Event id, the `eventId` of an occurrence, such as `cal_` followed by 24 hex characters.
- `patch['summary']` (`str`): The title, 1 to 255 characters.
- `patch['start']` (`datetime | str`): When it starts, a `datetime` or an ISO 8601 string with a zone.
- `patch['end']` (`datetime | str`): When it ends, after `start` and at most two years later.
- `patch['description']` (`str`): Notes about the event, up to 8,000 characters.
- `patch['location']` (`str`): Where it is: a room, an address or a link.
- `patch['url']` (`str`): A link that belongs to the event.
- `patch['meetingUrl']` (`str`): The link to the video call. An empty string takes it off.
- `patch['allDay']` (`bool`): Whether it fills whole days. `start` and `end` then mark the days.
- `patch['timezone']` (`str`): The IANA zone the event belongs to, `UTC` unless you say.
- `patch['recurrence']` (`str`): An RRULE such as `FREQ=WEEKLY;BYDAY=MO` when it repeats.
- `patch['attendees']` (`list[CalendarAttendeeInput]`): Up to 200 people to invite, each a dict with `email` and optionally `name` and `optional`. It replaces the whole list.
- `patch['reminders']` (`list[CalendarReminderInput]`): Up to five reminders, each a dict with `minutesBefore` and optionally `action`, one of `DISPLAY`, `EMAIL` and `AUDIO`.
- `patch['transparency']` (`CalendarTransparency`): `OPAQUE` shows you busy and `TRANSPARENT` free.
- `patch['visibility']` (`CalendarVisibility`): `PUBLIC`, `PRIVATE` or `CONFIDENTIAL`.
- `patch['sendInvites']` (`bool`): `False` keeps the change to your own calendar and sends nothing.
- `patch['from']` (`str`): The address the invitations come from. It has to be one the key may send as.
- `patch['scope']` (`CalendarEditScope`): `occurrence`, `following` or `series`: which dates of a repeating event change. Left out, the change reaches the event or date the id names.
- `patch['occurrenceStart']` (`datetime | str`): The start of the date you mean, a `datetime` or an ISO 8601 string as `list_events` gives it. Needed with `occurrence` and `following`.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`CalendarEventResource` as it stands after the change, with `sequence` one higher.

**Example**

```python
from nextmail import nextmail

event = nextmail.calendar.update_event(
    'cal_3f9a1c7e5b2d48a06c1e9f4b',
    {'start': '2026-10-12T10:00:00Z', 'end': '2026-10-12T10:30:00Z'},
)

print(event['start'], event['sequence'])
```

**Notes**

- The SDK does not retry it, because a retry after a lost response sends the update to everybody again.
- An event organised by somebody else is refused with 403 `not_organizer`: answer it with `respond_to_event` instead. An event the key does not reach is a 404.

Also available in: API [`PATCH /calendar/events/{id}`](https://nextmail.sh/docs/api/reference/calendar#patch-calendar-events-id); TypeScript [`nextmail.calendar.updateEvent()`](https://nextmail.sh/docs/sdk/reference/calendar#updateEvent); Ruby [`calendar.update_event`](https://nextmail.sh/docs/ruby/reference/calendar#updateEvent); PHP [`calendar->updateEvent`](https://nextmail.sh/docs/php/reference/calendar#updateEvent); Go [`Calendar.UpdateEvent`](https://nextmail.sh/docs/go/reference/calendar#updateEvent); Java [`calendar().updateEvent`](https://nextmail.sh/docs/java/reference/calendar#updateEvent); C# [`Calendar.UpdateEventAsync`](https://nextmail.sh/docs/csharp/reference/calendar#updateEvent); CLI [`nextmail calendar update-event`](https://nextmail.sh/docs/cli/reference/calendar#calendar-update-event).

### `calendar.delete_event()`

Remove an event from the calendar

```python
def delete_event(
    id: str,
    *,
    scope: CalendarEditScope | None = None,
    occurrence_start: datetime | str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> DeletedCalendarEventResource
```

Removes an event for good, as Remove does in the app. Nobody else is told, so call off a meeting with others in it with `cancel_event` instead. For a repeating event, `scope='occurrence'` with `occurrence_start=` removes one date, and `scope='following'` that date and every one after it.

Scopes: `calendar:write`.

**Parameters**

- `id` (`str`, required): Event id, the `eventId` of an occurrence, such as `cal_` followed by 24 hex characters.
- `scope` (`CalendarEditScope`): `occurrence`, `following` or `series`: which dates of a repeating event go.
- `occurrence_start` (`datetime | str`): The start of the date you mean, a `datetime` or an ISO 8601 string as `list_events` gives it. Needed with `occurrence` and `following`.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`DeletedCalendarEventResource`, such as `{'object': 'calendar_event', 'id': 'cal_3f9a1c7e5b2d48a06c1e9f4b', 'deleted': True}`.

**Example**

```python
from nextmail import nextmail

deleted = nextmail.calendar.delete_event('cal_3f9a1c7e5b2d48a06c1e9f4b')

print(deleted['id'], deleted['deleted'])
```

**Notes**

- An event organised by somebody else is refused with 403 `not_organizer`, and an event that is gone or that the key does not reach is a 404. An event you may not remove can still be taken off your own calendar with `hide_event`.

Also available in: API [`DELETE /calendar/events/{id}`](https://nextmail.sh/docs/api/reference/calendar#delete-calendar-events-id); TypeScript [`nextmail.calendar.deleteEvent()`](https://nextmail.sh/docs/sdk/reference/calendar#deleteEvent); Ruby [`calendar.delete_event`](https://nextmail.sh/docs/ruby/reference/calendar#deleteEvent); PHP [`calendar->deleteEvent`](https://nextmail.sh/docs/php/reference/calendar#deleteEvent); Go [`Calendar.DeleteEvent`](https://nextmail.sh/docs/go/reference/calendar#deleteEvent); Java [`calendar().deleteEvent`](https://nextmail.sh/docs/java/reference/calendar#deleteEvent); C# [`Calendar.DeleteEventAsync`](https://nextmail.sh/docs/csharp/reference/calendar#deleteEvent); CLI [`nextmail calendar delete-event`](https://nextmail.sh/docs/cli/reference/calendar#calendar-delete-event).

### `calendar.cancel_event()`

Call off a meeting

```python
def cancel_event(
    id: str,
    body: CalendarEventCancel | None = None,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> CalendarEventResource
```

Cancels a meeting you organise, as Cancel meeting does in the app. It stays on the calendar with `status` `CANCELLED`, and everyone invited is told it is off by email from `from`, so a meeting with attendees also needs `emails:send`. `body` is optional.

For a repeating meeting, `scope` `occurrence` with `occurrenceStart` calls off one date, and `following` that date and every one after it. Guests get the matching update.

Scopes: `calendar:write`.

**Parameters**

- `id` (`str`, required): Event id, the `eventId` of an occurrence, such as `cal_` followed by 24 hex characters.
- `body['from']` (`str`): The address the cancellation comes from. It has to be one the key may send as.
- `body['scope']` (`CalendarEditScope`): `occurrence`, `following` or `series`: which dates of a repeating meeting are called off.
- `body['occurrenceStart']` (`datetime | str`): The start of the date you mean, a `datetime` or an ISO 8601 string as `list_events` gives it. Needed with `occurrence` and `following`.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`CalendarEventResource` with `status` `CANCELLED`.

**Example**

```python
from nextmail import nextmail

event = nextmail.calendar.cancel_event(
    'cal_3f9a1c7e5b2d48a06c1e9f4b', {'from': 'team@acme.com'}
)

print(event['summary'], event['status'])
```

**Notes**

- The SDK does not retry it, because a retry sends the cancellation again.
- An event organised by somebody else is refused with 403 `not_organizer`.

Also available in: API [`POST /calendar/events/{id}/cancel`](https://nextmail.sh/docs/api/reference/calendar#post-calendar-events-id-cancel); TypeScript [`nextmail.calendar.cancelEvent()`](https://nextmail.sh/docs/sdk/reference/calendar#cancelEvent); Ruby [`calendar.cancel_event`](https://nextmail.sh/docs/ruby/reference/calendar#cancelEvent); PHP [`calendar->cancelEvent`](https://nextmail.sh/docs/php/reference/calendar#cancelEvent); Go [`Calendar.CancelEvent`](https://nextmail.sh/docs/go/reference/calendar#cancelEvent); Java [`calendar().cancelEvent`](https://nextmail.sh/docs/java/reference/calendar#cancelEvent); C# [`Calendar.CancelEventAsync`](https://nextmail.sh/docs/csharp/reference/calendar#cancelEvent); CLI [`nextmail calendar cancel-event`](https://nextmail.sh/docs/cli/reference/calendar#calendar-cancel-event).

### `calendar.respond_to_event()`

Answer an invitation

```python
def respond_to_event(
    id: str,
    body: CalendarEventAnswer,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> CalendarEventResource
```

Accepts, declines or tentatively accepts an invitation, as the buttons of an invitation do in the app. The answer goes to the organiser by email from the invited address the key may send as, or from `respondingAs` when the invitation went to more than one of yours, so it needs `emails:send` as well.

Scopes: `calendar:write`, `emails:send`.

**Parameters**

- `id` (`str`, required): Event id, the `eventId` of an occurrence, such as `cal_` followed by 24 hex characters.
- `body['response']` (`CalendarResponse`, required): `ACCEPTED`, `DECLINED` or `TENTATIVE`.
- `body['respondingAs']` (`str`): Which of your invited addresses answers, when there is more than one.
- `body['proposedStart']` (`datetime | str`): A new start to propose with your answer, a `datetime` or an ISO 8601 string, sent together with `proposedEnd`.
- `body['proposedEnd']` (`datetime | str`): The end of the proposed time.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`CalendarEventResource` with your answer on your attendee row.

**Example**

```python
from nextmail import nextmail

event = nextmail.calendar.respond_to_event(
    'cal_3f9a1c7e5b2d48a06c1e9f4b', {'response': 'ACCEPTED'}
)

for attendee in event['attendees']:
    print(attendee['email'], attendee['partstat'])
```

**Notes**

- An event that invited none of the addresses the key may send as is refused with 403 `not_invited`, and a cancelled meeting with 409 `event_cancelled`.
- When the organiser cannot be reached, nothing changes and the call is a 409 `reply_not_sent`.
- With `proposedStart` and `proposedEnd` the organiser gets a counter-proposal, as Propose a new time sends from Google Calendar and Outlook, and decides whether to move the meeting.

Also available in: API [`POST /calendar/events/{id}/respond`](https://nextmail.sh/docs/api/reference/calendar#post-calendar-events-id-respond); TypeScript [`nextmail.calendar.respondToEvent()`](https://nextmail.sh/docs/sdk/reference/calendar#respondToEvent); Ruby [`calendar.respond_to_event`](https://nextmail.sh/docs/ruby/reference/calendar#respondToEvent); PHP [`calendar->respondToEvent`](https://nextmail.sh/docs/php/reference/calendar#respondToEvent); Go [`Calendar.RespondToEvent`](https://nextmail.sh/docs/go/reference/calendar#respondToEvent); Java [`calendar().respondToEvent`](https://nextmail.sh/docs/java/reference/calendar#respondToEvent); C# [`Calendar.RespondToEventAsync`](https://nextmail.sh/docs/csharp/reference/calendar#respondToEvent); CLI [`nextmail calendar respond-to-event`](https://nextmail.sh/docs/cli/reference/calendar#calendar-respond-to-event).

### `calendar.decline_proposal()`

Decline a proposed time

```python
def decline_proposal(
    id: str,
    body: CalendarProposalDecline,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> CalendarEventResource
```

Keeps the original time of a meeting you organise after a guest proposed another one, as Keep the original does on the event in the app. The guest is told by email from `from`, or from the organising address, so it needs `emails:send` as well. To take the proposed time instead, change the event with `update_event`.

Scopes: `calendar:write`, `emails:send`.

**Parameters**

- `id` (`str`, required): Event id, the `eventId` of an occurrence, such as `cal_` followed by 24 hex characters.
- `body['attendee']` (`str`, required): The guest whose proposal you decline, by email address.
- `body['from']` (`str`): The address the answer is sent from, when it is not the organising address.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`CalendarEventResource` with the guest's `proposedStart` and `proposedEnd` cleared.

**Example**

```python
from nextmail import nextmail

event = nextmail.calendar.decline_proposal(
    'cal_3f9a1c7e5b2d48a06c1e9f4b', {'attendee': 'ada@example.com'}
)

print(event['summary'], 'keeps its time')
```

**Notes**

- A guest who has not proposed a new time is refused with 404 `proposal_not_found`, and an event somebody else organises with 403 `not_organizer`.

Also available in: API [`POST /calendar/events/{id}/decline-proposal`](https://nextmail.sh/docs/api/reference/calendar#post-calendar-events-id-decline-proposal); TypeScript [`nextmail.calendar.declineProposal()`](https://nextmail.sh/docs/sdk/reference/calendar#declineProposal); Ruby [`calendar.decline_proposal`](https://nextmail.sh/docs/ruby/reference/calendar#declineProposal); PHP [`calendar->declineProposal`](https://nextmail.sh/docs/php/reference/calendar#declineProposal); Go [`Calendar.DeclineProposal`](https://nextmail.sh/docs/go/reference/calendar#declineProposal); Java [`calendar().declineProposal`](https://nextmail.sh/docs/java/reference/calendar#declineProposal); C# [`Calendar.DeclineProposalAsync`](https://nextmail.sh/docs/csharp/reference/calendar#declineProposal); CLI [`nextmail calendar decline-proposal`](https://nextmail.sh/docs/cli/reference/calendar#calendar-decline-proposal).

### `calendar.hide_event()`

Hide an event from your calendar

```python
def hide_event(
    id: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> CalendarEventHiddenResource
```

Takes an event off your own calendar and leaves it for everyone else, as Hide from my calendar does in the app. Use it for an invitation that reached you through a shared address, or an event you may not remove. It hides every date of a repeating event, and stops its reminders for you. `unhide_event` brings it back.

Scopes: `calendar:write`.

**Parameters**

- `id` (`str`, required): Event id, the `eventId` of an occurrence, such as `cal_` followed by 24 hex characters.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`CalendarEventHiddenResource`, such as `{'object': 'calendar_event', 'id': 'cal_3f9a1c7e5b2d48a06c1e9f4b', 'uid': '...', 'hidden': True}`.

**Example**

```python
from nextmail import nextmail

hidden = nextmail.calendar.hide_event('cal_3f9a1c7e5b2d48a06c1e9f4b')

print(hidden['id'], hidden['hidden'])
```

**Notes**

- Hiding an event that is already hidden changes nothing, so it is safe to repeat. An event the key does not reach is a 404.
- It hides the event for the person the key or app acts for, and nobody else. `list_events` leaves it out from then on, and `get_event` still finds it, with `hidden` `True`.

Also available in: API [`POST /calendar/events/{id}/hide`](https://nextmail.sh/docs/api/reference/calendar#post-calendar-events-id-hide); TypeScript [`nextmail.calendar.hideEvent()`](https://nextmail.sh/docs/sdk/reference/calendar#hideEvent); Ruby [`calendar.hide_event`](https://nextmail.sh/docs/ruby/reference/calendar#hideEvent); PHP [`calendar->hideEvent`](https://nextmail.sh/docs/php/reference/calendar#hideEvent); Go [`Calendar.HideEvent`](https://nextmail.sh/docs/go/reference/calendar#hideEvent); Java [`calendar().hideEvent`](https://nextmail.sh/docs/java/reference/calendar#hideEvent); C# [`Calendar.HideEventAsync`](https://nextmail.sh/docs/csharp/reference/calendar#hideEvent); CLI [`nextmail calendar hide-event`](https://nextmail.sh/docs/cli/reference/calendar#calendar-hide-event).

### `calendar.unhide_event()`

Show a hidden event again

```python
def unhide_event(
    id: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> CalendarEventHiddenResource
```

Puts an event you hid back on your calendar, as Show on my calendar does on the invitation in the app.

Scopes: `calendar:write`.

**Parameters**

- `id` (`str`, required): Event id, the `eventId` of an occurrence, such as `cal_` followed by 24 hex characters.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`CalendarEventHiddenResource` with `hidden` `False`.

**Example**

```python
from nextmail import nextmail

shown = nextmail.calendar.unhide_event('cal_3f9a1c7e5b2d48a06c1e9f4b')

print(shown['id'], shown['hidden'])
```

**Notes**

- An event that was not hidden stays as it was, so it is safe to repeat.

Also available in: API [`POST /calendar/events/{id}/unhide`](https://nextmail.sh/docs/api/reference/calendar#post-calendar-events-id-unhide); TypeScript [`nextmail.calendar.unhideEvent()`](https://nextmail.sh/docs/sdk/reference/calendar#unhideEvent); Ruby [`calendar.unhide_event`](https://nextmail.sh/docs/ruby/reference/calendar#unhideEvent); PHP [`calendar->unhideEvent`](https://nextmail.sh/docs/php/reference/calendar#unhideEvent); Go [`Calendar.UnhideEvent`](https://nextmail.sh/docs/go/reference/calendar#unhideEvent); Java [`calendar().unhideEvent`](https://nextmail.sh/docs/java/reference/calendar#unhideEvent); C# [`Calendar.UnhideEventAsync`](https://nextmail.sh/docs/csharp/reference/calendar#unhideEvent); CLI [`nextmail calendar unhide-event`](https://nextmail.sh/docs/cli/reference/calendar#calendar-unhide-event).

### `calendar.free_busy()`

Free and busy times

```python
def free_busy(
    *,
    emails: Sequence[str],
    from_: datetime | str,
    to: datetime | str,
    timezone: str | None = None,
    duration_minutes: int | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> CalendarFreeBusyResource
```

When the people you name are busy, as Find a time in the event editor of the app shows it. An address of the workspace shows the busy times of the person who holds it, from their events, the invitations they have not declined, their subscribed calendars and their bookings, without any detail. Any other address comes back with `known` `False`.

With `duration_minutes=`, `suggestions` lists up to five times when everyone known is free, on weekdays between 09:00 and 17:00 in `timezone=`. `from_=` and `to=` take a `datetime` or an ISO 8601 string and may be at most 31 days apart.

Scopes: `calendar:read`.

**Parameters**

- `emails` (`Sequence[str]`, required): The addresses to look up, at most 20.
- `from_` (`datetime | str`, required): The start of the window, a `datetime` or an ISO 8601 string, sent as the `from` query parameter.
- `to` (`datetime | str`, required): The end of the window, at most 31 days after `from_=`.
- `timezone` (`str`): The IANA zone the suggested times keep to working hours in, `UTC` unless you say.
- `duration_minutes` (`int`): The length of the meeting, 5 to 480 minutes. Send it to get `suggestions`.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`CalendarFreeBusyResource` with `people`, each a dict with `email`, `known` and `busy`, its busy times as dicts with `start` and `end`, and `suggestions` in the same shape.

**Example**

```python
from datetime import datetime, timedelta, timezone

from nextmail import nextmail

start = datetime.now(timezone.utc)
times = nextmail.calendar.free_busy(
    emails=['ana@acme.com', 'ben@acme.com'],
    from_=start,
    to=start + timedelta(days=7),
    timezone='Europe/London',
    duration_minutes=30,
)

for person in times['people']:
    print(person['email'], len(person['busy']), 'busy blocks')

for suggestion in times['suggestions']:
    print('Free:', suggestion['start'])
```

**Notes**

- A key or an app limited to particular addresses or domains is refused with 422 `capability_unsupported`, because the answer reads whole calendars.
- It reads the calendars as they are now and holds nothing, so a suggested time can be gone a moment later.

Also available in: API [`GET /calendar/free-busy`](https://nextmail.sh/docs/api/reference/calendar#get-calendar-free-busy); TypeScript [`nextmail.calendar.freeBusy()`](https://nextmail.sh/docs/sdk/reference/calendar#freeBusy); Ruby [`calendar.free_busy`](https://nextmail.sh/docs/ruby/reference/calendar#freeBusy); PHP [`calendar->freeBusy`](https://nextmail.sh/docs/php/reference/calendar#freeBusy); Go [`Calendar.FreeBusy`](https://nextmail.sh/docs/go/reference/calendar#freeBusy); Java [`calendar().freeBusy`](https://nextmail.sh/docs/java/reference/calendar#freeBusy); C# [`Calendar.FreeBusyAsync`](https://nextmail.sh/docs/csharp/reference/calendar#freeBusy); CLI [`nextmail calendar free-busy`](https://nextmail.sh/docs/cli/reference/calendar#calendar-free-busy).

### `calendar.list_feeds()`

List calendar links

```python
def list_feeds(
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> builtins.list[CalendarFeedResource]
```

Returns the secret links that publish your calendar to other calendar apps, as the Share tab of the calendar settings in the app lists them. Anyone with a link can read what it shows, so treat `url` and `webcalUrl` like passwords.

Scopes: `calendar:read`.

**Parameters**

- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`list[CalendarFeedResource]`, each with `id`, `name`, `mode`, `url`, `webcalUrl` and when calendar apps last read it.

**Example**

```python
from nextmail import nextmail

for feed in nextmail.calendar.list_feeds():
    print(feed['name'], feed['mode'], feed['url'])
```

**Notes**

- A key limited to particular addresses or domains is refused with 422 `capability_unsupported`, because a link covers the whole calendar.

Also available in: API [`GET /calendar/feeds`](https://nextmail.sh/docs/api/reference/calendar#get-calendar-feeds); TypeScript [`nextmail.calendar.listFeeds()`](https://nextmail.sh/docs/sdk/reference/calendar#listFeeds); Ruby [`calendar.list_feeds`](https://nextmail.sh/docs/ruby/reference/calendar#listFeeds); PHP [`calendar->listFeeds`](https://nextmail.sh/docs/php/reference/calendar#listFeeds); Go [`Calendar.ListFeeds`](https://nextmail.sh/docs/go/reference/calendar#listFeeds); Java [`calendar().listFeeds`](https://nextmail.sh/docs/java/reference/calendar#listFeeds); C# [`Calendar.ListFeedsAsync`](https://nextmail.sh/docs/csharp/reference/calendar#listFeeds); CLI [`nextmail calendar list-feeds`](https://nextmail.sh/docs/cli/reference/calendar#calendar-list-feeds).

### `calendar.create_feed()`

Create a calendar link

```python
def create_feed(
    body: CalendarFeedCreate,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> CalendarFeedResource
```

Makes a secret link that publishes your calendar as an iCalendar feed, for Google Calendar (From URL), Outlook (Subscribe from web) or Apple Calendar (New subscription). `full` shows every detail, `busy` only the times you are busy. The calendar app reads the link again on its own schedule, usually every few hours.

Scopes: `calendar:write`.

**Parameters**

- `body['name']` (`str`, required): The name calendar apps show for the calendar, up to 100 characters.
- `body['mode']` (`CalendarFeedMode`, required): `full` for every detail, `busy` for busy times only.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`CalendarFeedResource` with the new `url` and `webcalUrl`.

**Example**

```python
from nextmail import nextmail

feed = nextmail.calendar.create_feed({'name': 'Work (busy)', 'mode': 'busy'})

print('Paste into Google Calendar:', feed['url'])
```

**Notes**

- One person may keep 10 links. One more is refused with 409 `calendar_feed_limit`.

Also available in: API [`POST /calendar/feeds`](https://nextmail.sh/docs/api/reference/calendar#post-calendar-feeds); TypeScript [`nextmail.calendar.createFeed()`](https://nextmail.sh/docs/sdk/reference/calendar#createFeed); Ruby [`calendar.create_feed`](https://nextmail.sh/docs/ruby/reference/calendar#createFeed); PHP [`calendar->createFeed`](https://nextmail.sh/docs/php/reference/calendar#createFeed); Go [`Calendar.CreateFeed`](https://nextmail.sh/docs/go/reference/calendar#createFeed); Java [`calendar().createFeed`](https://nextmail.sh/docs/java/reference/calendar#createFeed); C# [`Calendar.CreateFeedAsync`](https://nextmail.sh/docs/csharp/reference/calendar#createFeed); CLI [`nextmail calendar create-feed`](https://nextmail.sh/docs/cli/reference/calendar#calendar-create-feed).

### `calendar.reset_feed()`

Reset a calendar link

```python
def reset_feed(
    id: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> CalendarFeedResource
```

Replaces the secret in a calendar link, so the old address stops working everywhere it was shared. Paste the new address into the calendar apps you still want to update.

Scopes: `calendar:write`.

**Parameters**

- `id` (`str`, required): Calendar link id, `cfd_` followed by 24 hex characters.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`CalendarFeedResource` with its new `url` and `webcalUrl`.

**Example**

```python
from nextmail import nextmail

feed = nextmail.calendar.reset_feed('cfd_3f9a1c7e5b2d48a06c1e9f4b')

print(feed['url'])
```

**Notes**

- A link that is not yours is a 404 `calendar_feed_not_found`.

Also available in: API [`POST /calendar/feeds/{id}/reset`](https://nextmail.sh/docs/api/reference/calendar#post-calendar-feeds-id-reset); TypeScript [`nextmail.calendar.resetFeed()`](https://nextmail.sh/docs/sdk/reference/calendar#resetFeed); Ruby [`calendar.reset_feed`](https://nextmail.sh/docs/ruby/reference/calendar#resetFeed); PHP [`calendar->resetFeed`](https://nextmail.sh/docs/php/reference/calendar#resetFeed); Go [`Calendar.ResetFeed`](https://nextmail.sh/docs/go/reference/calendar#resetFeed); Java [`calendar().resetFeed`](https://nextmail.sh/docs/java/reference/calendar#resetFeed); C# [`Calendar.ResetFeedAsync`](https://nextmail.sh/docs/csharp/reference/calendar#resetFeed); CLI [`nextmail calendar reset-feed`](https://nextmail.sh/docs/cli/reference/calendar#calendar-reset-feed).

### `calendar.delete_feed()`

Delete a calendar link

```python
def delete_feed(
    id: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> DeletedCalendarFeedResource
```

Turns a calendar link off for good. Calendar apps that subscribed to it stop getting updates.

Scopes: `calendar:write`.

**Parameters**

- `id` (`str`, required): Calendar link id, `cfd_` followed by 24 hex characters.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`DeletedCalendarFeedResource` with `deleted` set to `True`.

**Example**

```python
from nextmail import nextmail

deleted = nextmail.calendar.delete_feed('cfd_3f9a1c7e5b2d48a06c1e9f4b')

print(deleted['id'], deleted['deleted'])
```

**Notes**

- A link that is not yours is a 404 `calendar_feed_not_found`.

Also available in: API [`DELETE /calendar/feeds/{id}`](https://nextmail.sh/docs/api/reference/calendar#delete-calendar-feeds-id); TypeScript [`nextmail.calendar.deleteFeed()`](https://nextmail.sh/docs/sdk/reference/calendar#deleteFeed); Ruby [`calendar.delete_feed`](https://nextmail.sh/docs/ruby/reference/calendar#deleteFeed); PHP [`calendar->deleteFeed`](https://nextmail.sh/docs/php/reference/calendar#deleteFeed); Go [`Calendar.DeleteFeed`](https://nextmail.sh/docs/go/reference/calendar#deleteFeed); Java [`calendar().deleteFeed`](https://nextmail.sh/docs/java/reference/calendar#deleteFeed); C# [`Calendar.DeleteFeedAsync`](https://nextmail.sh/docs/csharp/reference/calendar#deleteFeed); CLI [`nextmail calendar delete-feed`](https://nextmail.sh/docs/cli/reference/calendar#calendar-delete-feed).

### `calendar.list_subscriptions()`

List calendar subscriptions

```python
def list_subscriptions(
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> builtins.list[CalendarSubscriptionResource]
```

Returns the other calendars you subscribe to by link, such as a Google or Outlook calendar address, a team rota or public holidays, as Other calendars in the app lists them. Their events show on your calendar, read-only.

Scopes: `calendar:read`.

**Parameters**

- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`list[CalendarSubscriptionResource]`, each with `name`, `url`, `color`, `status`, `lastError` and how many events it holds.

**Example**

```python
from nextmail import nextmail

for subscription in nextmail.calendar.list_subscriptions():
    print(subscription['name'], subscription['status'], subscription['eventCount'])
```

**Notes**

- `status` is `failing` while reads fail and are retried, and `stopped` after ten failures in a row until you refresh it.

Also available in: API [`GET /calendar/subscriptions`](https://nextmail.sh/docs/api/reference/calendar#get-calendar-subscriptions); TypeScript [`nextmail.calendar.listSubscriptions()`](https://nextmail.sh/docs/sdk/reference/calendar#listSubscriptions); Ruby [`calendar.list_subscriptions`](https://nextmail.sh/docs/ruby/reference/calendar#listSubscriptions); PHP [`calendar->listSubscriptions`](https://nextmail.sh/docs/php/reference/calendar#listSubscriptions); Go [`Calendar.ListSubscriptions`](https://nextmail.sh/docs/go/reference/calendar#listSubscriptions); Java [`calendar().listSubscriptions`](https://nextmail.sh/docs/java/reference/calendar#listSubscriptions); C# [`Calendar.ListSubscriptionsAsync`](https://nextmail.sh/docs/csharp/reference/calendar#listSubscriptions); CLI [`nextmail calendar list-subscriptions`](https://nextmail.sh/docs/cli/reference/calendar#calendar-list-subscriptions).

### `calendar.create_subscription()`

Subscribe to a calendar

```python
def create_subscription(
    body: CalendarSubscriptionCreate,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> CalendarSubscriptionResource
```

Adds a calendar by its iCalendar link: an `https`, `http` or `webcal` address, such as the secret address of a Google calendar or a published Outlook calendar. It is read at once, then again about every half hour. `name` defaults to the name the calendar gives itself.

Scopes: `calendar:write`.

**Parameters**

- `body['url']` (`str`, required): The calendar link, `https`, `http` or `webcal`.
- `body['name']` (`str`): The name to show, up to 100 characters.
- `body['color']` (`CalendarColor`): The colour of its events, from `CALENDAR_COLORS`.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`CalendarSubscriptionResource` with its first events read.

**Example**

```python
from nextmail import nextmail

holidays = nextmail.calendar.create_subscription(
    {'url': 'webcal://example.com/holidays.ics', 'color': 'green'}
)

print(holidays['name'], holidays['eventCount'])
```

**Notes**

- An address that does not answer with a calendar is refused with 422 `calendar_subscription_unreadable`, and one you already subscribe to with 409 `calendar_subscription_exists`.
- One person may subscribe to 20 calendars.

Also available in: API [`POST /calendar/subscriptions`](https://nextmail.sh/docs/api/reference/calendar#post-calendar-subscriptions); TypeScript [`nextmail.calendar.createSubscription()`](https://nextmail.sh/docs/sdk/reference/calendar#createSubscription); Ruby [`calendar.create_subscription`](https://nextmail.sh/docs/ruby/reference/calendar#createSubscription); PHP [`calendar->createSubscription`](https://nextmail.sh/docs/php/reference/calendar#createSubscription); Go [`Calendar.CreateSubscription`](https://nextmail.sh/docs/go/reference/calendar#createSubscription); Java [`calendar().createSubscription`](https://nextmail.sh/docs/java/reference/calendar#createSubscription); C# [`Calendar.CreateSubscriptionAsync`](https://nextmail.sh/docs/csharp/reference/calendar#createSubscription); CLI [`nextmail calendar create-subscription`](https://nextmail.sh/docs/cli/reference/calendar#calendar-create-subscription).

### `calendar.update_subscription()`

Change a calendar subscription

```python
def update_subscription(
    id: str,
    patch: CalendarSubscriptionPatch,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> CalendarSubscriptionResource
```

Renames a subscription or gives it another colour.

Scopes: `calendar:write`.

**Parameters**

- `id` (`str`, required): Subscription id, `csb_` followed by 24 hex characters.
- `patch['name']` (`str`): The new name, up to 100 characters.
- `patch['color']` (`CalendarColor`): The new colour, from `CALENDAR_COLORS`.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`CalendarSubscriptionResource` as it stands now.

**Example**

```python
from nextmail import nextmail

subscription = nextmail.calendar.update_subscription(
    'csb_3f9a1c7e5b2d48a06c1e9f4b', {'name': 'Team rota', 'color': 'purple'}
)

print(subscription['name'], subscription['color'])
```

**Notes**

- A subscription that is not yours is a 404 `calendar_subscription_not_found`.

Also available in: API [`PATCH /calendar/subscriptions/{id}`](https://nextmail.sh/docs/api/reference/calendar#patch-calendar-subscriptions-id); TypeScript [`nextmail.calendar.updateSubscription()`](https://nextmail.sh/docs/sdk/reference/calendar#updateSubscription); Ruby [`calendar.update_subscription`](https://nextmail.sh/docs/ruby/reference/calendar#updateSubscription); PHP [`calendar->updateSubscription`](https://nextmail.sh/docs/php/reference/calendar#updateSubscription); Go [`Calendar.UpdateSubscription`](https://nextmail.sh/docs/go/reference/calendar#updateSubscription); Java [`calendar().updateSubscription`](https://nextmail.sh/docs/java/reference/calendar#updateSubscription); C# [`Calendar.UpdateSubscriptionAsync`](https://nextmail.sh/docs/csharp/reference/calendar#updateSubscription); CLI [`nextmail calendar update-subscription`](https://nextmail.sh/docs/cli/reference/calendar#calendar-update-subscription).

### `calendar.refresh_subscription()`

Refresh a calendar subscription

```python
def refresh_subscription(
    id: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> CalendarSubscriptionResource
```

Reads a subscribed calendar again now instead of waiting for the next half-hourly read, and restarts one that stopped after repeated failures.

Scopes: `calendar:write`.

**Parameters**

- `id` (`str`, required): Subscription id, `csb_` followed by 24 hex characters.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`CalendarSubscriptionResource` after the read, with `status` and `lastError`.

**Example**

```python
from nextmail import nextmail

subscription = nextmail.calendar.refresh_subscription('csb_3f9a1c7e5b2d48a06c1e9f4b')

if subscription['lastError']:
    print('Could not read it:', subscription['lastError'])
```

**Notes**

- A calendar read less than a minute ago is refused with 429 `calendar_subscription_refresh_too_soon`.

Also available in: API [`POST /calendar/subscriptions/{id}/refresh`](https://nextmail.sh/docs/api/reference/calendar#post-calendar-subscriptions-id-refresh); TypeScript [`nextmail.calendar.refreshSubscription()`](https://nextmail.sh/docs/sdk/reference/calendar#refreshSubscription); Ruby [`calendar.refresh_subscription`](https://nextmail.sh/docs/ruby/reference/calendar#refreshSubscription); PHP [`calendar->refreshSubscription`](https://nextmail.sh/docs/php/reference/calendar#refreshSubscription); Go [`Calendar.RefreshSubscription`](https://nextmail.sh/docs/go/reference/calendar#refreshSubscription); Java [`calendar().refreshSubscription`](https://nextmail.sh/docs/java/reference/calendar#refreshSubscription); C# [`Calendar.RefreshSubscriptionAsync`](https://nextmail.sh/docs/csharp/reference/calendar#refreshSubscription); CLI [`nextmail calendar refresh-subscription`](https://nextmail.sh/docs/cli/reference/calendar#calendar-refresh-subscription).

### `calendar.delete_subscription()`

Unsubscribe from a calendar

```python
def delete_subscription(
    id: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> DeletedCalendarSubscriptionResource
```

Removes a subscription and every event it brought onto your calendar.

Scopes: `calendar:write`.

**Parameters**

- `id` (`str`, required): Subscription id, `csb_` followed by 24 hex characters.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`DeletedCalendarSubscriptionResource` with `deleted` set to `True`.

**Example**

```python
from nextmail import nextmail

deleted = nextmail.calendar.delete_subscription('csb_3f9a1c7e5b2d48a06c1e9f4b')

print(deleted['id'], deleted['deleted'])
```

**Notes**

- A subscription that is not yours is a 404 `calendar_subscription_not_found`.

Also available in: API [`DELETE /calendar/subscriptions/{id}`](https://nextmail.sh/docs/api/reference/calendar#delete-calendar-subscriptions-id); TypeScript [`nextmail.calendar.deleteSubscription()`](https://nextmail.sh/docs/sdk/reference/calendar#deleteSubscription); Ruby [`calendar.delete_subscription`](https://nextmail.sh/docs/ruby/reference/calendar#deleteSubscription); PHP [`calendar->deleteSubscription`](https://nextmail.sh/docs/php/reference/calendar#deleteSubscription); Go [`Calendar.DeleteSubscription`](https://nextmail.sh/docs/go/reference/calendar#deleteSubscription); Java [`calendar().deleteSubscription`](https://nextmail.sh/docs/java/reference/calendar#deleteSubscription); C# [`Calendar.DeleteSubscriptionAsync`](https://nextmail.sh/docs/csharp/reference/calendar#deleteSubscription); CLI [`nextmail calendar delete-subscription`](https://nextmail.sh/docs/cli/reference/calendar#calendar-delete-subscription).

### `calendar.import_ics()`

Import a calendar file

```python
def import_ics(
    data: RawBody,
    *,
    filename: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> CalendarImportResource
```

Sends an iCalendar (.ics) file as the request body and queues it. Its events become events of your own that you can edit, and nobody on them is sent anything, now or when you change them later, unless you ask for that on the change. Poll `get_import` until `status` is `completed`.

The file may be 10 MB and hold 25,000 events. Several calendars in one file are merged, and the report keeps their names. An event whose UID is already in the workspace is skipped and counted, never overwritten, so sending the same file twice adds nothing.

`data` is `bytes`, a `bytearray` or a `memoryview`, and it goes out as `text/calendar`.

Scopes: `calendar:write`.

**Parameters**

- `data` (`RawBody`, required): The .ics file as `bytes`, a `bytearray` or a `memoryview`.
- `filename` (`str`): The name to show for the file in the list of imports, at most 255 characters.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`CalendarImportResource` with `status` set to `queued`.

**Example**

```python
from pathlib import Path

from nextmail import nextmail

queued = nextmail.calendar.import_ics(Path('work.ics').read_bytes(), filename='work.ics')

print(queued['id'], queued['status'])
```

**Notes**

- A body that is not an iCalendar file is a 400 `not_a_calendar_file`, an empty one a 400 `item_import_empty`, and one holding more than 25,000 events a 400 `too_many_events`.
- A file over 10 MB is a 413 `item_import_too_large`.
- To send text you already hold, such as what `get_event_ics` returns, encode it first with `text.encode()`.
- An upload that outlasts the client's `timeout` raises `NextmailNetworkError` with `is_timeout`, so pass a longer `timeout=` for a large file on a slow connection.
- Not retried automatically by the SDK.

Also available in: API [`POST /calendar/imports`](https://nextmail.sh/docs/api/reference/calendar#post-calendar-imports); TypeScript [`nextmail.calendar.importIcs()`](https://nextmail.sh/docs/sdk/reference/calendar#importIcs); Ruby [`calendar.import_ics`](https://nextmail.sh/docs/ruby/reference/calendar#importIcs); PHP [`calendar->importIcs`](https://nextmail.sh/docs/php/reference/calendar#importIcs); Go [`Calendar.ImportIcs`](https://nextmail.sh/docs/go/reference/calendar#importIcs); Java [`calendar().importIcs`](https://nextmail.sh/docs/java/reference/calendar#importIcs); C# [`Calendar.ImportIcsAsync`](https://nextmail.sh/docs/csharp/reference/calendar#importIcs); CLI [`nextmail calendar import-ics`](https://nextmail.sh/docs/cli/reference/calendar#calendar-import-ics).

### `calendar.list_imports()`

List calendar imports

```python
def list_imports(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> Page[CalendarImportResource]
```

Returns one page of the calendar files and calendars brought in, newest first, each with what it added and what it left out. Beside the files sent with `import_ics` it lists the calendars that came with a mailbox import, and those carry the id of that import in `parentImportId`.

Paging is keyset. `limit=` takes 1 to 100 and defaults to 25, and `nextCursor` goes back as `cursor=` while `hasMore` is `True`. `list_all_imports` and `iterate_imports` do that walk for you.

Scopes: `calendar:read`.

**Parameters**

- `limit` (`int`): Imports per page, a whole number from 1 to 100. The server defaults to 25.
- `cursor` (`str`): The `nextCursor` from the previous page. Never build one yourself.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`Page[CalendarImportResource]`, a dict with `items`, `hasMore` and `nextCursor`.

**Example**

```python
from nextmail import nextmail

page = nextmail.calendar.list_imports(limit=50)

for item in page['items']:
    counts = item['counts']
    print(item['fileName'], item['status'], counts['imported'], counts['skipped'])
```

**Notes**

- `source` is `file` for an upload. `takeout`, `caldav` and `microsoft` came with a mailbox import.
- A cursor that names no import is a 400 `invalid_cursor`.
- A GET is retried automatically on network failure and on 408, 500, 502, 503 and 504 responses, up to the client's `max_retries`, and on a 429 only when it carries a `Retry-After` of a minute or less.

Also available in: API [`GET /calendar/imports`](https://nextmail.sh/docs/api/reference/calendar#get-calendar-imports); TypeScript [`nextmail.calendar.listImports()`](https://nextmail.sh/docs/sdk/reference/calendar#listImports); Ruby [`calendar.list_imports`](https://nextmail.sh/docs/ruby/reference/calendar#listImports); PHP [`calendar->listImports`](https://nextmail.sh/docs/php/reference/calendar#listImports); Go [`Calendar.ListImports`](https://nextmail.sh/docs/go/reference/calendar#listImports); Java [`calendar().listImports`](https://nextmail.sh/docs/java/reference/calendar#listImports); C# [`Calendar.ListImportsAsync`](https://nextmail.sh/docs/csharp/reference/calendar#listImports); CLI [`nextmail calendar list-imports`](https://nextmail.sh/docs/cli/reference/calendar#calendar-list-imports).

### `calendar.list_all_imports()`

Collect every calendar import into one list

```python
def list_all_imports(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> builtins.list[CalendarImportResource]
```

Follows `nextCursor` from page to page and returns every calendar import in one list, newest first.

Everything is held in memory before the call returns. Prefer `iterate_imports` when you can stop early. `limit=` sets the page size of each request, not the total.

Scopes: `calendar:read`.

**Parameters**

- `limit` (`int`): Page size per request, from 1 to 100, defaulting to 25 on the server.
- `cursor` (`str`): A cursor from an earlier page to start after.
- `api_key` (`str`): Overrides the client's API key for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`list[CalendarImportResource]` holding every import across all pages.

**Example**

```python
from nextmail import nextmail

imports = nextmail.calendar.list_all_imports()
added = sum(item['counts']['imported'] for item in imports)

print(f'{added} events came from {len(imports)} imports')
```

**Notes**

- A failure on any page raises out of the whole call, and the imports already fetched are discarded.

Also available in: API [`GET /calendar/imports`](https://nextmail.sh/docs/api/reference/calendar#get-calendar-imports); TypeScript [`nextmail.calendar.listAllImports()`](https://nextmail.sh/docs/sdk/reference/calendar#listAllImports); Ruby [`calendar.list_all_imports`](https://nextmail.sh/docs/ruby/reference/calendar#listAllImports); PHP [`calendar->listAllImports`](https://nextmail.sh/docs/php/reference/calendar#listAllImports); Go [`Calendar.ListAllImports`](https://nextmail.sh/docs/go/reference/calendar#listAllImports); Java [`calendar().listAllImports`](https://nextmail.sh/docs/java/reference/calendar#listAllImports); C# [`Calendar.ListAllImportsAsync`](https://nextmail.sh/docs/csharp/reference/calendar#listAllImports).

### `calendar.iterate_imports()`

Stream calendar imports one at a time

```python
def iterate_imports(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    api_key: str | None = None,
    timeout: float | None = None,
) -> Iterator[CalendarImportResource]
```

Returns a generator that yields calendar imports one at a time, newest first, and requests the next page only once the current one is used up. Nothing is fetched until the loop starts, and leaving the loop with `break` stops the requests.

Scopes: `calendar:read`.

**Parameters**

- `limit` (`int`): Page size per request, from 1 to 100, defaulting to 25 on the server.
- `cursor` (`str`): A cursor from an earlier page to start after.
- `api_key` (`str`): Overrides the client's API key for every page of this walk.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`Iterator[CalendarImportResource]`, a generator yielding one import per step.

**Example**

```python
from nextmail import nextmail

for item in nextmail.calendar.iterate_imports():
    if item['status'] != 'running':
        continue

    print('still running', item['id'], item['counts']['total'])
    break
```

**Notes**

- The generator is lazy, so an abandoned loop costs only the pages you consumed.

Also available in: API [`GET /calendar/imports`](https://nextmail.sh/docs/api/reference/calendar#get-calendar-imports); TypeScript [`nextmail.calendar.iterateImports()`](https://nextmail.sh/docs/sdk/reference/calendar#iterateImports); Ruby [`calendar.iterate_imports`](https://nextmail.sh/docs/ruby/reference/calendar#iterateImports); PHP [`calendar->iterateImports`](https://nextmail.sh/docs/php/reference/calendar#iterateImports); Go [`Calendar.IterateImports`](https://nextmail.sh/docs/go/reference/calendar#iterateImports); Java [`calendar().iterateImports`](https://nextmail.sh/docs/java/reference/calendar#iterateImports); C# [`Calendar.IterateImportsAsync`](https://nextmail.sh/docs/csharp/reference/calendar#iterateImports).

### `calendar.get_import()`

Get a calendar import

```python
def get_import(
    id: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> CalendarImportResource
```

Returns one calendar import with its counts and its report: the calendars it read, how many events it left out and why, and up to 20 of the events it left out. Poll it after `import_ics` until `status` is `completed` or `failed`.

`counts['imported']` is what was added and `counts['skipped']` what was left out, which `report['skipped']` breaks down by reason: `uid-taken` for an event that was already here, `no-start` for one with no start time, `invalid` for one that could not be read and `empty` for a file or calendar that held no events.

Scopes: `calendar:read`.

**Parameters**

- `id` (`str`, required): Calendar import id, `iimp_` followed by 24 hex characters.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`CalendarImportResource` with `status`, `counts`, `report`, `undoable` and when it started, finished and was undone.

**Example**

```python
from nextmail import nextmail

item = nextmail.calendar.get_import('iimp_3f9c2a7b1e4d8f60a5c7b92d')
counts = item['counts']
already_here = item['report']['skipped'].get('uid-taken', 0)

print(item['status'], counts['imported'], counts['skipped'], already_here)

for sample in item['report']['samples']:
    print(sample['name'], sample['reason'])
```

**Notes**

- An id that names no calendar import is a 404 `item_import_not_found`, which raises `NextmailApiError` with `is_not_found`.
- A GET is retried automatically on network failure and on 408, 500, 502, 503 and 504 responses, up to the client's `max_retries`, and on a 429 only when it carries a `Retry-After` of a minute or less.

Also available in: API [`GET /calendar/imports/{id}`](https://nextmail.sh/docs/api/reference/calendar#get-calendar-imports-id); TypeScript [`nextmail.calendar.getImport()`](https://nextmail.sh/docs/sdk/reference/calendar#getImport); Ruby [`calendar.get_import`](https://nextmail.sh/docs/ruby/reference/calendar#getImport); PHP [`calendar->getImport`](https://nextmail.sh/docs/php/reference/calendar#getImport); Go [`Calendar.GetImport`](https://nextmail.sh/docs/go/reference/calendar#getImport); Java [`calendar().getImport`](https://nextmail.sh/docs/java/reference/calendar#getImport); C# [`Calendar.GetImportAsync`](https://nextmail.sh/docs/csharp/reference/calendar#getImport); CLI [`nextmail calendar get-import`](https://nextmail.sh/docs/cli/reference/calendar#calendar-get-import).

### `calendar.undo_import()`

Remove what a calendar import added

```python
def undo_import(
    id: str,
    *,
    api_key: str | None = None,
    timeout: float | None = None,
) -> CalendarImportResource
```

Deletes every event the import added, changed since or not, and marks the import `undone`. Nobody is told. Events that were skipped because they were already here are not touched.

Scopes: `calendar:write`.

**Parameters**

- `id` (`str`, required): Calendar import id, `iimp_` followed by 24 hex characters.
- `api_key` (`str`): Overrides the client's API key for this call only.
- `timeout` (`float`): Seconds this call may take, the response included, before it raises `NextmailNetworkError` with `is_timeout`. It overrides the client's `timeout` for this call, and `0` turns the limit off.

**Returns**

`CalendarImportResource` with `status` set to `undone` and `undoneAt` set.

**Example**

```python
from nextmail import nextmail

undone = nextmail.calendar.undo_import('iimp_3f9c2a7b1e4d8f60a5c7b92d')

print(undone['status'], undone['undoneAt'], undone['counts']['imported'])
```

**Notes**

- An import that is still running, or was already removed, is a 409 `item_import_not_undoable`. `undoable` on the import says whether the call would remove anything.
- An id that names no calendar import is a 404 `item_import_not_found`.
- Not retried automatically by the SDK.

Also available in: API [`POST /calendar/imports/{id}/undo`](https://nextmail.sh/docs/api/reference/calendar#post-calendar-imports-id-undo); TypeScript [`nextmail.calendar.undoImport()`](https://nextmail.sh/docs/sdk/reference/calendar#undoImport); Ruby [`calendar.undo_import`](https://nextmail.sh/docs/ruby/reference/calendar#undoImport); PHP [`calendar->undoImport`](https://nextmail.sh/docs/php/reference/calendar#undoImport); Go [`Calendar.UndoImport`](https://nextmail.sh/docs/go/reference/calendar#undoImport); Java [`calendar().undoImport`](https://nextmail.sh/docs/java/reference/calendar#undoImport); C# [`Calendar.UndoImportAsync`](https://nextmail.sh/docs/csharp/reference/calendar#undoImport); CLI [`nextmail calendar undo-import`](https://nextmail.sh/docs/cli/reference/calendar#calendar-undo-import).
