async_client: Async JMAP calendar client#

Asynchronous JMAP client.

Mirrors JMAPClient with all public methods as coroutines. Uses niquests’ AsyncSession for HTTP: the one part of calendaring-jmap with no fallback to another HTTP library. See calendaring_jmap._http.

All response-parsing logic lives in _JMAPClientBase (client.py); each method here is a ~3-line async wrapper: get session, send request, delegate to parser.

class calendaring_jmap.async_client.AsyncJMAPClient(url: str, username: str | None = None, password: str | None = None, auth=None, auth_type: str | None = None, timeout: int = 30)[source]#

Bases: _JMAPClientBase

Asynchronous JMAP client for calendar operations.

The JMAP support is experimental, the API may change in minor-releases

Usage:

from calendaring_jmap import get_async_jmap_client
async with get_async_jmap_client(url="https://jmap.example.com/.well-known/jmap",
                                  username="alice", password="secret") as client:
    calendars = await client.get_calendars()
Parameters:
  • url – URL of the JMAP session endpoint (/.well-known/jmap).

  • username – Username for Basic auth.

  • password – Password for Basic auth, or bearer token if no username.

  • auth – A pre-built niquests-compatible auth object. Takes precedence over username/password if provided.

  • auth_type – Force a specific auth type: "basic" or "bearer".

  • timeout – HTTP request timeout in seconds.

Unless a method’s own docstring says otherwise, its account_id parameter is the JMAP account to operate on, defaulting to the authenticated user’s own primary account; pass a different account (typically one shared with you via share_calendar) to operate on that account’s data instead.

async accept_invitation(event_id: str, own_email: str, account_id: str | None = None) → None[source]#

Accept a meeting invitation, notifying the organizer.

See accept_invitation() for the full semantics.

async aclose() → None[source]#

Release the persistent HTTP session and its connection pool.

Only needed when the client was not used as an async context manager: the documented Quick Start builds one directly. Idempotent; the session is recreated on the next request.

async attach_to_event(event_id: str, blob_id: str, name: str, content_type: str, account_id: str | None = None) → None[source]#

Attach an uploaded blob to a calendar event.

See attach_to_event() for the full semantics.

async confirm_push_verification(subscription_id: str, verification_code: str) → None[source]#

Submit the verification code the server POSTed to callback_url.

See confirm_push_verification() for the full semantics.

async create_calendar(name: str, color: str | None = None, timezone: str | None = None) → str[source]#

Create a calendar.

Returns:

The server-assigned JMAP calendar ID.

Raises:

JMAPMethodError – If the server rejects the create request.

async create_event(calendar_id: str, ical_str: str, account_id: str | None = None, if_in_state: str | None = None) → str[source]#

Create a calendar event from an iCalendar string.

Parameters:
  • calendar_id – The JMAP calendar ID to create the event in.

  • ical_str – A VCALENDAR string representing the event.

  • account_id – The account owning calendar_id; pass the owner’s account here to add an event to a calendar shared with you (see get_calendars(account_id=...)).

  • if_in_state – A state string from a prior call that read this account’s calendar state. If the account has changed since, the server rejects the create with JMAPMethodError (error_type == "stateMismatch") instead of applying it (RFC 8620 Section 5.3). The state covers every event in the account, not just this one. None applies unconditionally.

Returns:

The server-assigned JMAP event ID.

Raises:
  • JMAPMethodError – If the server rejects the create request.

  • ValueError – If ical_str is malformed in a way ical_to_jscal rejects (missing UID/DTSTART, a negative duration, mismatched DTSTART/DTEND value types).

async create_task(task_list_id: str, title: str, **kwargs) → str[source]#

Create a task in a task list.

Parameters:
  • task_list_id – The JMAP task list ID to create the task in.

  • title – Task title (maps to VTODO SUMMARY).

  • **kwargs – Optional JMAP Task fields using wire names: description, due, start, timeZone, estimatedDuration, percentComplete, progress, priority.

Returns:

The server-assigned JMAP task ID.

Raises:

JMAPMethodError – If the server rejects the create request.

async decline_invitation(event_id: str, own_email: str, account_id: str | None = None) → None[source]#

Decline a meeting invitation, notifying the organizer.

Same as accept_invitation(), setting participationStatus to declined instead.

async delete_calendar(calendar_id: str, on_destroy_remove_events: bool = False, account_id: str | None = None) → None[source]#

Delete a calendar.

Parameters:
  • calendar_id – The JMAP calendar ID to delete.

  • on_destroy_remove_events – If False (the default), deleting a calendar that still has events fails with a calendarHasEvent error instead of deleting anything. Pass True to remove those events along with the calendar.

  • account_id – The account owning calendar_id; pass the owner’s account here to delete a calendar shared with you.

Raises:

