---
title: "Push a calendar event"
method: POST
path: "/calendar_events"
tags: ["Calendar events"]
---

# Push a calendar event

`POST /calendar_events`

## Request

- Base URL: `https://www.happyscribe.com/api/v1`
- URL: `https://www.happyscribe.com/api/v1/calendar_events`
- Auth: API key in header `Authorization`

For partners that sync calendars themselves. Send each event as your calendar provider
returned it — a Google Calendar event, a Microsoft Graph event or a Nylas event — or in
the small shape, and get back the calendar event of that occurrence (`source: calendar_sync`),
with the `meeting_id` of its meeting.
We read the title, times, organizer, attendees with their response, and the meeting
link (from the conferencing fields, or found in the location or description).

- **Identity.** An event is its iCal UID plus, for an occurrence of a recurring event,
  its original start. Push the same event again to update it; push it for every member
  who has it and they share one meeting and one bot. Send a stable `iCalUID`.
- **Owner.** `member_email` creates the member the first time, as `POST /members` does;
  `member_id` names an existing one.
- **Recurring events.** Push each occurrence (Google instances, Graph occurrences, Nylas
  instances). A series master, with the repeat rule itself, is refused.
- **Cancelling.** Push the event as your provider reports it cancelled
  (`status: cancelled`, including Google's `{ id, status }` deletions, Graph's
  `isCancelled` or `@removed`, `cancelled: true` in the small shape). The meeting is
  cancelled once no member of the workspace still has the event; pushing it live again
  brings it back.
- **Past events** are kept, with their people, as past meetings. No bot is sent.
- **No link.** The meeting exists and `effective_policy.recording.enabled_because` is
  `no_meeting_url` until a push carries one. Only Google Meet, Zoom and Microsoft Teams
  links count; any other link (Webex, a dial-in page) reads as no link.

Whether a meeting is recorded follows the member's policy; change one meeting with
`PATCH /policies/{id}`. A member who also connects the same calendar sees each
meeting twice: use one calendar source per workspace.

## Request body

- CalendarEventPush: Exactly one of `member_id` and `member_email`.
  - `workspace_id` string, required: Workspace ID, beginning with `wks_`. Treat it as an opaque string. Integer workspace ids are still accepted.
  - `member_id` string: An existing member of the workspace.
  - `member_email` string, email: The calendar owner; created as a member the first time.
  - `metadata` Metadata: Your own key-value pairs, for your ids and context. HappyScribe stores them, returns them on the object and in every webhook that carries it, and never acts on them. Up to 50 keys of up to 40 characters (no square brackets), values up to 500 characters. See [metadata](/meetingkit/setup#a-workspace-per-customer).
  - `event` object, required: The event exactly as your provider returned it: a Google Calendar `events` resource, a Microsoft Graph `event` (request it with `Prefer: outlook.timezone="UTC"` or an IANA time zone), or a Nylas v3 event (bare, as `data`, or as a webhook's `data.object`). Keys keep your provider's casing. Without a provider payload, send the small shape (`CalendarEventSmall`).

## Response `200`

The calendar event of the occurrence, the same object as the meeting's `calendar_event`.
Versions before 2026-10-12 return the meeting instead.

- CalendarEvent: One calendar event per occurrence and workspace: every member's copy of it shares this object, as they share the meeting. Its times, link and title are the occurrence's; its people come from the organizer's copy.
  - `id` string: Beginning with `cev_`. One per meeting; stays the same through reschedules and every member's push.
  - `object` string: Always `calendar_event`.
  - `meeting_id` string: The meeting of this occurrence, beginning with `mtg_`.
  - `ical_uid` string, nullable: The iCal UID shared by every attendee's copy of the event, or `null` when the calendar sent none.
  - `recurring` boolean, nullable: Whether the event is an occurrence of a recurring series. `null` only in a webhook event from before 2026-10-12, which did not record it.
  - `title` string, nullable: The event title.
  - `starts_at` string, date-time, nullable: Scheduled start, in ISO 8601.
  - `ends_at` string, date-time, nullable: Scheduled end, in ISO 8601.
  - `meeting_url` string, nullable: The link to join the call, or `null` when the event has none.
  - `organizer` MeetingPerson
    - `name` string, nullable: The person's name, or `null` when unknown.
    - `email` string, nullable: The person's email, or `null` when unknown.
    - `member_id` string, nullable: Set when the person is a member of the meeting's workspace.
  - `attendees` object[]: Everyone on the invite, the organizer included. Empty once no member has the event.
    - `name` string, nullable: The attendee's name, when the calendar has one.
    - `email` string, nullable: The attendee's email.
    - `member_id` string, nullable: Set when the attendee is a member of the workspace.
    - `response` 'accepted' | 'declined' | 'tentative' | 'needs_action' | 'null', nullable: The attendee's answer to the invite, or `null` when the calendar does not say. - `accepted`: accepted. - `declined`: declined. - `tentative`: answered maybe. - `needs_action`: has not answered.
  - `source` 'calendar_connection' | 'calendar_sync': Where the event came from. - `calendar_connection`: a calendar the member connected. - `calendar_sync`: an event you pushed with `POST /calendar_events`.
  - `metadata` Metadata: Your own key-value pairs, for your ids and context. HappyScribe stores them, returns them on the object and in every webhook that carries it, and never acts on them. Up to 50 keys of up to 40 characters (no square brackets), values up to 500 characters. See [metadata](/meetingkit/setup#a-workspace-per-customer).
  - `cancelled_at` string, date-time, nullable: When the event was cancelled, once no member of the workspace still has it; otherwise `null`.
  - `created_at` string, date-time: When the event first appeared, in ISO 8601.
  - `updated_at` string, date-time: When the event or its meeting last changed, in ISO 8601.

## Other responses

- `401`: Missing or invalid API key, or the key is not an owner or admin of the workspace.
- `404`: The workspace or `member_id` is not one the key can see, or a cancelled event that was never pushed.
- `409`: Another push of the same event is still being processed (`busy`). Retry after `Retry-After` seconds.
- `413`: The request is over 64 KB.
- `422`: An `event` no shape matches (`unrecognised_event`), a recurring series (`recurring_series_not_supported`), an event without an iCal UID or a start (`missing`), unreadable times (`invalid`), or not exactly one of `member_id` and `member_email`.
- `429`: More than 1,000 pushes in a minute for the workspace. `Retry-After` says when to try again.

---

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