# CONTRACT.md — Dial

What this product does. Every clause has a stable ID and at least one tagged test; `pnpm verify` reports each as satisfied, overridden, or broken. Security, privacy, and operational guarantees are in `BASELINE.md`; a clause here may supersede one of them by naming its `BASE-` ID (`AUTH-001`, `PROFILE-003`, `EVENT-005`, and `DATA-001` do). Capacity and rate limits are in `seed.json → limits`. Times are p95 at those limits. Clauses marked *Policy* have a default that buyers may replace through the named extension point. This seed is at the maturity recorded in `seed.json → maturity`: the clauses below are the specification the backend pass must satisfy, and until it lands `pnpm verify` checks contract and manifest integrity only and reports no clause as satisfied.

## Scope

Dial is a scheduler for one business or one independent host. Operators publish a host profile and one or more event types, connect the host's calendars, and define when bookings may occur. An invitee books without an account, in their own timezone, on a time rechecked against the host's live calendar at the moment they confirm; the booking then carries a calendar event, a location, email that says what actually happened, and a private link to reschedule or cancel. An invitee who finds nothing suitable can ask the host for another window.

## Authorisation

- **AUTH-001 — Public authority.** Supersedes `BASE-ACCESS-004` for the paths listed in `seed.json → deploy.publicPaths` and for no others. Those paths authorise the caller by the capability material named in `AUTH-002` to `AUTH-005` instead of an operator session, each capability grants exactly the one action or record it names, and a request carrying no valid capability material changes nothing. Every other mutation requires a valid operator session and a server-side ownership check.
- **AUTH-002 — Booking nonce.** Reaching the details step for a selected slot, and reaching the review step of an alternative request, each issue a single-use nonce of at least 256 bits from a cryptographically secure random source, bound to the event type and to either the selected slot or the composed alternative request, and expiring 30 minutes after issue. The nonce is not bound to a browser: possession of an unexpired, unconsumed nonce is what authorises the confirmation. A confirmation whose nonce is missing, unknown, expired, or bound to different values creates nothing and answers per `PUBLIC-003`; a nonce that has already been consumed is answered by `BOOK-003`.
- **AUTH-003 — Booking access.** A booking is readable and changeable from a public surface only through the manage token defined in `BOOK-006`. A booking ID, an invitee email address, or any other guessable identifier grants access to no booking data.
- **AUTH-004 — Calendar authorisation callback.** The calendar OAuth callback accepts only signed state that is bound to the operator session that started the connection and was issued less than 10 minutes earlier. State that is missing, unsigned, mismatched, replayed, or expired stores no credential, connects no account, and returns 400.
- **AUTH-005 — Provider callbacks.** A calendar or mail provider callback is accepted only when the provider's own authentication passes — its registered channel identity or its request signature — together with the secret configured for that provider. A callback that fails the check returns 401 and changes nothing.

## Operators

- **OPER-001 — Equal operators.** Every operator can see and act on the same host profile, event types, availability, calendar connections, bookings, and requests. There are no roles.
- **OPER-002 — Operator management.** Operators are invited to, and removed from, the sign-in allow-list in settings. Removing an operator ends their sessions within 60 seconds and changes no record they created, including the calendar connection they authorised. *Policy: `operator.management.v1`; default: any operator may invite or remove another, the operator configured at setup can be removed only by themselves, and the last remaining operator cannot be removed.*
- **OPER-003 — Booking workspace.** Operators can list bookings filtered by the lifecycle states of `BOOK-004`, and independently by whether the start instant is in the future or the past, by event type, by date range, and by whether any external delivery has failed. Case-insensitive substring search over invitee name and email address returns within 500ms. Lists are sorted by start instant.
- **OPER-004 — Booking record.** A booking record shows the event type, every invitee answer and custom-field value, the start and end in both the host's and the invitee's IANA zones, the location or meeting link, the history from `BOOK-010`, the separate state of the calendar event, the meeting link, and each email, and every recorded failure with its `CAL-005` or `NOTIFY-005` reason.