JMAPMethodError – If the server rejects the delete. error_type is "calendarHasEvent" when the calendar still has events and on_destroy_remove_events was left False.

async delete_event(event_id: str, account_id: str | None = None, send_scheduling_messages: bool = False, if_in_state: str | None = None) → None[source]#

Delete a calendar event.

Parameters:
  • event_id – The JMAP event ID to delete.

  • account_id – The account owning event_id; pass the owner’s account here to delete an event on a calendar shared with you.

  • send_scheduling_messages – If true, and this account is the event’s origin, the server sends an iTIP CANCEL to the event’s participants (draft-ietf-jmap-calendars section 5.9.2.2).

  • if_in_state – A state string from a prior call that read this account’s calendar state. If the account has changed since, the server rejects the delete with JMAPMethodError (error_type == "stateMismatch") instead of applying it (RFC 8620 Section 5.3). The state covers every event in the account, not just this one. None applies unconditionally.

Raises:

JMAPMethodError – If the server rejects the delete.

async delete_task(task_id: str) → None[source]#

Delete a task.

Parameters:

task_id – The JMAP task ID to delete.

Raises:

JMAPMethodError – If the server rejects the delete.

async download_attachment(blob_id: str, content_type: str | None = None, filename: str | None = None, account_id: str | None = None) → bytes[source]#

Download a JMAP blob by id.

See download_attachment() for the full semantics.

async get_address_books(account_id: str | None = None) → list[JMAPAddressBook][source]#

Fetch all address books for an account.

See get_address_books() for the full semantics, including the capability-fallback behavior.

async get_availability(account_ids: list[str], start: str, end: str, show_details: bool = False) → dict[str, list[BusyInterval]][source]#

Return busy intervals for each of your own accounts over a time period.

See get_availability() for the full semantics.

async get_calendar_subscriptions() → list[JMAPCalendar[Literal[True]]][source]#

Fetch the calendars the authenticated account is subscribed to.

JMAP Calendars has no separate subscription object; this is get_calendars() filtered to is_subscribed.

Returns:

List of JMAPCalendar objects with is_subscribed set.

async get_calendars(account_id: str | None = None) → list[JMAPCalendar[Literal[True]]][source]#

Fetch all calendars for an account.

Parameters:

account_id – Pass a different account here to browse calendars another user has shared with you, once share_calendar has granted access; the session only lists accounts you can actually reach (see Session.raw["accounts"]).

Returns:

List of JMAPCalendar objects.

async get_event(event_id: str, account_id: str | None = None) → JMAPCalendarObject[source]#

Fetch a calendar event as an iCalendar string.

Parameters:
  • event_id – The JMAP event ID to retrieve.

  • account_id – The account owning event_id; pass the owner’s account here to fetch an event on a calendar shared with you.

Returns:

A JMAPCalendarObject wrapping the raw JSCalendar dict. parent is None since no JMAPCalendar is available at the client level.

Raises:

JMAPMethodError – If the event is not found.

async get_event_attachments(event_id: str, account_id: str | None = None) → list[JMAPAttachment][source]#

Return the attachments on a calendar event.

See get_event_attachments() for the full semantics.

async get_objects_by_sync_token(sync_token: str, account_id: str | None = None, max_changes: int | None = None) → tuple[list[JMAPCalendarObject], list[JMAPCalendarObject], list[str], str, str, bool][source]#

Fetch one page of events changed since a previous sync token.

Calls CalendarEvent/changes to discover which events were created, modified, or destroyed since sync_token was issued. Created and modified events are returned as JMAPCalendarObject instances; destroyed events are returned as IDs (the objects no longer exist on the server).

Parameters:
  • sync_token – A state string previously returned by get_sync_token() or by a prior call to this method.

  • account_id – The account to fetch changes for; pass a different account here to sync a calendar shared with you. Must match the account sync_token was issued for.

  • max_changes – Optional upper bound on the number of changes the server returns in this page. See get_objects_by_sync_token() for the paging behavior this enables.

Returns:

  • added: objects for newly created events (parent is None).

  • modified: objects for updated events (parent is None).

  • deleted: Event IDs that were destroyed.

  • old_state: The sync_token argument echoed back by the server. Compare this against sync_token to detect a server that silently accepted an invalid or stale state instead of reporting an error (RFC 8620 Section 5.2 does not require a server to validate sinceState against a real prior state; confirmed live that Cyrus echoes back whatever value it was given, including one that was never actually issued, rather than rejecting it).

  • new_state: Pass to the next call to this method as sync_token, whether or not has_more_changes is set. Named to match oldState/newState as a pair (RFC 8620 Section 5.2), not sync_token: both names refer to the same kind of value, a state string previously returned by this method or get_sync_token().

  • has_more_changes: If true, the server capped this page and more changes remain; call this method again with new_state to fetch the next page (RFC 8620 Section 5.2).

