:::endpoint GET /v1/me/calendar/events

What a grid draws. Repeating appointments are expanded, so a weekly meeting
comes back once per week in the window rather than once with a rule to work out.

:::note The window is bounded
At most 92 days per request. An app showing a year asks four times.
:::

## Request

| Parameter     | Description                                                                                                   |
| ------------- | ------------------------------------------------------------------------------------------------------------- |
| `from`        | ISO 8601. Defaults to the start of today                                                                      |
| `until`       | ISO 8601. Defaults to 31 days after `from`                                                                    |
| `calendars[]` | Only these calendars. Defaults to every calendar in reach. Ids you cannot see are ignored rather than refused |

```bash
curl -G https://api.proppertrading.com/v1/me/calendar/events \
  -H "Authorization: Bearer ptat_9f2c1a44_3b8e7d2f5c9a1b4e6d8f0a2c4e6b8d1f" \
  --data-urlencode "from=2026-08-24T00:00:00Z" \
  --data-urlencode "until=2026-08-31T00:00:00Z" \
  --data-urlencode "calendars[]=12"
```

## Response

```json
{
    "data": [
        {
            "id": 4471,
            "object": "calendar_event",
            "occurrence_start": "2026-08-24T07:00:00+00:00",
            "calendar_id": 12,
            "title": "Weekly sync",
            "description": null,
            "location": null,
            "starts_at": "2026-08-24T07:00:00+00:00",
            "ends_at": "2026-08-24T07:30:00+00:00",
            "all_day": false,
            "timezone": "Europe/Amsterdam",
            "status": "confirmed",
            "source": "manual",
            "is_recurring": true,
            "recurrence_rule": "FREQ=WEEKLY;BYDAY=MO",
            "conference": {
                "provider": "google_meet",
                "url": "https://meet.google.com/abc-defg-hij",
                "meeting_id": null,
                "passcode": null
            },
            "organizer": {
                "name": "Sarah Klein",
                "email": "sarah@acmefunded.com"
            },
            "attendees": [
                {
                    "name": "Tom de Vries",
                    "email": "tom@acmefunded.com",
                    "response": "accepted",
                    "is_organizer": false,
                    "is_optional": false
                }
            ],
            "reminders": [{ "minutes_before": 10, "channel": "in_app" }],
            "tags": [{ "id": 4, "name": "Onboarding", "color": "emerald" }],
            "can_write": true
        }
    ],
    "meta": {
        "request_id": "req_01kz4bkr1aj1gv63d8fv3133ey",
        "from": "2026-08-24T00:00:00+00:00",
        "until": "2026-08-31T00:00:00+00:00"
    }
}
```

| Field              | Description                                                                                                                                               |
| ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `occurrence_start` | Which appearance this is, as the moment the rule originally put it. Null on an appointment that happens once. Send it back on every write                 |
| `is_recurring`     | Whether this is one of many                                                                                                                               |
| `status`           | `confirmed`, or `pending` for an invitation that arrived by mail and has not been answered                                                                |
| `source`           | `manual`, `mail` for one that arrived as an invitation, or `import`                                                                                       |
| `reminders`        | Only ever yours. A shared calendar does not hand you a colleague's alarms                                                                                 |
| `tags`             | The labels on it, whole rather than by id. Of the workspace, so the same label means the same thing in every calendar. Empty where the workspace has none |
| `can_write`        | Whether this account may change it                                                                                                                        |

At the `busy` level `title` reads "Busy" and every other field is empty or null.
The words never leave the server.

## Errors

| Status | Code                     | Meaning                                    |
| ------ | ------------------------ | ------------------------------------------ |
| 400    | `invalid_date`           | A date was not ISO 8601                    |
| 400    | `invalid_window`         | `until` is not after `from`                |
| 400    | `window_too_wide`        | More than 92 days apart                    |
| 403    | `insufficient_scope`     | This device does not hold `calendar:read`  |
| 403    | `calendar_not_permitted` | The account may not open a calendar at all |