## Profile and event types

- **PROFILE-001 — Host profile.** Operators edit the host's name, title, biography, avatar, IANA timezone, focus areas, and social-proof entries, within the counts and lengths in `seed.json → limits`. A social-proof entry is a quotation and an attribution typed by an operator: Dial collects no invitee feedback and publishes no rating, score, or count it did not receive from an operator. There is exactly one host timezone per deployment and it is the zone every other clause means by "the host's zone". The in-app preview renders the same content as the public page.
- **PROFILE-002 — Public profile.** The host profile is served at `/book/<handle>`, where the handle is set by an operator. It shows the configured host content and every published, listed event type, and never a draft, unlisted, or disabled one. An operator can change the handle; the previous handle then answers per `PUBLIC-005`.
- **PROFILE-003 — Avatar asset.** Supersedes `BASE-INPUT-003` for the `/media/` prefix only. The avatar is an upload within the size and type limits in `seed.json → limits`, stored in R2 and served under `/media/` with its declared content type and inline rather than with a download disposition, because it renders as an image on the public profile and event pages. That prefix is the public delivery channel the baseline requires a product contract to name; every other upload rule still applies, and no other upload in this product is publicly readable.
- **EVENT-001 — Event type lifecycle.** Operators create, edit, publish, disable, and delete event types. An event type is draft, published, or disabled, and a published one is either listed on the public profile or unlisted and reachable only by its own URL. Each carries a title, description, a single duration of 5 to 480 minutes, colour, location, and a slug unique among event types, and is served at `/book/<handle>/<slug>`. A slug can be edited; the previous slug is not retained and then answers per `PUBLIC-005`, while existing bookings and their manage links are unaffected.
- **EVENT-002 — Scheduling constraints.** Each event type configures its recurring availability, date-specific overrides, slot start increment of 5, 10, 15, 30, or 60 minutes, minimum notice of 0 minutes to 7 days, maximum booking horizon of 1 day to `seed.json → limits.availabilityLookaheadDays`, before and after buffers of 0 to 120 minutes each, and an optional maximum number of bookings per host day, where a host day is one calendar date in the host's timezone. A new event type starts at a 30-minute duration, a 15-minute increment, 4 hours' minimum notice, a 60-day horizon, no buffers, and no host-day limit.
- **EVENT-003 — Invitee questions.** Each event type has ordered booking questions of type text, textarea, single choice, multiple choice, or checkbox, each required or optional, within the question and option counts in `seed.json → limits`. Choice options have stable values and editable labels, and a submitted option not configured on the event type is rejected. A name and a syntactically valid email address are always required. *Policy: `booking.questions.v1`; default: name and email, plus an optional agenda of at most 500 characters, on every new event type.*
- **EVENT-004 — Location.** An event type uses exactly one location: a Google Meet conference provisioned per `CAL-003`, a fixed link the operator supplies, a host phone number the invitee calls, or an in-person address. The public event page shows only the location's label; the link, number, or street address appears only after confirmation — in the receipt, the invitee's email, and the calendar event. Dial never collects an invitee phone number.
- **EVENT-005 — Removing an event type.** Supersedes `BASE-DATA-003` for event types only: disabling or deleting an event type removes its public page and all its future availability but does not cascade to its bookings, which keep their times, manage links, calendar events, history, and their own copy of the event title, duration, and location. Invitee data is erased under `DATA-002`, not by deleting an event type.
- **EVENT-006 — Publication readiness.** An event type can be published only when it has at least one open availability window and when its transactional-mail configuration, its destination calendar, and, where its location is provider-generated, its conferencing capability each passed a connection check within the last 24 hours or since they last changed. A connection check is a live read of the destination calendar, a credential probe against the mail provider, and, where conferencing is used, a capability check against the conferencing provider; an operator can run it on demand. The operator sees each unmet requirement named. A requirement that becomes unhealthy after publication does not unpublish the event type: the connection state changes under `CAL-006` and public booking behaves under `AVAIL-004`.