Return type:

A 6-tuple (added, modified, deleted, old_state, new_state, has_more_changes)

async get_sync_token(account_id: str | None = None) → str[source]#

Return the current CalendarEvent state string for use as a sync token.

Calls CalendarEvent/get with an empty ID list, so no event data is transferred, only the state field from the response.

Parameters:

account_id – The account to read the state of; pass a different account here to sync a calendar shared with you, since a JMAP state string is scoped to one account, not the whole session.

Returns:

Opaque state string. Pass to get_objects_by_sync_token() to retrieve only what changed since this point.

async get_task(task_id: str) → dict[source]#

Fetch a task by ID.

Parameters:

task_id – The JMAP task ID to retrieve.

Returns:

Raw JMAP Task dict as returned by the server.

Raises:

JMAPMethodError – If the task is not found.

async get_task_lists() → list[dict][source]#

Fetch all task lists for the authenticated account.

Returns:

List of raw JMAP TaskList dicts as returned by the server.

async get_task_sync_token(account_id: str | None = None) → str[source]#

Return the current Task state string for use as a sync token.

Calls Task/get with an empty ID list, so no task data is transferred, only the state field from the response. Reads the Task type’s own state counter, not TaskList’s: RFC 8620 Section 5.2’s /changes is scoped per type, and get_tasks_by_sync_token() calls Task/changes, which only accepts a state previously returned for Task itself.

Parameters:

account_id – The account to read the state of; pass a different account here to sync a task list shared with you, since a JMAP state string is scoped to one account, not the whole session.

Returns:

Opaque state string. Pass to get_tasks_by_sync_token() to retrieve only what changed since this point.

async get_tasks_by_sync_token(sync_token: str, account_id: str | None = None, max_changes: int | None = None) → tuple[list[dict], list[dict], list[str], str, str, bool][source]#

Fetch one page of tasks changed since a previous sync token.

Calls Task/changes to discover which tasks were created, modified, or destroyed since sync_token was issued. Created and modified tasks are returned as raw JMAP Task dicts, matching every other task method’s own return convention; destroyed tasks are returned as IDs.

Parameters:
  • sync_token – A state string previously returned by get_task_sync_token() or by a prior call to this method.

  • account_id – The account to fetch changes for; pass a different account here to sync a task list shared with you. Must match the account sync_token was issued for.

  • max_changes – Optional upper bound on the number of changes the server returns in this page. See get_objects_by_sync_token() for the paging behavior this enables.

Returns:

  • added: raw dicts for newly created tasks.

  • modified: raw dicts for updated tasks.

  • deleted: Task IDs that were destroyed.

  • old_state: The sync_token argument echoed back by the server. See get_objects_by_sync_token() for why this is worth comparing against sync_token yourself.

  • new_state: Pass to the next call to this method as sync_token, whether or not has_more_changes is set. See get_objects_by_sync_token() for why this is named new_state rather than sync_token.

  • has_more_changes: If true, the server capped this page and more changes remain; call this method again with new_state to fetch the next page (RFC 8620 Section 5.2).

Return type:

A 6-tuple (added, modified, deleted, old_state, new_state, has_more_changes)

async renew_push(subscription_id: str, expires: str | None = None) → None[source]#

Extend (or shorten) a push subscription’s expiry.

See renew_push() for the full semantics.

async search_contacts(text: str | None = None, email: str | None = None, account_id: str | None = None) → list[JMAPContact][source]#

Search for contact cards.

See search_contacts() for the full semantics.

async search_events(calendar_id: str | None = None, start: str | None = None, end: str | None = None, text: str | None = None, has_attachment: bool | None = None, participant_email: str | None = None, participation_status: str | None = None, participant_role: str | None = None, account_id: str | None = None, position: int = 0, limit: int | None = None, calculate_total: bool = False) → EventSearchResult[source]#

Search for calendar events.

See search_events() for the full semantics, including why has_attachment/participant_email/ participation_status/participant_role filter client-side and their confirmed server-specific false-negatives, and how total behaves on calculate_total.

async search_tasks(text: str | None = None, due_before: str | None = None, due_after: str | None = None, progress: str | None = None) → list[dict][source]#

Search for tasks.

See search_tasks() for the full semantics, including why due_before/due_after/progress filter client-side and what the Task/query-unsupported fallback can and cannot help with.

async send_invite(calendar_id: str, ical_str: str, account_id: str | None = None, if_in_state: str | None = None) → str[source]#

Create a calendar event and notify its participants.

Identical to create_event(), except the server actually dispatches iTIP invitations to the event’s participants (an iCalendar string with ATTENDEE/ORGANIZER properties). create_event itself never sends scheduling messages; use this instead when the event has participants who need to be notified.

