> ## Documentation Index
> Fetch the complete documentation index at: https://developers.getbreezyapp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Read and manage the schedule

> Read complete visits and internal events, then make deliberate scheduling changes.

Every valid company API key can use scheduling reads and writes. Begin with `GET /company` to establish the timezone and `GET /schedule/settings` for members, their scheduling capabilities, arrival-window options and calendar display hours.

## Read a day or week

`GET /schedule?starts_from=2026-09-08T07:00:00Z&starts_before=2026-09-09T07:00:00Z` reads September 8 in Los Angeles. Both timestamps require an offset. The start is inclusive and the end exclusive. Visits qualify when their arrival window or an assignment overlaps the period, including work that began earlier.

The `data` array contains `APPOINTMENT` visits and `INTERNAL_EVENT` occurrences. Visits include customer, contact, location, coordinates, job dimensions, arrival window, confirmation/cancellation state and **all** assignments. A member filter selects visits involving that member; it retains their other technicians. An empty assignment array identifies an unassigned visit. Jobs without any visit remain available through `/jobs`.

Internal events retain their reason, such as training, on-call duty or time off. Recurring events expand in the company's timezone, preserving local times through daylight-saving changes and honoring series end dates and exception days. Identify an occurrence with its series `id` and `starts_at`. Unsupported stored recurrence returns `409 INVALID_SCHEDULE_EVENT` with the event ID instead of hiding the event.

Follow `pagination.next_cursor` until null. Periods may span at most 31 days; pages contain at most 100 complete items. Reads exceeding 5,000 selected visits, event series or occurrences return `400 SCHEDULE_TOO_LARGE`; narrow the period or member filter. Order is earliest start at millisecond precision, ID, then kind. Cursors bind to the company, timezone and filters. They provide traversal of current data, not a snapshot or deletion feed.

The default `canceled=false` selects uncanceled visits and internal events. `canceled=true` selects canceled visits only. Business-unit, job-type and job-class filters select visits only; internal events have no such dimensions. Empty calendar space and display hours do not establish availability.

## Understand dispatch state

Each assignment exposes its recorded execution status. The derived visit `dispatch_status` is `CANCELED` for canceled visits, `UNASSIGNED` with no assignments, then `IN_PROGRESS` if any assignment is in progress, `EN_ROUTE` if any is en route, `COMPLETED` if all assignments are completed, and `ASSIGNED` otherwise. This is operational state, not attendance, actual work hours or a lateness promise.

## Change a visit

| Operation                            | Behavior                                                                                                          |
| ------------------------------------ | ----------------------------------------------------------------------------------------------------------------- |
| `POST /job-appointments`             | Create a visit for an existing company job, optionally with several technicians. Returns 201.                     |
| `PATCH /job-appointments/{id}`       | Edit description/type, change the arrival window, or replace the complete assignment set atomically.              |
| `POST /job-appointments/{id}/cancel` | Record a required reason and optional note, remove assignments and disable pending visit confirmations/reminders. |
| `POST /job-assignments`              | Add one technician without removing the others. Returns 201.                                                      |
| `PATCH /job-assignments/{id}`        | Move or reassign one technician.                                                                                  |
| `DELETE /job-assignments/{id}`       | Remove one assignment, retaining the visit. Supply the conditional-write JSON body.                               |

Every command requires an `Idempotency-Key` header containing 8–128 letters, numbers, underscores or hyphens. Use a fresh key for each intended change. Retry the same operation with the same key and body after a network failure or retryable server error. Keys are scoped to company and authenticated user and retained without an automatic expiry. A different request with the same key returns `409 IDEMPOTENCY_CONFLICT`.

For any edit, first read `GET /schedule/appointments/{id}` or use a visit returned by `/schedule`. Pass its opaque `revision` as `expected_revision`. A stale revision returns `409 STALE_REVISION` without changes. Re-read the visit and reconsider the edit; do not automatically overwrite another dispatcher's work. A successful command returns the full visit, a new revision and notification outcome. A replay returns the original committed visit snapshot; re-read to obtain subsequent changes.

```http theme={"theme":{"light":"github-light","dark":"github-dark"}}
PATCH /v1/job-appointments/11111111-1111-4111-8111-000000001109
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
Idempotency-Key: reschedule-20260908-001

{
  "expected_revision": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
  "window_start_at": "2026-09-09T19:00:00Z",
  "window_end_at": "2026-09-09T21:00:00Z"
}
```

Omitted fields retain their values. An omitted `assignments` array preserves every technician. A supplied array replaces the complete set: include each existing assignment's `id` to retain it; omit `id` to add one; omitted existing IDs are removed. Moving the arrival window does **not** shift assignments implicitly. Include their revised spans in the same request when both should move.

Assignments must have positive durations and distinct, active, eligible company members. Work outside the arrival window requires `allow_outside_window=true`, or an explicit window change. The API permits scheduling overlaps and does not select or reserve available slots. It rejects moving, reassigning, removing or canceling active/completed work; execution-status transitions remain an office workflow.

Write timestamps require a UTC offset and support up to six fractional second digits, matching database precision. Appointment windows must also have positive durations. UUIDs are case-insensitive; responses use their canonical lowercase spelling.

## Request customer notifications explicitly

Omitting `customer_notification` (or sending `null`) sends no customer message for that operation, regardless of stored company defaults. Each operation may include one request:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "customer_notification": {
    "channel": "EMAIL",
    "contact_id": "11111111-1111-4111-8111-000000001101"
  }
}
```

The contact must be actively linked to the job's account in this company. The API uses that contact's primary email or phone; SMS requires a subscribed number. The message describes the scheduled, updated or canceled visit in the company's timezone. This is a single immediate notification, not enrollment in reminders. Rescheduling the arrival window or canceling disables the visit's stored confirmation/reminder flags. A reschedule clears the previous reminder's sent marker so Office can explicitly authorize a reminder for the new window later. Other edits leave previously authorized reminders unchanged.

The scheduling transaction commits before notification delivery. `notification.status` is `NOT_REQUESTED`, `PENDING`, `SENT` (provider call completed), `UNKNOWN` (delivery may have occurred), or `SUPPRESSED` (the visit or recipient changed before delivery). An identical retry can resume an unclaimed pending send. Before sending, the API rechecks the visit revision, account contact link, selected destination and SMS subscription; an obsolete request is suppressed. An attempted send is never automatically repeated, including after a timeout or process interruption; `UNKNOWN` requires checking the communication history. Notification failure or suppression does not undo the visit change.

This slice does not send technician alerts or run job-lifecycle automations. Explicit status transitions, recurring-event editing, automatic booking, and draft preview/apply are separate workflows. Existing reporting reads retain their original filters and representations, including start-time filters on `/job-appointments` and `/job-assignments`.