## Public home

- **HOME-001 — Owner front door.** The deployment root `/` is a sessionless owner-facing landing, not a vendor marketing page. It renders the deployment-configured owner identity and single-line description, and links to the host's public booking profile when one is available. Its `Powered by Dial · runeditrun.com` footer credit is a static link controlled by deployment configuration, enabled by default and omitted when disabled.

## Public booking surfaces

- **PUBLIC-001 — Event presentation.** A published event page names the host and the event and shows its description, duration, public location label, the invitee's selected timezone, the recommended times of `AVAIL-007`, every other valid slot in the displayed week, week-by-week navigation bounded by today and the booking horizon, the change window the `booking.reschedule.v1` and `booking.cancellation.v1` policies currently enforce, and, when the event type enables it, the alternative-request path.
- **PUBLIC-002 — Invitee-first flow.** An invitee views availability and completes a booking without an account and without signing in. Before confirmation the flow asks only for the event type's configured questions, names who receives the answers — the host and the connected calendar provider — and links the privacy notice URL configured for the deployment.
- **PUBLIC-003 — Progress and recovery.** The public flow has distinct choose-time, details, and confirmation states, each at its own URL, and reloading or navigating back preserves a still-valid selection. The selection and its nonce are carried in the URL and in the browser's session storage; the public flow sets no cookie. An expired nonce, or an expired or rotated manage token, returns 410; an unknown or mismatched one returns 404. Both render a page that offers a fresh start and that contains no booking, invitee, or sample data.
- **PUBLIC-004 — Embed.** Operators copy an asynchronous script tag naming a public event slug and `inline` or `modal` mode. Inline mode renders where the tag sits; modal mode opens only from an element whose `data-dial-embed` attribute names that embed. The initial script is under 50KB gzipped, and the embedded flow offers the same booking, reschedule, and cancel actions as the standalone page.
- **PUBLIC-005 — Unavailable pages.** An unknown, draft, disabled, deleted, or renamed profile or event URL returns 404 and reveals nothing about whether a record ever existed there. This governs profile and event URLs only; manage-token responses are governed by `PUBLIC-003`, where a rotated or expired token deliberately returns 410 so the invitee knows to ask for a new link. A published event type with no valid slot inside its horizon still returns 200, says so, and offers the alternative-request path when the event type enables it.
- **PUBLIC-006 — Abuse.** A public request refused by the rate limits of `BASE-ACCESS-003` returns 429 and the flow keeps the invitee's selection and answers so the same person can retry without re-entering them. An operator can block an email address or a domain; a submission from a blocked address is refused with a message that does not say it was blocked, and creates no booking, request, notification, or calendar event.

## Availability

