# API reference The REST API lives at `https://daybag.io/api/v1`: send `Authorization: Bearer daybag_live_…` and JSON. Lists come back as `{ data }` and errors as `{ "error": { "code", "message" } }` with an HTTP status. Every operation is also an MCP tool of the same name, and this page is generated from the schemas behind the OpenAPI document. URL: https://daybag.io/docs/api Base URL: https://daybag.io/api/v1 · OpenAPI: https://daybag.io/api/v1/openapi.json · MCP: https://daybag.io/api/mcp Auth: `Authorization: Bearer daybag_live_…` ## Offerings ### offerings_list: GET /offerings List offerings (classes, appointments and rentals), paused ones included Request: - kind (query, class | appointment | rental): class: scheduled sessions with seats · appointment: slots from weekly hours · rental: inventory over periods Response 200: JSON - data (object[], required) - data[].id (string, required) - data[].org_id (string, required) - data[].kind (class | appointment | rental, required): class: scheduled sessions with seats · appointment: slots from weekly hours · rental: inventory over periods - data[].name (string, required) - data[].description (string, required) - data[].location (string, required) - data[].duration_minutes (integer, required): Length of a class session or appointment, or of one rental period (1440 = a day) - data[].capacity (integer, required): class: seats per session · appointment: parallel bookings per slot · rental: items in stock - data[].max_per_booking (integer, required): Most seats, party size or items in one booking - data[].price_cents (integer, required): class: per seat · appointment: per booking · rental: per item per period - data[].slot_interval_minutes (integer | null, required): Minutes between start times; null means back to back - data[].buffer_minutes (integer, required) - data[].min_notice_minutes (integer, required) - data[].max_advance_days (integer, required) - data[].max_periods (integer, required): Rentals: the most periods one booking can span - data[].active (boolean, required): false pauses booking: guests and agents can't book it, and it stays in your dashboard - data[].metadata (any, required): Free-form JSON object for your own integrations - data[].lat (number | null): The meeting point's latitude, for the weather call - data[].lon (number | null) - data[].gauge_site (string | null): A USGS river gauge's site number - data[].created_at (string, required): ISO 8601 timestamp (UTC) ### offerings_create: POST /offerings Create an offering. Only kind and name are required; everything else has defaults per kind. Appointments and rentals open daily 9:00–17:00 (org time) until you set hours; classes need sessions. Request: - name (body, string · ≤ 120 chars, required) - description (body, string · ≤ 2000 chars) - location (body, string · ≤ 200 chars) - duration_minutes (body, integer · 5–43200): Length of a class session or appointment, or of one rental period (1440 = a day) - capacity (body, integer · 1–10000): class: seats per session · appointment: parallel bookings per slot · rental: items in stock - max_per_booking (body, integer · 1–10000): Most seats, party size or items in one booking - price_cents (body, integer · ≥ 0): class: per seat · appointment: per booking · rental: per item per period - slot_interval_minutes (body, integer | null): Minutes between start times; null means back to back - buffer_minutes (body, integer · 0–1440): Gap kept free around appointments and rentals - min_notice_minutes (body, integer · 0–43200) - max_advance_days (body, integer · 1–730) - max_periods (body, integer · 1–60): Rentals: the most periods one booking can span - active (body, boolean): false pauses booking: guests and agents can't book it, and it stays in your dashboard - metadata (body, object): Free-form JSON for your own integrations - lat (body, number | null): The meeting point's latitude, for the ops agent's weather call - lon (body, number | null): The meeting point's longitude - gauge_site (body, string | null): A USGS river gauge's site number (e.g. 13022500), for a river trip's flow - kind (body, class | appointment | rental, required): class: scheduled sessions with seats · appointment: slots from weekly hours · rental: inventory over periods Response 201: JSON - id (string, required) - org_id (string, required) - kind (class | appointment | rental, required): class: scheduled sessions with seats · appointment: slots from weekly hours · rental: inventory over periods - name (string, required) - description (string, required) - location (string, required) - duration_minutes (integer, required): Length of a class session or appointment, or of one rental period (1440 = a day) - capacity (integer, required): class: seats per session · appointment: parallel bookings per slot · rental: items in stock - max_per_booking (integer, required): Most seats, party size or items in one booking - price_cents (integer, required): class: per seat · appointment: per booking · rental: per item per period - slot_interval_minutes (integer | null, required): Minutes between start times; null means back to back - buffer_minutes (integer, required) - min_notice_minutes (integer, required) - max_advance_days (integer, required) - max_periods (integer, required): Rentals: the most periods one booking can span - active (boolean, required): false pauses booking: guests and agents can't book it, and it stays in your dashboard - metadata (any, required): Free-form JSON object for your own integrations - lat (number | null): The meeting point's latitude, for the weather call - lon (number | null) - gauge_site (string | null): A USGS river gauge's site number - created_at (string, required): ISO 8601 timestamp (UTC) ### offerings_get: GET /offerings/{id} Get one offering Request: - id (path, string · uuid, required) Response 200: JSON - id (string, required) - org_id (string, required) - kind (class | appointment | rental, required): class: scheduled sessions with seats · appointment: slots from weekly hours · rental: inventory over periods - name (string, required) - description (string, required) - location (string, required) - duration_minutes (integer, required): Length of a class session or appointment, or of one rental period (1440 = a day) - capacity (integer, required): class: seats per session · appointment: parallel bookings per slot · rental: items in stock - max_per_booking (integer, required): Most seats, party size or items in one booking - price_cents (integer, required): class: per seat · appointment: per booking · rental: per item per period - slot_interval_minutes (integer | null, required): Minutes between start times; null means back to back - buffer_minutes (integer, required) - min_notice_minutes (integer, required) - max_advance_days (integer, required) - max_periods (integer, required): Rentals: the most periods one booking can span - active (boolean, required): false pauses booking: guests and agents can't book it, and it stays in your dashboard - metadata (any, required): Free-form JSON object for your own integrations - lat (number | null): The meeting point's latitude, for the weather call - lon (number | null) - gauge_site (string | null): A USGS river gauge's site number - created_at (string, required): ISO 8601 timestamp (UTC) ### offerings_update: PATCH /offerings/{id} Update an offering. Set active: false to pause booking; it stays in your dashboard. Request: - id (path, string · uuid, required) - name (body, string · ≤ 120 chars) - description (body, string · ≤ 2000 chars) - location (body, string · ≤ 200 chars) - duration_minutes (body, integer · 5–43200): Length of a class session or appointment, or of one rental period (1440 = a day) - capacity (body, integer · 1–10000): class: seats per session · appointment: parallel bookings per slot · rental: items in stock - max_per_booking (body, integer · 1–10000): Most seats, party size or items in one booking - price_cents (body, integer · ≥ 0): class: per seat · appointment: per booking · rental: per item per period - slot_interval_minutes (body, integer | null): Minutes between start times; null means back to back - buffer_minutes (body, integer · 0–1440): Gap kept free around appointments and rentals - min_notice_minutes (body, integer · 0–43200) - max_advance_days (body, integer · 1–730) - max_periods (body, integer · 1–60): Rentals: the most periods one booking can span - active (body, boolean): false pauses booking: guests and agents can't book it, and it stays in your dashboard - metadata (body, object): Free-form JSON for your own integrations - lat (body, number | null): The meeting point's latitude, for the ops agent's weather call - lon (body, number | null): The meeting point's longitude - gauge_site (body, string | null): A USGS river gauge's site number (e.g. 13022500), for a river trip's flow Response 200: JSON - id (string, required) - org_id (string, required) - kind (class | appointment | rental, required): class: scheduled sessions with seats · appointment: slots from weekly hours · rental: inventory over periods - name (string, required) - description (string, required) - location (string, required) - duration_minutes (integer, required): Length of a class session or appointment, or of one rental period (1440 = a day) - capacity (integer, required): class: seats per session · appointment: parallel bookings per slot · rental: items in stock - max_per_booking (integer, required): Most seats, party size or items in one booking - price_cents (integer, required): class: per seat · appointment: per booking · rental: per item per period - slot_interval_minutes (integer | null, required): Minutes between start times; null means back to back - buffer_minutes (integer, required) - min_notice_minutes (integer, required) - max_advance_days (integer, required) - max_periods (integer, required): Rentals: the most periods one booking can span - active (boolean, required): false pauses booking: guests and agents can't book it, and it stays in your dashboard - metadata (any, required): Free-form JSON object for your own integrations - lat (number | null): The meeting point's latitude, for the weather call - lon (number | null) - gauge_site (string | null): A USGS river gauge's site number - created_at (string, required): ISO 8601 timestamp (UTC) ### offerings_delete: DELETE /offerings/{id} Delete an offering. With upcoming bookings, pass cancel_bookings: true: they're cancelled (guests emailed, online payments refunded in full) and past bookings stay on record. Returns { cancelled, refunded }. Request: - id (path, string · uuid, required) - cancel_bookings (query, boolean): Required when the offering has upcoming bookings or live holds; otherwise the delete answers 409 with what it would cancel Response 200: JSON - cancelled (integer, required): Upcoming bookings and holds cancelled - refunded (integer, required): Of those, paid online and refunded in full ### offerings_get_hours: GET /offerings/{id}/hours Weekly opening hours of an appointment or rental, in the org's local time Request: - id (path, string · uuid, required) Response 200: JSON - data (object[], required) - data[].weekday (integer, required): 0 = Sunday … 6 = Saturday - data[].start_time (string, required): Local wall-clock time, HH:MM:SS - data[].end_time (string, required): Local wall-clock time, HH:MM:SS ### offerings_set_hours: PUT /offerings/{id}/hours Replace weekly opening hours, e.g. [{ weekday: 6, start_time: '08:00', end_time: '16:00' }] Request: - id (path, string · uuid, required) - hours (body, object[] · ≤ 50 items, required) - hours[].weekday (body, integer · 0–6, required): 0 = Sunday … 6 = Saturday - hours[].start_time (body, string, required) - hours[].end_time (body, string, required) Response 200: JSON - data (object[], required) - data[].weekday (integer, required): 0 = Sunday … 6 = Saturday - data[].start_time (string, required): Local wall-clock time, HH:MM:SS - data[].end_time (string, required): Local wall-clock time, HH:MM:SS ## Availability ### availability_get: GET /offerings/{id}/availability Bookable slots with remaining capacity between two local dates (default: the next 14 days) Request: - id (path, string · uuid, required) - from (query, string · date): Local calendar date (YYYY-MM-DD) in the org's time zone - to (query, string · date): Local calendar date (YYYY-MM-DD) in the org's time zone Response 200: JSON - timezone (string, required): The org's time zone; from and to are local dates in it - data (object[], required) - data[].starts_at (string, required): ISO 8601 timestamp (UTC) - data[].ends_at (string, required): ISO 8601 timestamp (UTC) - data[].capacity (integer, required) - data[].remaining (integer, required): What's left to book: seats, parallel slots or items - data[].session_id (string): Classes only: pass it to bookings_create ## Sessions ### sessions_list: GET /sessions List class sessions with the number of seats booked Request: - offering_id (query, string · uuid) - from (query, string): ISO 8601 instant, e.g. 2026-10-03T15:00:00Z - to (query, string): ISO 8601 instant, e.g. 2026-10-03T15:00:00Z - status (query, scheduled | cancelled) Response 200: JSON - data (object[], required) - data[].id (string, required) - data[].org_id (string, required) - data[].offering_id (string, required) - data[].starts_at (string, required): ISO 8601 timestamp (UTC) - data[].ends_at (string, required): ISO 8601 timestamp (UTC) - data[].capacity (integer | null, required): Seats for this session; null means the class's capacity - data[].status (scheduled | cancelled, required) - data[].created_at (string, required): ISO 8601 timestamp (UTC) - data[].booked (integer, required): Seats booked ### sessions_create: POST /sessions Schedule a class session; repeat_weeks repeats it weekly at the same local time Request: - offering_id (body, string · uuid, required) - starts_at (body, string, required): ISO 8601 instant, e.g. 2026-10-03T15:00:00Z - repeat_weeks (body, integer · 1–52) - capacity (body, integer | null): Override the class's seats Response 201: JSON - data (object[], required) - data[].id (string, required) - data[].org_id (string, required) - data[].offering_id (string, required) - data[].starts_at (string, required): ISO 8601 timestamp (UTC) - data[].ends_at (string, required): ISO 8601 timestamp (UTC) - data[].capacity (integer | null, required): Seats for this session; null means the class's capacity - data[].status (scheduled | cancelled, required) - data[].created_at (string, required): ISO 8601 timestamp (UTC) ### sessions_cancel: POST /sessions/{id}/cancel Cancel a class session and every booking in it Request: - id (path, string · uuid, required) Response 200: JSON - id (string, required) - org_id (string, required) - offering_id (string, required) - starts_at (string, required): ISO 8601 timestamp (UTC) - ends_at (string, required): ISO 8601 timestamp (UTC) - capacity (integer | null, required): Seats for this session; null means the class's capacity - status (scheduled | cancelled, required) - created_at (string, required): ISO 8601 timestamp (UTC) ## Bookings ### bookings_list: GET /bookings List bookings filtered by time range, status, offering, customer or session (order=desc for newest first) Request: - from (query, string): ISO 8601 instant, e.g. 2026-10-03T15:00:00Z - to (query, string): ISO 8601 instant, e.g. 2026-10-03T15:00:00Z - status (query, held | confirmed | cancelled) - offering_id (query, string · uuid) - customer_id (query, string · uuid) - session_id (query, string · uuid) - order (query, asc | desc) - limit (query, integer · 1–500) Response 200: JSON - data (object[], required) - data[].id (string, required) - data[].org_id (string, required) - data[].offering_id (string, required) - data[].session_id (string | null, required): Classes only - data[].customer_id (string, required) - data[].starts_at (string, required): ISO 8601 timestamp (UTC) - data[].ends_at (string, required): ISO 8601 timestamp (UTC) - data[].quantity (integer, required): class: seats · appointment: party size · rental: items - data[].total_cents (integer, required): In the org's currency - data[].reference (string, required): Short code the guest sees, e.g. 0GWYRZ2V - data[].status (held | confirmed | cancelled, required): held, confirmed or cancelled; a hold keeps its place until expires_at - data[].source (string, required): Where it was made: dashboard, api, mcp or widget - data[].notes (string, required) - data[].metadata (any, required): Free-form JSON object for your own integrations - data[].cancelled_at (string | null, required): ISO 8601 timestamp (UTC) - data[].created_at (string, required): ISO 8601 timestamp (UTC) - data[].expires_at (string | null, required): Held bookings: when the hold stops keeping its place - data[].handle (string, required): The guest's own page for the booking: /b/{handle} - data[].checkout_url (string | null, required): Stripe Checkout for a hold being paid online - data[].paid_at (string | null, required): When it was paid online - data[].payment_intent_id (string | null, required): The Stripe payment, when paid online - data[].previous (object): bookings.move: where the booking was - data[].previous.starts_at (string, required): ISO 8601 timestamp (UTC) - data[].previous.ends_at (string, required): ISO 8601 timestamp (UTC) - data[].previous.session_id (string | null, required) - data[].offering (object, required) - data[].offering.id (string, required) - data[].offering.name (string, required) - data[].offering.kind (class | appointment | rental, required): class: scheduled sessions with seats · appointment: slots from weekly hours · rental: inventory over periods - data[].customer (object, required) - data[].customer.id (string, required) - data[].customer.name (string, required) - data[].customer.email (string, required) - data[].customer.phone (string, required) ### bookings_get: GET /bookings/{id} Get one booking with its offering and customer Request: - id (path, string · uuid, required) Response 200: JSON - id (string, required) - org_id (string, required) - offering_id (string, required) - session_id (string | null, required): Classes only - customer_id (string, required) - starts_at (string, required): ISO 8601 timestamp (UTC) - ends_at (string, required): ISO 8601 timestamp (UTC) - quantity (integer, required): class: seats · appointment: party size · rental: items - total_cents (integer, required): In the org's currency - reference (string, required): Short code the guest sees, e.g. 0GWYRZ2V - status (held | confirmed | cancelled, required): held, confirmed or cancelled; a hold keeps its place until expires_at - source (string, required): Where it was made: dashboard, api, mcp or widget - notes (string, required) - metadata (any, required): Free-form JSON object for your own integrations - cancelled_at (string | null, required): ISO 8601 timestamp (UTC) - created_at (string, required): ISO 8601 timestamp (UTC) - expires_at (string | null, required): Held bookings: when the hold stops keeping its place - handle (string, required): The guest's own page for the booking: /b/{handle} - checkout_url (string | null, required): Stripe Checkout for a hold being paid online - paid_at (string | null, required): When it was paid online - payment_intent_id (string | null, required): The Stripe payment, when paid online - previous (object): bookings.move: where the booking was - previous.starts_at (string, required): ISO 8601 timestamp (UTC) - previous.ends_at (string, required): ISO 8601 timestamp (UTC) - previous.session_id (string | null, required) - offering (object, required) - offering.id (string, required) - offering.name (string, required) - offering.kind (class | appointment | rental, required): class: scheduled sessions with seats · appointment: slots from weekly hours · rental: inventory over periods - customer (object, required) - customer.id (string, required) - customer.name (string, required) - customer.email (string, required) - customer.phone (string, required) ### bookings_create: POST /bookings Book a slot from availability.get (classes need session_id; appointments and rentals need starts_at). Pass hold_minutes to hold it instead, and idempotency_key so retries never book twice. Request: - offering_id (body, string · uuid, required) - session_id (body, string · uuid) - starts_at (body, string): ISO 8601 instant, e.g. 2026-10-03T15:00:00Z - quantity (body, integer · 1–10000): class: seats · appointment: party size · rental: items - periods (body, integer · 1–60): Rentals: how many periods (e.g. days) - customer_id (body, string · uuid) - customer (body, object): Matched by email; created if new - customer.email (body, string · email, required) - customer.name (body, string · ≤ 120 chars) - customer.phone (body, string · ≤ 40 chars) - notes (body, string · ≤ 2000 chars) - hold_minutes (body, integer · 5–1440): Hold the place this many minutes (status held) instead of confirming; then call bookings.confirm - idempotency_key (body, string · uuid): A UUID you generate per booking: retrying with it returns the same booking instead of booking twice Response 201: JSON - id (string, required) - org_id (string, required) - offering_id (string, required) - session_id (string | null, required): Classes only - customer_id (string, required) - starts_at (string, required): ISO 8601 timestamp (UTC) - ends_at (string, required): ISO 8601 timestamp (UTC) - quantity (integer, required): class: seats · appointment: party size · rental: items - total_cents (integer, required): In the org's currency - reference (string, required): Short code the guest sees, e.g. 0GWYRZ2V - status (held | confirmed | cancelled, required): held, confirmed or cancelled; a hold keeps its place until expires_at - source (string, required): Where it was made: dashboard, api, mcp or widget - notes (string, required) - metadata (any, required): Free-form JSON object for your own integrations - cancelled_at (string | null, required): ISO 8601 timestamp (UTC) - created_at (string, required): ISO 8601 timestamp (UTC) - expires_at (string | null, required): Held bookings: when the hold stops keeping its place - handle (string, required): The guest's own page for the booking: /b/{handle} - checkout_url (string | null, required): Stripe Checkout for a hold being paid online - paid_at (string | null, required): When it was paid online - payment_intent_id (string | null, required): The Stripe payment, when paid online - previous (object): bookings.move: where the booking was - previous.starts_at (string, required): ISO 8601 timestamp (UTC) - previous.ends_at (string, required): ISO 8601 timestamp (UTC) - previous.session_id (string | null, required) - offering (object, required) - offering.id (string, required) - offering.name (string, required) - offering.kind (class | appointment | rental, required): class: scheduled sessions with seats · appointment: slots from weekly hours · rental: inventory over periods - customer (object, required) - customer.id (string, required) - customer.name (string, required) - customer.email (string, required) - customer.phone (string, required) ### bookings_cancel: POST /bookings/{id}/cancel Cancel a booking or a hold and free its capacity (safe to repeat) Request: - id (path, string · uuid, required) Response 200: JSON - id (string, required) - org_id (string, required) - offering_id (string, required) - session_id (string | null, required): Classes only - customer_id (string, required) - starts_at (string, required): ISO 8601 timestamp (UTC) - ends_at (string, required): ISO 8601 timestamp (UTC) - quantity (integer, required): class: seats · appointment: party size · rental: items - total_cents (integer, required): In the org's currency - reference (string, required): Short code the guest sees, e.g. 0GWYRZ2V - status (held | confirmed | cancelled, required): held, confirmed or cancelled; a hold keeps its place until expires_at - source (string, required): Where it was made: dashboard, api, mcp or widget - notes (string, required) - metadata (any, required): Free-form JSON object for your own integrations - cancelled_at (string | null, required): ISO 8601 timestamp (UTC) - created_at (string, required): ISO 8601 timestamp (UTC) - expires_at (string | null, required): Held bookings: when the hold stops keeping its place - handle (string, required): The guest's own page for the booking: /b/{handle} - checkout_url (string | null, required): Stripe Checkout for a hold being paid online - paid_at (string | null, required): When it was paid online - payment_intent_id (string | null, required): The Stripe payment, when paid online - previous (object): bookings.move: where the booking was - previous.starts_at (string, required): ISO 8601 timestamp (UTC) - previous.ends_at (string, required): ISO 8601 timestamp (UTC) - previous.session_id (string | null, required) - offering (object, required) - offering.id (string, required) - offering.name (string, required) - offering.kind (class | appointment | rental, required): class: scheduled sessions with seats · appointment: slots from weekly hours · rental: inventory over periods - customer (object, required) - customer.id (string, required) - customer.name (string, required) - customer.email (string, required) - customer.phone (string, required) ### bookings_confirm: POST /bookings/{id}/confirm Confirm a held booking (safe to repeat). A hold past its expires_at confirms only if its place is still free. Request: - id (path, string · uuid, required) Response 200: JSON - id (string, required) - org_id (string, required) - offering_id (string, required) - session_id (string | null, required): Classes only - customer_id (string, required) - starts_at (string, required): ISO 8601 timestamp (UTC) - ends_at (string, required): ISO 8601 timestamp (UTC) - quantity (integer, required): class: seats · appointment: party size · rental: items - total_cents (integer, required): In the org's currency - reference (string, required): Short code the guest sees, e.g. 0GWYRZ2V - status (held | confirmed | cancelled, required): held, confirmed or cancelled; a hold keeps its place until expires_at - source (string, required): Where it was made: dashboard, api, mcp or widget - notes (string, required) - metadata (any, required): Free-form JSON object for your own integrations - cancelled_at (string | null, required): ISO 8601 timestamp (UTC) - created_at (string, required): ISO 8601 timestamp (UTC) - expires_at (string | null, required): Held bookings: when the hold stops keeping its place - handle (string, required): The guest's own page for the booking: /b/{handle} - checkout_url (string | null, required): Stripe Checkout for a hold being paid online - paid_at (string | null, required): When it was paid online - payment_intent_id (string | null, required): The Stripe payment, when paid online - previous (object): bookings.move: where the booking was - previous.starts_at (string, required): ISO 8601 timestamp (UTC) - previous.ends_at (string, required): ISO 8601 timestamp (UTC) - previous.session_id (string | null, required) - offering (object, required) - offering.id (string, required) - offering.name (string, required) - offering.kind (class | appointment | rental, required): class: scheduled sessions with seats · appointment: slots from weekly hours · rental: inventory over periods - customer (object, required) - customer.id (string, required) - customer.name (string, required) - customer.email (string, required) - customer.phone (string, required) ### bookings_move: POST /bookings/{id}/move Move a booking or a hold to another time of the same offering (classes: session_id; appointments and rentals: starts_at from availability.get). It keeps its reference, price and payment, the guest gets one email, and moving it to its current time changes nothing. Request: - id (path, string · uuid, required) - session_id (body, string · uuid): Classes: the session to move to - starts_at (body, string): Appointments and rentals: the new start; the booking keeps its length Response 200: JSON - id (string, required) - org_id (string, required) - offering_id (string, required) - session_id (string | null, required): Classes only - customer_id (string, required) - starts_at (string, required): ISO 8601 timestamp (UTC) - ends_at (string, required): ISO 8601 timestamp (UTC) - quantity (integer, required): class: seats · appointment: party size · rental: items - total_cents (integer, required): In the org's currency - reference (string, required): Short code the guest sees, e.g. 0GWYRZ2V - status (held | confirmed | cancelled, required): held, confirmed or cancelled; a hold keeps its place until expires_at - source (string, required): Where it was made: dashboard, api, mcp or widget - notes (string, required) - metadata (any, required): Free-form JSON object for your own integrations - cancelled_at (string | null, required): ISO 8601 timestamp (UTC) - created_at (string, required): ISO 8601 timestamp (UTC) - expires_at (string | null, required): Held bookings: when the hold stops keeping its place - handle (string, required): The guest's own page for the booking: /b/{handle} - checkout_url (string | null, required): Stripe Checkout for a hold being paid online - paid_at (string | null, required): When it was paid online - payment_intent_id (string | null, required): The Stripe payment, when paid online - previous (object): bookings.move: where the booking was - previous.starts_at (string, required): ISO 8601 timestamp (UTC) - previous.ends_at (string, required): ISO 8601 timestamp (UTC) - previous.session_id (string | null, required) - offering (object, required) - offering.id (string, required) - offering.name (string, required) - offering.kind (class | appointment | rental, required): class: scheduled sessions with seats · appointment: slots from weekly hours · rental: inventory over periods - customer (object, required) - customer.id (string, required) - customer.name (string, required) - customer.email (string, required) - customer.phone (string, required) ### bookings_resend: POST /bookings/{id}/resend Email the guest their booking confirmation again, at most once a day; not for held or cancelled bookings Request: - id (path, string · uuid, required) Response 200: JSON - ok (true, required) ## Conversations ### conversations_list: GET /conversations List conversations with guests, the ones waiting on a reply first, each with its guest, last message and booking chip (filter by status, unread, a search q, or customer_id for one guest's) Request: - status (query, open | done) - unread (query, boolean) - q (query, string · ≤ 200 chars): Matches the guest's name or email, or words in the messages - customer_id (query, string · uuid) - limit (query, integer · 1–500) Response 200: JSON - data (object[], required) - data[].id (string, required) - data[].status (open | done, required) - data[].last_message_at (string, required): ISO 8601 timestamp (UTC) - data[].outfitter_read_at (string | null, required): ISO 8601 timestamp (UTC) - data[].guest_read_at (string | null, required): ISO 8601 timestamp (UTC) - data[].snoozed_until (string | null, required): ISO 8601 timestamp (UTC) - data[].created_at (string, required): ISO 8601 timestamp (UTC) - data[].unread (boolean, required): A guest message is newer than outfitter_read_at - data[].needs_reply (boolean, required): The guest wrote last - data[].customer (object | null, required): Null on a pre-sale question - data[].last_message (object | null, required) - data[].booking (object | null, required): The guest's next upcoming booking, else their latest ### conversations_get: GET /conversations/{id} Get a conversation with its thread oldest first: messages, notes, and the guest's booking events as system rows Request: - id (path, string · uuid, required) Response 200: JSON - id (string, required) - status (open | done, required) - last_message_at (string, required): ISO 8601 timestamp (UTC) - outfitter_read_at (string | null, required): ISO 8601 timestamp (UTC) - guest_read_at (string | null, required): ISO 8601 timestamp (UTC) - snoozed_until (string | null, required): ISO 8601 timestamp (UTC) - created_at (string, required): ISO 8601 timestamp (UTC) - unread (boolean, required): A guest message is newer than outfitter_read_at - needs_reply (boolean, required): The guest wrote last - customer (object | null, required): Null on a pre-sale question - last_message (object | null, required) - booking (object | null, required): The guest's next upcoming booking, else their latest - thread (object | object[], required) ### conversations_update: PATCH /conversations/{id} Mark a conversation done or open again; read: true once the guest's messages have been seen Request: - id (path, string · uuid, required) - status (body, open | done) - read (body, boolean) Response 200: JSON - id (string, required) - status (open | done, required) - last_message_at (string, required): ISO 8601 timestamp (UTC) - outfitter_read_at (string | null, required): ISO 8601 timestamp (UTC) - guest_read_at (string | null, required): ISO 8601 timestamp (UTC) - snoozed_until (string | null, required): ISO 8601 timestamp (UTC) - created_at (string, required): ISO 8601 timestamp (UTC) - unread (boolean, required): A guest message is newer than outfitter_read_at - needs_reply (boolean, required): The guest wrote last - customer (object | null, required): Null on a pre-sale question - last_message (object | null, required) - booking (object | null, required): The guest's next upcoming booking, else their latest ## Messages ### messages_list: GET /messages List messages with guests, both ways and oldest first: a conversation's, a booking's (booking_id) or a guest's (customer_id) Request: - conversation_id (query, string · uuid) - booking_id (query, string · uuid) - customer_id (query, string · uuid) - limit (query, integer · 1–500) Response 200: JSON - data (object[], required) - data[].id (string, required) - data[].org_id (string, required) - data[].conversation_id (string | null, required) - data[].booking_id (string | null, required): The booking it's about, shown as its chip; null when about the guest in general - data[].customer_id (string | null, required): Null on a pre-sale question - data[].direction (to_guest | to_outfitter, required): to_guest: the outfitter wrote · to_outfitter: the guest wrote from their booking page or replied by email - data[].subject (string, required): Empty on guest messages - data[].body (string, required) - data[].sent_by (dashboard | api | mcp | guest | agent, required): agent: the ops agent wrote it (on the system channel) - data[].channel (string, required): How it travelled: page (typed in daybag), email, api or system - data[].kind (message | note, required): A note is the outfitter's own, never sent to the guest - data[].read_at (string | null, required): When the other side read it - data[].attachments (object[], required): Files on it, each opened at /api/attachments/{id} - data[].attachments[].id (string, required) - data[].attachments[].name (string, required) - data[].attachments[].size (integer, required) - data[].attachments[].type (string, required) - data[].delivered_at (string | null, required): Messages to guests: when the guest's mail server took the email - data[].failed_at (string | null, required): Messages to guests: when the email bounced or was marked as spam - data[].from_name (string | null, required): Guest messages: the name they gave - data[].from_email (string | null, required): Guest messages: the email they gave, where replies go - data[].created_at (string, required): ISO 8601 timestamp (UTC) - data[].booking (object | null, required) ### messages_send: POST /messages Write to a guest by conversation_id, booking_id or customer_id (not about a held booking); they get it by email and their reply reaches the outfitter. kind note keeps an internal note instead Request: - conversation_id (body, string · uuid) - booking_id (body, string · uuid): The booking it's about; it shows as the message's chip - customer_id (body, string · uuid) - subject (body, string · ≤ 200 chars) - body (body, string · ≤ 5000 chars, required) - kind (body, message | note): note: for the outfitter's team, never sent - attachments (body, object[] · ≤ 5 items): Files uploaded with the attachment picker ({ id, name }), up to 5: photos (JPEG, PNG, WebP, HEIC) or PDFs, 10 MB each - attachments[].id (body, string, required) - attachments[].name (body, string · ≤ 1000 chars, required) Response 201: JSON - id (string, required) - org_id (string, required) - conversation_id (string | null, required) - booking_id (string | null, required): The booking it's about, shown as its chip; null when about the guest in general - customer_id (string | null, required): Null on a pre-sale question - direction (to_guest | to_outfitter, required): to_guest: the outfitter wrote · to_outfitter: the guest wrote from their booking page or replied by email - subject (string, required): Empty on guest messages - body (string, required) - sent_by (dashboard | api | mcp | guest | agent, required): agent: the ops agent wrote it (on the system channel) - channel (string, required): How it travelled: page (typed in daybag), email, api or system - kind (message | note, required): A note is the outfitter's own, never sent to the guest - read_at (string | null, required): When the other side read it - attachments (object[], required): Files on it, each opened at /api/attachments/{id} - attachments[].id (string, required) - attachments[].name (string, required) - attachments[].size (integer, required) - attachments[].type (string, required) - delivered_at (string | null, required): Messages to guests: when the guest's mail server took the email - failed_at (string | null, required): Messages to guests: when the email bounced or was marked as spam - from_name (string | null, required): Guest messages: the name they gave - from_email (string | null, required): Guest messages: the email they gave, where replies go - created_at (string, required): ISO 8601 timestamp (UTC) ### messages_broadcast: POST /messages/broadcast Write to many guests, once each: everyone confirmed in a class session (target.session_id), starting on a local date across offerings (target.date), or booked ahead on an offering (target.offering_id). Returns how many were sent, skipped (erased guests) and failed Request: - target (body, object | object | object, required): { session_id }, { date } or { offering_id } - subject (body, string · ≤ 200 chars) - body (body, string · ≤ 5000 chars, required) Response 200: JSON - sent (integer, required) - skipped (integer, required) - failed (integer, required) ### messages_draft: POST /messages/draft Draft a reply in a conversation for the outfitter to edit and send; nothing is sent. Written from the last 10 messages and the guest's latest booking. 409 drafts_unavailable when drafting is off; 60 drafts per org in any 24 hours Request: - conversation_id (body, string · uuid, required): The conversation to draft a reply in Response 200: JSON - text (string, required): The drafted reply, plain text, to review and edit before sending ## Saved replies ### saved_replies_list: GET /saved_replies List the outfitter's saved replies by title. With booking_id, or conversation_id (its guest and their booking under way or next), each comes with text: its body filled in, ready to send with messages_send Request: - booking_id (query, string · uuid): Fill the replies in for this booking and its guest - conversation_id (query, string · uuid): Or for this conversation's guest and their booking under way or next Response 200: JSON - data (object[], required) - data[].id (string, required) - data[].org_id (string, required) - data[].title (string, required): What the outfitter picks it by - data[].body (string, required): The message as saved, with its {variables} - data[].created_at (string, required): ISO 8601 timestamp (UTC) - data[].updated_at (string, required): ISO 8601 timestamp (UTC) - data[].text (string, required): The body filled in for booking_id or conversation_id, ready for messages_send; without them only {org} is filled in ### saved_replies_create: POST /saved_replies Save a reply the outfitter can reuse. Its body can use {first_name}, {start_time}, {offering}, {meeting_point}, {org}, {booking_link}, which saved_replies_list fills in for a guest Request: - title (body, string · ≤ 80 chars, required): What it's picked by, such as Directions - body (body, string · ≤ 5000 chars, required): The message. {first_name}, {start_time}, {offering}, {meeting_point}, {org}, {booking_link} are filled in for a guest when it's used Response 201: JSON - id (string, required) - org_id (string, required) - title (string, required): What the outfitter picks it by - body (string, required): The message as saved, with its {variables} - created_at (string, required): ISO 8601 timestamp (UTC) - updated_at (string, required): ISO 8601 timestamp (UTC) ### saved_replies_update: PATCH /saved_replies/{id} Change a saved reply's title or body Request: - id (path, string · uuid, required) - title (body, string · ≤ 80 chars): What it's picked by, such as Directions - body (body, string · ≤ 5000 chars): The message. {first_name}, {start_time}, {offering}, {meeting_point}, {org}, {booking_link} are filled in for a guest when it's used Response 200: JSON - id (string, required) - org_id (string, required) - title (string, required): What the outfitter picks it by - body (string, required): The message as saved, with its {variables} - created_at (string, required): ISO 8601 timestamp (UTC) - updated_at (string, required): ISO 8601 timestamp (UTC) ### saved_replies_delete: DELETE /saved_replies/{id} Delete a saved reply Request: - id (path, string · uuid, required) Response 200: JSON - ok (true, required) ## Automations ### automations_list: GET /automations The ops agent's four automations (weather, fill, brief, reminders), each with its dial (off, ask, do_tell, do_quiet) and settings. Its guardrails (quiet hours, daily cap, refund ceiling) are on org_get Response 200: JSON - data (object[], required) - data[].automation (weather | fill | brief | reminders, required): weather: call off or move trips over a forecast · fill: open or fill sessions · brief: the morning brief · reminders: before and after each booking - data[].dial (off | ask | do_tell | do_quiet, required): off · ask: propose in the Inbox and wait · do_tell: act, then a card and a push · do_quiet: act and log - data[].settings (object, required): weather: { offerings: { : limits }, default: limits } · fill: { full_pct (50–100, default 80): open the next day's session past this, min_guests (null = off), min_guests_hours (default 48) } · brief: { see_you (default true): offer a note to everyone booked today } · reminders: { before_24h, before_2h, after_6h (needs the org's review_url), before_24h_reply_id (a saved reply sent the day before; null = ours) } - data[].updated_at (string | null, required): null until it's first set ### automations_update: PATCH /automations/{automation} Turn an automation's dial, or change its settings: the fields given replace those set Request: - automation (path, weather | fill | brief | reminders, required): weather: call off or move trips over a forecast · fill: open or fill sessions · brief: the morning brief · reminders: before and after each booking - dial (body, off | ask | do_tell | do_quiet): off · ask: propose in the Inbox and wait · do_tell: act, then a card and a push · do_quiet: act and log - settings (body, object): weather: { offerings: { : limits }, default: limits } · fill: { full_pct (50–100, default 80): open the next day's session past this, min_guests (null = off), min_guests_hours (default 48) } · brief: { see_you (default true): offer a note to everyone booked today } · reminders: { before_24h, before_2h, after_6h (needs the org's review_url), before_24h_reply_id (a saved reply sent the day before; null = ours) } Response 200: JSON - automation (weather | fill | brief | reminders, required): weather: call off or move trips over a forecast · fill: open or fill sessions · brief: the morning brief · reminders: before and after each booking - dial (off | ask | do_tell | do_quiet, required): off · ask: propose in the Inbox and wait · do_tell: act, then a card and a push · do_quiet: act and log - settings (object, required): weather: { offerings: { : limits }, default: limits } · fill: { full_pct (50–100, default 80): open the next day's session past this, min_guests (null = off), min_guests_hours (default 48) } · brief: { see_you (default true): offer a note to everyone booked today } · reminders: { before_24h, before_2h, after_6h (needs the org's review_url), before_24h_reply_id (a saved reply sent the day before; null = ours) } - updated_at (string | null, required): null until it's first set ## Proposals ### proposals_list: GET /proposals What the ops agent proposed or did, newest first. A pending one waits for proposals_approve (which runs its actions) or proposals_dismiss Request: - status (query, pending | done | failed | dismissed | expired | undone) - automation (query, weather | fill | brief | reminders): weather: call off or move trips over a forecast · fill: open or fill sessions · brief: the morning brief · reminders: before and after each booking - limit (query, integer · 1–500) Response 200: JSON - data (object[], required) - data[].id (string, required) - data[].automation (weather | fill | brief | reminders, required): weather: call off or move trips over a forecast · fill: open or fill sessions · brief: the morning brief · reminders: before and after each booking - data[].status (pending | done | failed | dismissed | expired | undone, required): pending waits for proposals_approve or proposals_dismiss; approving runs it, so it lands on done or failed - data[].dial (ask | do_tell | do_quiet, required): ask: it waited for a person · do_tell, do_quiet: the agent acted on its own - data[].title (string, required) - data[].day (string, required): The local date it's for - data[].session_id (string | null, required) - data[].booking_id (string | null, required) - data[].data (object, required): The brief's trips, unanswered threads and weather; held: "cap" or "money" when the agent waited instead of acting - data[].actions (object[], required): Registry operations, run in order as the agent - data[].actions[].kind (cancel_session | cancel_booking | move_booking | create_session | message | remind, required) - data[].actions[].op (string, required): The operation it runs, e.g. sessions.create - data[].actions[].input (object, required): The operation's input - data[].actions[].facts (object, required): What it's about and why: the session or booking, the reason, the refund, the drafted words - data[].actions[].money (boolean): It refunds - data[].actions[].reversible (boolean): Once done, proposals_undo can reverse it - data[].actions[].result (object) - data[].actions[].result.ok (boolean, required) - data[].actions[].result.data (any) - data[].actions[].result.error (string) - data[].actions[].undo (object) - data[].actions[].undo.op (string, required) - data[].actions[].undo.input (object, required) - data[].money (boolean, required): An action refunds - data[].reversible (boolean, required): Done, and proposals_undo can reverse it - data[].expires_at (string | null, required): ISO 8601 timestamp (UTC) - data[].created_at (string, required): ISO 8601 timestamp (UTC) - data[].decided_at (string | null, required): ISO 8601 timestamp (UTC) - data[].decided_by (string | null, required): dashboard, api, mcp or agent - data[].done_at (string | null, required): ISO 8601 timestamp (UTC) - data[].error (string | null, required) ### proposals_approve: POST /proposals/{id}/approve Approve a pending proposal: its actions run now, in order, as the agent, stopping at the first that fails. draft rewords its message first Request: - id (path, string · uuid, required) - draft (body, object): Replaces the words of its first message or reminder - draft.subject (body, string · ≤ 200 chars) - draft.body (body, string · ≤ 5000 chars, required) Response 200: JSON - id (string, required) - automation (weather | fill | brief | reminders, required): weather: call off or move trips over a forecast · fill: open or fill sessions · brief: the morning brief · reminders: before and after each booking - status (pending | done | failed | dismissed | expired | undone, required): pending waits for proposals_approve or proposals_dismiss; approving runs it, so it lands on done or failed - dial (ask | do_tell | do_quiet, required): ask: it waited for a person · do_tell, do_quiet: the agent acted on its own - title (string, required) - day (string, required): The local date it's for - session_id (string | null, required) - booking_id (string | null, required) - data (object, required): The brief's trips, unanswered threads and weather; held: "cap" or "money" when the agent waited instead of acting - actions (object[], required): Registry operations, run in order as the agent - actions[].kind (cancel_session | cancel_booking | move_booking | create_session | message | remind, required) - actions[].op (string, required): The operation it runs, e.g. sessions.create - actions[].input (object, required): The operation's input - actions[].facts (object, required): What it's about and why: the session or booking, the reason, the refund, the drafted words - actions[].money (boolean): It refunds - actions[].reversible (boolean): Once done, proposals_undo can reverse it - actions[].result (object) - actions[].result.ok (boolean, required) - actions[].result.data (any) - actions[].result.error (string) - actions[].undo (object) - actions[].undo.op (string, required) - actions[].undo.input (object, required) - money (boolean, required): An action refunds - reversible (boolean, required): Done, and proposals_undo can reverse it - expires_at (string | null, required): ISO 8601 timestamp (UTC) - created_at (string, required): ISO 8601 timestamp (UTC) - decided_at (string | null, required): ISO 8601 timestamp (UTC) - decided_by (string | null, required): dashboard, api, mcp or agent - done_at (string | null, required): ISO 8601 timestamp (UTC) - error (string | null, required) ### proposals_dismiss: POST /proposals/{id}/dismiss Dismiss a pending proposal; nothing runs Request: - id (path, string · uuid, required) Response 200: JSON - id (string, required) - automation (weather | fill | brief | reminders, required): weather: call off or move trips over a forecast · fill: open or fill sessions · brief: the morning brief · reminders: before and after each booking - status (pending | done | failed | dismissed | expired | undone, required): pending waits for proposals_approve or proposals_dismiss; approving runs it, so it lands on done or failed - dial (ask | do_tell | do_quiet, required): ask: it waited for a person · do_tell, do_quiet: the agent acted on its own - title (string, required) - day (string, required): The local date it's for - session_id (string | null, required) - booking_id (string | null, required) - data (object, required): The brief's trips, unanswered threads and weather; held: "cap" or "money" when the agent waited instead of acting - actions (object[], required): Registry operations, run in order as the agent - actions[].kind (cancel_session | cancel_booking | move_booking | create_session | message | remind, required) - actions[].op (string, required): The operation it runs, e.g. sessions.create - actions[].input (object, required): The operation's input - actions[].facts (object, required): What it's about and why: the session or booking, the reason, the refund, the drafted words - actions[].money (boolean): It refunds - actions[].reversible (boolean): Once done, proposals_undo can reverse it - actions[].result (object) - actions[].result.ok (boolean, required) - actions[].result.data (any) - actions[].result.error (string) - actions[].undo (object) - actions[].undo.op (string, required) - actions[].undo.input (object, required) - money (boolean, required): An action refunds - reversible (boolean, required): Done, and proposals_undo can reverse it - expires_at (string | null, required): ISO 8601 timestamp (UTC) - created_at (string, required): ISO 8601 timestamp (UTC) - decided_at (string | null, required): ISO 8601 timestamp (UTC) - decided_by (string | null, required): dashboard, api, mcp or agent - done_at (string | null, required): ISO 8601 timestamp (UTC) - error (string | null, required) ### proposals_undo: POST /proposals/{id}/undo Undo a done proposal that opened a session: the session is cancelled, while nobody has booked it Request: - id (path, string · uuid, required) Response 200: JSON - id (string, required) - automation (weather | fill | brief | reminders, required): weather: call off or move trips over a forecast · fill: open or fill sessions · brief: the morning brief · reminders: before and after each booking - status (pending | done | failed | dismissed | expired | undone, required): pending waits for proposals_approve or proposals_dismiss; approving runs it, so it lands on done or failed - dial (ask | do_tell | do_quiet, required): ask: it waited for a person · do_tell, do_quiet: the agent acted on its own - title (string, required) - day (string, required): The local date it's for - session_id (string | null, required) - booking_id (string | null, required) - data (object, required): The brief's trips, unanswered threads and weather; held: "cap" or "money" when the agent waited instead of acting - actions (object[], required): Registry operations, run in order as the agent - actions[].kind (cancel_session | cancel_booking | move_booking | create_session | message | remind, required) - actions[].op (string, required): The operation it runs, e.g. sessions.create - actions[].input (object, required): The operation's input - actions[].facts (object, required): What it's about and why: the session or booking, the reason, the refund, the drafted words - actions[].money (boolean): It refunds - actions[].reversible (boolean): Once done, proposals_undo can reverse it - actions[].result (object) - actions[].result.ok (boolean, required) - actions[].result.data (any) - actions[].result.error (string) - actions[].undo (object) - actions[].undo.op (string, required) - actions[].undo.input (object, required) - money (boolean, required): An action refunds - reversible (boolean, required): Done, and proposals_undo can reverse it - expires_at (string | null, required): ISO 8601 timestamp (UTC) - created_at (string, required): ISO 8601 timestamp (UTC) - decided_at (string | null, required): ISO 8601 timestamp (UTC) - decided_by (string | null, required): dashboard, api, mcp or agent - done_at (string | null, required): ISO 8601 timestamp (UTC) - error (string | null, required) ## Customers ### customers_list: GET /customers List customers, newest first; q searches name, email and phone Request: - q (query, string · ≤ 100 chars) - limit (query, integer · 1–500) Response 200: JSON - data (object[], required) - data[].id (string, required) - data[].org_id (string, required) - data[].email (string, required): Lowercase; unique within the org - data[].name (string, required) - data[].phone (string, required) - data[].notes (string, required) - data[].metadata (any, required): Free-form JSON object for your own integrations - data[].created_at (string, required): ISO 8601 timestamp (UTC) ### customers_get: GET /customers/{id} Get a customer with their bookings Request: - id (path, string · uuid, required) Response 200: JSON - id (string, required) - org_id (string, required) - email (string, required): Lowercase; unique within the org - name (string, required) - phone (string, required) - notes (string, required) - metadata (any, required): Free-form JSON object for your own integrations - created_at (string, required): ISO 8601 timestamp (UTC) - bookings (object[], required) - bookings[].id (string, required) - bookings[].org_id (string, required) - bookings[].offering_id (string, required) - bookings[].session_id (string | null, required): Classes only - bookings[].customer_id (string, required) - bookings[].starts_at (string, required): ISO 8601 timestamp (UTC) - bookings[].ends_at (string, required): ISO 8601 timestamp (UTC) - bookings[].quantity (integer, required): class: seats · appointment: party size · rental: items - bookings[].total_cents (integer, required): In the org's currency - bookings[].reference (string, required): Short code the guest sees, e.g. 0GWYRZ2V - bookings[].status (held | confirmed | cancelled, required): held, confirmed or cancelled; a hold keeps its place until expires_at - bookings[].source (string, required): Where it was made: dashboard, api, mcp or widget - bookings[].notes (string, required) - bookings[].metadata (any, required): Free-form JSON object for your own integrations - bookings[].cancelled_at (string | null, required): ISO 8601 timestamp (UTC) - bookings[].created_at (string, required): ISO 8601 timestamp (UTC) - bookings[].expires_at (string | null, required): Held bookings: when the hold stops keeping its place - bookings[].handle (string, required): The guest's own page for the booking: /b/{handle} - bookings[].checkout_url (string | null, required): Stripe Checkout for a hold being paid online - bookings[].paid_at (string | null, required): When it was paid online - bookings[].payment_intent_id (string | null, required): The Stripe payment, when paid online - bookings[].previous (object): bookings.move: where the booking was - bookings[].previous.starts_at (string, required): ISO 8601 timestamp (UTC) - bookings[].previous.ends_at (string, required): ISO 8601 timestamp (UTC) - bookings[].previous.session_id (string | null, required) - bookings[].offering (object, required) - bookings[].offering.id (string, required) - bookings[].offering.name (string, required) - bookings[].offering.kind (class | appointment | rental, required): class: scheduled sessions with seats · appointment: slots from weekly hours · rental: inventory over periods - bookings[].customer (object, required) - bookings[].customer.id (string, required) - bookings[].customer.name (string, required) - bookings[].customer.email (string, required) - bookings[].customer.phone (string, required) ### customers_upsert: POST /customers Create a customer, or update the given fields when the email already exists Request: - name (body, string · ≤ 120 chars) - phone (body, string · ≤ 40 chars) - notes (body, string · ≤ 5000 chars) - metadata (body, object) - email (body, string · email, required) Response 200: JSON - id (string, required) - org_id (string, required) - email (string, required): Lowercase; unique within the org - name (string, required) - phone (string, required) - notes (string, required) - metadata (any, required): Free-form JSON object for your own integrations - created_at (string, required): ISO 8601 timestamp (UTC) ### customers_update: PATCH /customers/{id} Update a customer's name, phone, notes or metadata Request: - id (path, string · uuid, required) - name (body, string · ≤ 120 chars) - phone (body, string · ≤ 40 chars) - notes (body, string · ≤ 5000 chars) - metadata (body, object) Response 200: JSON - id (string, required) - org_id (string, required) - email (string, required): Lowercase; unique within the org - name (string, required) - phone (string, required) - notes (string, required) - metadata (any, required): Free-form JSON object for your own integrations - created_at (string, required): ISO 8601 timestamp (UTC) ### customers_delete: DELETE /customers/{id} Erase a customer's personal data (name, email, phone, notes) on request; their bookings stay, anonymized Request: - id (path, string · uuid, required) Response 200: JSON - ok (true, required) ## Org ### org_get: GET /org Your organization: name, slug (booking page /book/{slug}), time zone, currency and how guest messages are answered Response 200: JSON - id (string, required) - name (string, required) - slug (string, required): Booking page: /book/{slug} - timezone (string, required): IANA time zone, e.g. America/Denver. Weekly hours and dates are local to it. - currency (string, required): ISO 4217 code in lowercase, e.g. usd - reply_window (string | null, required): When guests can expect a reply, e.g. "We reply by 9 AM"; part of the acknowledgement - auto_ack (boolean, required): Emails a guest who writes "Thanks, we got your message", at most once a conversation a day - away_notice (string | null, required): While set: shown on the booking page and sent with the acknowledgement - review_url (string | null): Where guests leave a review; the ops agent's thank-you links it - brief_hour (integer): The local hour of the ops agent's morning brief - ops_daily_cap (integer): The most the ops agent does on its own in 24 hours; past it, it asks - quiet_from (integer | null): Quiet hours start (local hour): the agent sends guests nothing on its own until quiet_to - quiet_to (integer | null): Quiet hours end (local hour) - refund_ceiling_cents (integer | null): The ops agent refunds on its own up to this; null means a refund always waits for approval - created_at (string, required): ISO 8601 timestamp (UTC) ### org_update: PATCH /org Rename the organization, change its time zone (IANA, e.g. America/Denver) or currency, or set the reply window, auto-acknowledge and away notice guests see when they write Request: - name (body, string · ≤ 80 chars) - timezone (body, string · ≤ 64 chars) - currency (body, string) - reply_window (body, string | null): e.g. "We reply by 9 AM"; null or "" clears it - auto_ack (body, boolean): Email guests who write "Thanks, we got your message", at most once a conversation a day - away_notice (body, string | null): Shown on the booking page and sent with the acknowledgement while set; null or "" clears it - review_url (body, string | "" | null): Where guests leave a review (https), linked in the thank-you; null or "" clears it - brief_hour (body, integer · 0–23): The local hour of the morning brief - ops_daily_cap (body, integer · 1–500): The most the agent does on its own in 24 hours; past it, it asks - quiet_from (body, integer | null): Quiet hours start (local hour): the agent sends guests nothing on its own until quiet_to; null for none - quiet_to (body, integer | null): Quiet hours end (local hour) - refund_ceiling_cents (body, integer | null): The agent refunds on its own up to this; null means a refund always waits for approval Response 200: JSON - id (string, required) - name (string, required) - slug (string, required): Booking page: /book/{slug} - timezone (string, required): IANA time zone, e.g. America/Denver. Weekly hours and dates are local to it. - currency (string, required): ISO 4217 code in lowercase, e.g. usd - reply_window (string | null, required): When guests can expect a reply, e.g. "We reply by 9 AM"; part of the acknowledgement - auto_ack (boolean, required): Emails a guest who writes "Thanks, we got your message", at most once a conversation a day - away_notice (string | null, required): While set: shown on the booking page and sent with the acknowledgement - review_url (string | null): Where guests leave a review; the ops agent's thank-you links it - brief_hour (integer): The local hour of the ops agent's morning brief - ops_daily_cap (integer): The most the ops agent does on its own in 24 hours; past it, it asks - quiet_from (integer | null): Quiet hours start (local hour): the agent sends guests nothing on its own until quiet_to - quiet_to (integer | null): Quiet hours end (local hour) - refund_ceiling_cents (integer | null): The ops agent refunds on its own up to this; null means a refund always waits for approval - created_at (string, required): ISO 8601 timestamp (UTC) ### org_export: GET /org/export Export all your data as JSON: org, offerings, hours, sessions, customers, bookings, conversations, messages, saved replies, automations, proposals, webhooks and the last 1000 events Response 200: The org, offerings, hours, sessions, customers, bookings, conversations, messages, saved replies, automations, proposals, webhooks (no secrets) and the last 1000 events ## Events ### events_list: GET /events Everything that happened, oldest first. Poll with after= to follow along. Request: - after (query, integer · ≥ 0) - type (query, string · ≤ 60 chars): e.g. booking.confirmed - limit (query, integer · 1–500) Response 200: JSON - data (object[], required) - data[].id (integer, required): Increasing; poll with after= - data[].type (string, required): e.g. booking.confirmed, offering.updated - data[].data (any, required): The resource after the change: the booking, session, offering, customer or org - data[].created_at (string, required): ISO 8601 timestamp (UTC) - next_after (integer, required): Pass as after= to get what happened next ## Webhooks ### webhooks_list: GET /webhooks List webhook endpoints and their last delivery status Response 200: JSON - data (object[], required) - data[].id (string, required) - data[].url (string, required) - data[].events (string[], required): Event types it receives; ["*"] means all - data[].active (boolean, required) - data[].last_status (integer | null, required): HTTP status of the last delivery; 0 = unreachable - data[].last_attempt_at (string | null, required): ISO 8601 timestamp (UTC) - data[].created_at (string, required): ISO 8601 timestamp (UTC) ### webhooks_create: POST /webhooks Send events to an HTTPS URL, signed with HMAC-SHA256. Returns the signing secret once. Request: - url (body, string · uri, required) - events (body, string[] · ≤ 30 items): Event types, or ["*"] for all (default) Response 201: JSON - id (string, required) - url (string, required) - events (string[], required): Event types it receives; ["*"] means all - active (boolean, required) - last_status (integer | null, required): HTTP status of the last delivery; 0 = unreachable - last_attempt_at (string | null, required): ISO 8601 timestamp (UTC) - created_at (string, required): ISO 8601 timestamp (UTC) - secret (string, required): Verifies the Daybag-Signature header. Shown once. ### webhooks_delete: DELETE /webhooks/{id} Stop sending events to a webhook endpoint Request: - id (path, string · uuid, required) Response 200: JSON - ok (true, required) ## Billing ### billing_get: GET /billing Your plan (free or pro) and this month's confirmed bookings against the free plan's soft cap of 25 Response 200: JSON - plan (free | pro, required): pro: payment at booking, no daybag mark, no booking cap - usage (object, required) - usage.used (integer, required): Confirmed bookings created this UTC month - usage.cap (integer, required): The free plan's soft cap. Bookings keep confirming past it. - usage.pct (integer, required): used as a percentage of cap; keeps climbing past 100