---
title: "List calendar entries"
method: GET
path: "/v1/calendar-entries"
tags: ["Calendar"]
---

# List calendar entries

`GET /v1/calendar-entries`

## Request

- Base URL: `https://api.officina.it`
- URL: `https://api.officina.it/v1/calendar-entries`
- Auth: HTTP bearer

Returns calendar entries scoped to the authenticated user's
company.

Filter dimensions are mutually exclusive — the first match
wins, in order: `customer_id`, `vehicle_id`, then the default
timeframe. When no filter is supplied, returns entries from
midnight today to midnight 30 days later (Europe/Rome) across all
calendars.

Timeframe: `from` / `to` select the entries that overlap the
instant window [`from`, `to`) — an entry ending exactly at `from`
is not included.

Pagination (revision 2026-10-13 and later): every filter pages.
Entries are ordered by `start_time`, then `id`; `limit` sets the
page size (default 20, max 100) and `next_starting_after`, present
while `has_more` is true, is the opaque cursor to send back as
`starting_after`. The cursor marks a position, so entries created
or deleted while paging don't shift the remaining pages. A cursor
that is not one this endpoint issued → 422 on `/starting_after`.

Older revisions: the timeframe query ignores `limit` and returns
the whole window, stopping at 2000 entries with `has_more`; the
`customer_id` / `vehicle_id` lists return the first `limit` entries
with `has_more` but no cursor. `starting_after` is validated and
ignored.

## Query parameters

- `customer_id` string, uuid
- `vehicle_id` string, uuid
- `calendar_id` string
- `from` string, date-time
- `to` string, date-time
- `limit` integer
- `starting_after` string

## Response `200`

Page of calendar entries.

- object
  - `object` 'list', required: Polymorphic discriminator.
  - `data` CalendarEntry[], required: Page of items. Each item carries its own `object` field.
    - `object` 'calendar_entry', required
    - `id` string, uuid, required
    - `title` string, required
    - `description` string, nullable
    - `user_id` string, uuid, required
    - `start_time` string, date-time, required
    - `end_time` string, date-time, required
    - `color` string, required: Hex colour, e.g. #2563eb
    - `email_reminder` boolean, required
    - `customer_id` string, uuid, nullable
    - `vehicle_id` string, uuid, nullable
    - `worksheet_id` string, uuid, nullable
    - `quote_id` string, uuid, nullable
    - `checkin_id` string, uuid, nullable
    - `work_category_id` string, uuid, nullable
    - `work_category_name` string, nullable
    - `work_category_ids` string[], required: Every work category ("categoria") of the document, in the order chosen. `work_category_id` (kept for clients that handle a single category) always equals the first. `[]` when the document has none, never null. Available since revision 2026-10-03.
    - `work_categories` WorkCategory[], required: The same list as `work_category_ids` with each category's name, same order. `work_category_name` is the first one's name. Available since revision 2026-10-03.
      - `object` 'work_category', required
      - `id` string, uuid, required
      - `name` string, required
    - `calendar_id` string, uuid, nullable
    - `auto_reminder_enabled` boolean, required
    - `auto_reminder_channel` 'email' | 'sms' | 'whatsapp' | 'null', nullable
    - `auto_reminder_sent_at` string, date-time, nullable
    - `customer` CalendarEntryCustomer: Slim denormalized view of the linked customer, embedded inside a CalendarEntry. The full Customer resource lives at `/v1/customers/{id}`.
      - `object` 'customer', required
      - `id` string, uuid, required
      - `full_name` string, nullable
      - `email` string, nullable
      - `phone` string, nullable
    - `vehicle` CalendarEntryVehicle: Slim denormalized view of the linked vehicle, embedded inside a CalendarEntry. The full Vehicle resource lives at `/v1/vehicles/{id}`.
      - `object` 'vehicle', required
      - `id` string, uuid, required
      - `license` string, nullable
      - `model` string, required
      - `manufacturer_name` string, required
    - `user` CalendarEntryUser: Slim denormalized view of the owning user, embedded inside a CalendarEntry.
      - `object` 'user', required
      - `id` string, uuid, required
      - `name` string, required
    - `calendar` CalendarEntryCalendar: Slim denormalized view of the parent calendar, embedded inside a CalendarEntry. The Calendar container itself is not exposed on this tier.
      - `object` 'calendar', required
      - `id` string, uuid, required
      - `name` string, required
    - `created_at` string, date-time, required
    - `updated_at` string, date-time, required
    - `deleted_at` string, date-time, nullable
  - `has_more` boolean, required: True if more results exist beyond this page. Use this for the "load more" UI — `data.length` alone is unreliable because a full page may still be the last page.
  - `next_starting_after` string: Opaque cursor for the next page. Present (and non-empty) when `has_more` is `true`; omitted otherwise. Pass the value verbatim as `?starting_after=` on the follow-up request. Clients MUST NOT parse it — today it encodes an offset, tomorrow it may encode a `(created_at, id)` tuple without a wire-visible change.
  - `url` string, required: The request path that produced this list.
  - `calendar_filter` CalendarFilter: The effective calendar selection the server applied to a calendar-entry timeframe listing, echoed on the list envelope so clients can render the active calendar without re-deriving it. An empty `calendar_id` means "all calendars"; a selection is not carried over between requests. Echoed only on the default timeframe listing — `customer_id` / `vehicle_id` lookups don't apply a calendar filter.
    - `object` 'calendar_filter', required
    - `calendar_id` string, required: Selected parent calendar id, or empty for all calendars.

## Other responses

- `401`: Missing, malformed, expired, or wrong-audience bearer token.
- `402`: The company has no active subscription, or its plan does not include API access. API access is included in the Business and Illimitato plans.
- `403`: The authenticated user lacks the required permission.
- `415`: Content-Type is not `application/json`, or Accept rejects JSON.
- `422`: A request field is missing or invalid (fast-fail).
- `500`: Unhandled server error. Detail is logged, not exposed.

---

[API](https://skmtc.dev/officina/apis/officina-it-api.md) · [All operations](https://skmtc.dev/officina/apis/officina-it-api/llms.txt) · [OpenAPI document](https://skmtc.dev/officina/apis/officina-it-api/revisions/7da3fccfe551?raw)