- **AVAIL-001 — Schedule and overrides.** Recurring availability is a set of wall-clock windows per weekday in the host's IANA timezone. A date override replaces the recurring windows for one host date and may open or close that whole date. Editing a schedule or an override changes future availability only; it never alters a confirmed booking.
- **AVAIL-002 — Valid slots.** A slot is offered only when every one of these holds: its start falls on the event type's start increment measured from the start of its open window; the full duration fits inside that one window; the start is within the booking horizon and beyond the minimum notice; its host day is under the event type's booking limit; and it overlaps no booking in the `confirmed` state for this host on any event type and no busy interval from any selected conflict calendar, an external event the provider marks free being ignored. Anything else is not offered.
- **AVAIL-003 — Buffers.** The event type's before and after buffers extend the interval that must be free around a candidate slot, over both confirmed Dial bookings and external busy intervals. A buffer never changes the invitee's start or end time, the duration shown anywhere, or the times on the calendar event and invitation.
- **AVAIL-004 — Freshness and failure.** Availability is recomputed when the event page or a week loads. Changing the invitee's timezone re-renders and re-ranks the same slots and needs no new calendar read, because slot validity does not depend on the invitee's zone. A displayed slot may rest on cached provider busy data no more than 5 minutes old; the final check in `BOOK-001` uses provider data no more than 60 seconds old. Every selected conflict calendar is required, and a provider read that does not complete within 5 seconds counts as unreadable. If a required calendar is unreadable at either point, Dial offers and confirms no slot for that host and tells the invitee that booking is temporarily unavailable, rather than offering unverified time.
- **AVAIL-005 — Timezones.** Every invitee-facing time is rendered in the invitee's selected timezone, which starts as the browser's IANA zone when the browser reports one and as the host's zone when it does not. The zone is resolved in the browser and slot instants are fetched in UTC, so no visitor-derived value reaches the server on page load, as `BASE-PUBLIC-001` requires: the invitee's zone is sent only with a request their own action causes — changing the zone, moving week, or selecting a slot. The invitee can change the zone at any point before confirmation, every rendered time carries its zone name and UTC offset, and the details and confirmation surfaces also show the host's local time. A confirmed booking stores the UTC instant and both the host's and the invitee's IANA zone identifiers.
- **AVAIL-006 — Daylight saving time.** A local time that does not exist during a forward transition is never offered. Repeated wall-clock times during a backward transition are shown with distinct UTC offsets, so each offered time maps to exactly one instant. A DST transition between confirmation and the meeting never moves a booking's stored UTC instant, and the host and invitee renderings each follow their own zone's rules.
- **AVAIL-007 — Recommendations and filters.** At most 5 recommended times are shown, each a slot already valid under `AVAIL-002` and each showing the reason it was chosen. The ordering is deterministic: the same inputs always produce the same order. The first render of a page ranks in the host's zone alone; the ranking is recomputed once the invitee's zone reaches the server under `AVAIL-005`. An invitee-facing filter only narrows or reorders slots already valid under `AVAIL-002` and can never surface one that is not. *Policy: `availability.recommendation.v1`; default: slots fully inside 09:00–17:00 in both the host's and the invitee's selected zone first, then the larger sum of contiguous free minutes immediately before and after the slot, then the earlier UTC start.*
- **AVAIL-008 — Response time.** Loading one week of availability for one event type returns within 2 seconds.
- **AVAIL-009 — Operator diagnostics.** For any time an operator evaluates, Dial names which rule excluded it — schedule, date override, start increment, minimum notice, horizon, buffer, host-day limit, an existing Dial booking, or a conflict-calendar busy interval — without disclosing external event detail that `CAL-002` does not retain.

## Booking lifecycle