Parameters:
  • calendar_id – The JMAP calendar ID to create the event in.

  • ical_str – A VCALENDAR string representing the event.

  • account_id – The account owning calendar_id, same as create_event().

  • if_in_state – See create_event().

Returns:

The server-assigned JMAP event ID.

Raises:
  • JMAPMethodError – If the server rejects the create request, or with error_type == "noSupportedScheduleMethods" if a participant has no usable delivery method.

  • ValueError – Same conditions as create_event().

async set_default_alerts(calendar_id: str, alerts_with_time: dict | None = None, alerts_without_time: dict | None = None, account_id: str | None = None) → None[source]#

Set a calendar’s default alerts for new events.

alerts_with_time applies to timed events, alerts_without_time to all-day events (draft-ietf-jmap-calendars section 4). Each is a map of alert ID to Alert dict (RFC 8984 Section 4.5.2). Pass None to leave a property unchanged; pass {} to clear it.

Parameters:

account_id – The account owning calendar_id; pass the owner’s account here to set default alerts on a calendar shared with you.

Raises:

JMAPMethodError – If the server rejects the update.

async share_calendar(calendar_id: str, account_id: str, rights: dict, owning_account_id: str | None = None) → None[source]#

Share a calendar with another account.

account_id must already be a resolved JMAP Principal ID, not an email address. This client has no Principal/query/Principal/get support yet (RFC 9670), so resolving an email address to a Principal ID is left to the caller.

Replaces account_id’s entry in the calendar’s shareWith map wholesale; other accounts already sharing the calendar are left untouched. rights itself is not merged with any rights that account already had, so granting an additional right to an account that is already shared with needs the full rights dict, not just the new key. Requires the caller to already hold the mayShare right, and cannot grant a right the caller does not themselves hold.

Parameters:
  • account_id – The account to grant rights to.

  • rights – Dict of right names to bool, e.g. {"mayReadItems": True}. Replaces any rights this account already had on this calendar.

  • owning_account_id – The account owning calendar_id; pass the owner’s account here to re-share a calendar that was itself shared with you (if you hold mayShare on it).

Raises:

JMAPMethodError – If the server rejects the update, for example with forbidden when the caller lacks mayShare.

async subscribe_push(callback_url: str, device_client_id: str, types: list[str] | None = None) → str[source]#

Register a push subscription.

See subscribe_push() for the full semantics, including why this client cannot complete the verification handshake by itself.

async tentatively_accept(event_id: str, own_email: str, account_id: str | None = None) → None[source]#

Tentatively accept a meeting invitation, notifying the organizer.

Same as accept_invitation(), setting participationStatus to tentative instead.

async unsubscribe_push(subscription_id: str) → None[source]#

Destroy a push subscription.

See unsubscribe_push() for the full semantics.

async update_calendar(calendar_id: str, name: str | None = None, color: str | None = None, timezone: str | None = None, account_id: str | None = None) → None[source]#

Update a calendar’s name, color, or time zone.

name, color, and timeZone are per-user properties (draft-ietf-jmap-calendars section 4.3). Called by the calendar’s owner, this changes the value for everyone until a sharee sets their own override. Called by a sharee, it only ever changes that sharee’s own view; it can never rename or recolor the calendar for the owner or anyone else it is shared with.

Parameters:

account_id – The account owning calendar_id; pass the owner’s account here to update a calendar shared with you.

Raises:

JMAPMethodError – If the server rejects the update.

async update_event(event_id: str, ical_str: str, account_id: str | None = None, if_in_state: str | None = None) → None[source]#

Update a calendar event from an iCalendar string.

Parameters:
  • event_id – The JMAP event ID to update.

  • ical_str – A VCALENDAR string with the updated event data.

  • account_id – The account owning event_id; pass the owner’s account here to update an event on a calendar shared with you.

  • if_in_state – A state string from a prior call that read this account’s calendar state. If the account has changed since, the server rejects the update with JMAPMethodError (error_type == "stateMismatch") instead of applying it (RFC 8620 Section 5.3). The state covers every event in the account, not just this one. None applies unconditionally. Sent unchanged on every retry of the null-key-dropping loop below, since those retries narrow the same patch rather than re-reading the account’s state.

Raises:
  • JMAPMethodError – If the server rejects the update.

  • ValueError – If ical_str is malformed in a way ical_to_jscal rejects (missing UID/DTSTART, a negative duration, mismatched DTSTART/DTEND value types).

async update_task(task_id: str, patch: dict) → None[source]#

Update a task with a partial patch.

Parameters:
  • task_id – The JMAP task ID to update.

  • patch – Partial patch dict mapping property names to new values.

Raises:

JMAPMethodError – If the server rejects the update.

async upload_attachment(data: bytes, content_type: str, account_id: str | None = None) → str[source]#

Upload binary data as a JMAP blob.

See upload_attachment() for the full semantics.

Last updated on 2026-10-08.