- **BOOK-001 — Final validation.** Confirming a booking revalidates every answer against the event type's current questions and rechecks the selected slot under `AVAIL-002` against a fresh conflict-calendar read. Any failure creates no booking, no calendar event, and no email, and names the reason: an invalid or missing answer returns 400, and a slot that is no longer valid returns 409.
- **BOOK-002 — Atomic claim.** Every slot admits at most one booking in the `confirmed` state. Claiming the slot and recording its calendar, meeting, and notification commands happen in one transaction that either commits entirely or leaves nothing behind. Of any number of concurrent confirmations for the same slot exactly one commits; every other returns 409 with a `slot_taken` code and is returned to the choose-time step with availability recomputed.
- **BOOK-003 — Idempotent confirmation.** Replaying a confirmation with an already-consumed nonce and an identical payload returns the stored result of the original booking and creates no second booking, calendar event, meeting link, or email. Replaying it with a different payload returns 409 and creates nothing.
- **BOOK-004 — States and occurrences.** A booking is confirmed, cancelled, or rescheduled, and has one or more occurrences of which exactly one is current: a reschedule closes the current occurrence and opens a new one under the same booking ID and iCalendar UID. The calendar event, the meeting link, and each email carry their own pending, succeeded, or failed state; none of them changes the booking's state, and no external failure silently cancels or moves a booking.
- **BOOK-005 — Confirmation receipt.** Within 2 seconds of a successful claim the invitee is shown the event, the start and end in their own zone and in the host's, the duration, the host, the location or its pending state, the calendar file of `CAL-009`, and the manage link of `BOOK-006`. The join control is enabled only when a provisioned joining URL is stored on the booking; while `CAL-003` is still pending it is disabled and labelled as pending.
- **BOOK-006 — Manage link.** Every confirmed booking has a manage link whose token carries at least 256 bits from a cryptographically secure random source and grants view and change access to that one booking, without creating an invitee account. The token is transmitted only to the invitee, in their own email and on their confirmation page; operator surfaces show the booking but never the token, offering an action that copies or resends the link instead. A reschedule keeps the token, which always resolves to the booking's current occurrence. The token expires 90 days after the booking's start instant, and an operator can rotate it, which invalidates the previous token immediately. A request with an expired, rotated, or unknown token answers per `PUBLIC-003` and discloses no booking data.
- **BOOK-007 — Invitee rescheduling.** The holder of a booking's manage link can move it to another slot on the same event type. The new slot is validated under `BOOK-001` with only this booking's own occurrence excluded from internal conflicts, then claimed under `BOOK-002` before the old slot is released; if any step fails, the original booking is unchanged and still confirmed. *Policy: `booking.reschedule.v1`; default: any number of reschedules, allowed until the booking's start.*
- **BOOK-008 — Invitee cancellation.** The holder of a booking's manage link can cancel it with an optional reason. Cancellation cannot be reversed, releases the slot and its buffers within 5 seconds and before the next availability computation, and repeating it returns the same result without sending a second notification. *Policy: `booking.cancellation.v1`; default: allowed until the booking's start.*
- **BOOK-009 — Operator changes.** An operator can reschedule or cancel any confirmed booking under `BOOK-002`, `BOOK-007`, `BOOK-008`, `BOOK-010`, `CAL-003`, and `NOTIFY-004`. The reschedule and cancellation policies constrain invitees only: an operator may also act on a booking that has already started, and may reschedule onto a time outside published availability after confirming that override explicitly, but never onto a time that overlaps another confirmed booking.
- **BOOK-010 — History.** Rescheduling records the original occurrence as rescheduled and links it to its replacement. Every creation, cancellation, and reschedule records who acted — the manage-link holder, the named operator, or the calendar provider under `CAL-008` — the time, the previous and new start, and any reason given.
- **BOOK-011 — Attendance.** After a booking has ended an operator can mark it attended or a no-show and add a note. The mark records the operator and the time, appears in the booking record and the export, changes no lifecycle state and no calendar event, and sends the invitee nothing.
- **BOOK-012 — Operator-created booking.** An operator can create a booking, for an invitee whose name and email address they enter, on any slot valid under `AVAIL-002`, optionally overriding the minimum notice and the horizon but never the conflict and buffer checks. The invitee then receives the ordinary `NOTIFY-001` confirmation and manage link, and `BOOK-010` records the operator as the actor.

## Calendars and meetings

- **CAL-001 — Calendar connection.** An operator connects the host's Google Calendar account through OAuth under `AUTH-004`, selects up to `seed.json → limits.maxConflictCalendars` calendars whose busy intervals block availability, and selects exactly one calendar to receive Dial's bookings. There is exactly one connection; it belongs to the deployment rather than to the operator who authorised it, reconnecting replaces it and keeps the destination selection, and an operator can disconnect. After a disconnect, sync stops, the connection shows as disconnected, published event types keep their pages but offer no slot under `AVAIL-004`, and existing bookings and their history are unchanged. The implementation is replaceable through `calendar.provider.v1`.
- **CAL-002 — Busy privacy.** A calendar read retains only the identifiers, time bounds, transparency, and deletion state needed for conflict detection and sync. Titles, descriptions, attendees, and locations from conflict calendars are never stored, and so are never shown to an invitee or to an operator.
- **CAL-003 — Managed event.** Within 60 seconds of a successful claim the destination calendar holds one event for the booking carrying the event title, the booking's start and end with explicit timezone data, a stable iCalendar UID, the configured location or a Google Meet conference provisioned for this booking, and the invitee as an attendee so the provider's own invitation and updates also reach them — or the booking's calendar state is pending and `CAL-005` applies. An unsuccessful or ambiguous result is handled under `CAL-004` and `CAL-005`.
- **CAL-004 — Idempotent side effects.** Every calendar and meeting command carries the booking and occurrence IDs as its provider idempotency key. After a timeout, a lost response, or any other ambiguous result, the next attempt looks the object up by that key and updates it instead of creating a second one.
- **CAL-005 — Provider failure.** A transient calendar or meeting failure is retried up to 5 times over 1 hour. Until it succeeds the operator and the invitee receipt show the side effect as pending; after the retries are exhausted the operator sees a typed sanitised error code, safe remediation, and a retry action, while the invitee sees only that calendar setup is incomplete and that they should contact the host. The booking stays confirmed, and raw provider errors are neither persisted nor rendered.
- **CAL-006 — Connection health.** Revoked credentials, a refused token refresh, an invalid sync cursor, a push channel that cannot be renewed, or exhausted provider quota puts the connection in an unhealthy state that operators see in settings by name and that raises `calendar.connection-health-changed.v1`. An invalid cursor or a lapsed channel triggers a full resync before the next availability computation. While a required conflict or destination calendar is unreadable, public booking fails closed under `AVAIL-004` and no event type can be published under `EVENT-006`.
- **CAL-007 — Provider callbacks.** Calendar push channels are renewed before they expire. A callback authenticated under `AUTH-005` is processed independently of arrival order: duplicate or out-of-order delivery cannot duplicate a booking change or undo a newer one, and a callback describing a state older than the one held is discarded.
- **CAL-008 — External deletion and edits.** Deleting Dial's managed event in the destination calendar cancels the booking exactly once and sends the ordinary cancellation notices of `NOTIFY-004`. Any other external edit to the event's time or location is recorded as a sync conflict on the booking showing both times; the operator resolves it by restoring the managed event to the canonical time or by cancelling under `BOOK-009`, and Dial never adopts such an edit by itself. *Policy: `calendar.external-deletion.v1`; default: deletion cancels the booking.*
- **CAL-009 — Calendar file.** The confirmation and manage pages offer an `.ics` download for the booking carrying its stable UID and current occurrence. A reschedule increments that file's sequence and a cancellation marks the occurrence cancelled, so an invitee's calendar updates the same entry rather than gaining an unrelated one. The download makes no request to any provider not declared in `seed.json → externals`.

## Alternative availability requests

- **REQUEST-001 — Compose.** Where an event type enables it, an invitee can select future dates and time windows within the counts in `seed.json → limits`, whose starts precede their ends and which do not overlap each other, together with their IANA timezone, a name, an email address, and an optional note of at most 500 characters, and can review the exact request before submitting. A request that omits a date, a window, a name, or a valid email address, or that contains an overlapping or reversed window, is rejected with the offending field named and stores nothing.
- **REQUEST-002 — Submit.** A valid request is recorded once, acknowledged to the invitee on screen, and notified to operators within 60 seconds. The acknowledgement states the response expectation the operator configured, or, where none is configured, only that the host will follow up by email. Replaying the same nonce returns the existing request. A request reserves no time and creates no booking.
- **REQUEST-003 — Operator workflow.** Operators can list requests by submitted, handled, or closed state, open the invitee's windows rendered in both the invitee's and the host's zone, mark one handled or closed, and create a booking for an agreed time. That booking runs the ordinary `BOOK-012` path and marks the request handled.
- **REQUEST-004 — Routing.** A new request notifies every operator unless the routing policy selects a subset or suppresses email; suppressing email never suppresses the in-app record. *Policy: `availability-request.routing.v1`; default: every operator.*
- **REQUEST-005 — Invitee updates.** Submitting sends the invitee one acknowledgement email restating the requested windows in their own zone. Marking a request handled or closed sends the invitee exactly one further email; the closed one says the host cannot offer a time, and neither is sent twice however often the state is set again.

## Notifications

- **NOTIFY-001 — Booking confirmation.** Within 60 seconds of confirmation Dial emails, through `mail.sender.v1`, the invitee and every operator: the event, the time in each recipient's relevant zone, the duration, the answers, and the current calendar state. The invitee's copy also carries their manage link and the `.ics` file of `CAL-009`; no operator copy contains the manage token. Where the meeting link is still pending the email says so instead of inventing one. An operator can mute booking mail — confirmations, changes, and reminders — for themselves in settings; the invitee's copy is never suppressed.
- **NOTIFY-002 — Link completion.** When a pending joining link becomes available Dial updates the managed calendar event and sends the invitee exactly one link-ready email, however many times the completion job runs.
- **NOTIFY-003 — Reminders.** A confirmed future booking schedules invitee and operator reminders carrying the event, the time in the recipient's zone, the location or join link, and, for the invitee, the manage link. A reminder is sent within 5 minutes of its scheduled offset. Rescheduling replaces the scheduled times and cancellation removes them, so no reminder is ever sent for a time the booking no longer holds. *Policy: `reminder.schedule.v1`; default: 24 hours and 1 hour before the start, scheduling only the offsets still in the future at confirmation or reschedule.*
- **NOTIFY-004 — Changes.** A reschedule or a cancellation sends the invitee and every operator exactly one email naming who made the change, the previous time, the new time or the cancellation reason, and the current calendar state.
- **NOTIFY-005 — Delivery failure.** A transient email failure is retried up to 5 times over 1 hour. A permanent failure or a bounce is marked failed on the booking or request with a typed sanitised reason and safe remediation, and can be resent without duplicating a delivery that already succeeded. Raw provider errors are neither persisted nor rendered.
- **NOTIFY-006 — No mail loops.** Product mail is sent from a no-reply address. Mail delivered to that address is discarded and never creates a booking, a request, or a notification.
- **NOTIFY-007 — Connection alerts.** Within 60 seconds of the calendar connection becoming unhealthy under `CAL-006`, every operator is emailed once, naming the cause and the reconnect action, because public booking is failing closed meanwhile. This alert is operational and the mute of `NOTIFY-001` does not suppress it. No further alert is sent for that connection until it has recovered, and recovery is announced the same way.

## Data

- **DATA-001 — Export contents.** Supersedes `BASE-DATA-001` for OAuth credentials only. The export contains every operator, host profile, event type, question, availability rule and override, calendar-connection metadata and sync state, booking and occurrence, invitee answer and custom-field value, attendance mark, alternative request, delivery attempt, setting, and audit entry as JSON, plus every uploaded asset in its original form. Access and refresh tokens are credentials and are never exported: after an import the calendar connection is unhealthy under `CAL-006` until an operator re-authorises it.
- **DATA-002 — Erasure targets.** An operator can delete a booking or an alternative request. Deleting one removes its invitee answers, notes, attendance mark, and uploaded content, invalidates its manage token, and cancels its managed calendar event without sending the invitee a Dial email; the audit entries survive with the invitee reference anonymised, as `BASE-DATA-003` requires. Deleting every booking and request carrying an email address erases that person from the deployment, since Dial keeps no separate invitee record.

## Out of scope

Declared in `seed.json` as non-goals and not tested: multiple hosts or workspaces; roles and permissions; team round-robin or collective scheduling; group events, classes, waitlists, or resource capacity; additional invitee guests on a booking; bookings that wait for host approval before they are confirmed; payments, deposits, refunds, or booking fees; recurring booking series or scheduling polls; marketplace discovery, ratings, or invitee-submitted reviews; SMS, WhatsApp, phone automation, CRM, campaigns, or marketing automation; automatic adoption of arbitrary external-calendar time or location edits; AI routing or ranking, `AVAIL-007` being deterministic and using no model; and native mobile apps.
