API
29 operations. Every one is also an MCP tool.
REST
https://daybag.io/api/v1MCP
https://daybag.io/api/mcpAuth
Authorization: Bearer daybag_live_…Connect an agent
One line for Claude Code, one file for Cursor. Swap in a key from Dashboard → Developers.
claude code
claude mcp add --transport http daybag https://daybag.io/api/mcp --header "Authorization: Bearer daybag_live_…"cursor · .cursor/mcp.json
{
"mcpServers": {
"daybag": {
"type": "http",
"url": "https://daybag.io/api/mcp",
"headers": {
"Authorization": "Bearer daybag_live_…"
}
}
}
}claude.ai and ChatGPT connect once OAuth sign-in lands.
Quickstart
Three calls: find an offering, pick a slot, book it.
- 1Find an offeringofferings_listcurl
curl "https://daybag.io/api/v1/offerings" \ -H "Authorization: Bearer daybag_live_…"200response{ "data": [ { "id": "8f1c2e4a-0b7d-4c55-9a7e-3f2d1c0b9a88", "kind": "rental", "name": "Touring kayak", "duration_minutes": 1440, "capacity": 10, "max_per_booking": 4, "price_cents": 3500, "active": true } ] } - 2Pick a slotavailability_getcurl
curl "https://daybag.io/api/v1/offerings/8f1c2e4a-0b7d-4c55-9a7e-3f2d1c0b9a88/availability?from=2026-12-05&to=2026-12-05" \ -H "Authorization: Bearer daybag_live_…"200response{ "timezone": "America/Denver", "data": [ { "starts_at": "2026-12-05T15:00:00.000Z", "ends_at": "2026-12-06T15:00:00.000Z", "capacity": 10, "remaining": 8 } ] } - 3Book itbookings_createcurl
curl -X POST https://daybag.io/api/v1/bookings \ -H "Authorization: Bearer daybag_live_…" \ -H "Content-Type: application/json" \ -d '{ "offering_id": "8f1c2e4a-0b7d-4c55-9a7e-3f2d1c0b9a88", "starts_at": "2026-12-05T15:00:00Z", "quantity": 2, "customer": { "email": "pat@example.com", "name": "Pat" } }'201response{ "id": "1e2d3c4b-5a69-4788-9a6b-5c4d3e2f1a0b", "reference": "0GWYRZ2V", "status": "confirmed", "starts_at": "2026-12-05T15:00:00+00:00", "ends_at": "2026-12-06T15:00:00+00:00", "quantity": 2, "total_cents": 7000, "offering": { "id": "8f1c2e4a-0b7d-4c55-9a7e-3f2d1c0b9a88", "name": "Touring kayak", "kind": "rental", "location": "North dock", "duration_minutes": 1440 }, "customer": { "id": "5d4c3b2a-1f0e-4d9c-8b7a-6f5e4d3c2b1a", "name": "Pat", "email": "pat@example.com", "phone": "" } }
Widget
One tag on any site. The booking flow renders in place, or behind a floating button.
html
<script src="https://daybag.io/widget.js" data-org="your-slug" async></script>Options
data-orgYour booking page slug. Required.data-offeringOpen straight to one offering.data-modeinline (default) renders in place; button floats a pill that opens it.data-labelThe button's text. Default "Book now".data-targetCSS selector to render into, instead of next to the tag.After a booking
window event · daybag:booked
window.addEventListener("daybag:booked", (event) => {
const { org, reference } = event.detail; // e.g. "big-sky", "0GWYRZ2V"
});Events and webhooks
Every change is an event. Poll for them, or have them pushed to your server, signed.
booking.heldA seat is held while the guest pays; it expires at expires_atbooking.confirmedA booking is confirmed, from any surface and any pathbooking.cancelledA booking is cancelledsession.createdA class session is scheduledsession.cancelledA class session is cancelled, with its bookingsoffering.createdAn offering is addedoffering.updatedAn offering changes or is archivedoffering.deletedAn offering that was never booked is deletedoffering.hours_updatedWeekly hours are replacedcustomer.updatedA customer is added or editedcustomer.deletedA customer's personal data is erased on requestorg.updatedThe org's name, time zone or currency changesA paid booking is held first (booking.held) and confirmed when the payment lands; a free one confirms at once. Every event carries a source: dashboard, api, mcp, widget or stripe.
Poll
curl · then after=<next_after>
curl "https://daybag.io/api/v1/events?after=0" \
-H "Authorization: Bearer daybag_live_…"200response
{
"data": [
{
"id": 42,
"type": "booking.confirmed",
"data": {
"id": "1e2d3c4b-5a69-4788-9a6b-5c4d3e2f1a0b",
"reference": "0GWYRZ2V",
"status": "confirmed"
},
"created_at": "2026-12-01T17:04:11+00:00"
}
],
"next_after": 42
}Webhooks
Add an HTTPS endpoint with webhooks_create. Each event arrives as a JSON POST signed with the secret it returns once:
Daybag-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256(secret, "<t>.<raw body>")>- Keep the raw request body; verify before you parse it.
- Split the header at the commas: t is a unix time, v1 the signature.
- HMAC-SHA256 the string "<t>.<raw body>" with your webhook secret, hex encoded, and compare it to v1 in constant time.
- Reject a t more than five minutes away, so a captured request can't be replayed.
node
import { createHmac, timingSafeEqual } from "node:crypto";
// body: the raw request body · header: the Daybag-Signature header
function verify(secret, body, header) {
const { t, v1 = "" } = Object.fromEntries(header.split(",").map((part) => part.split("=")));
if (!t || Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // missing, stale or replayed
const expected = createHmac("sha256", secret).update(`${t}.${body}`).digest("hex");
return v1.length === expected.length && timingSafeEqual(Buffer.from(v1), Buffer.from(expected));
}Reference
offerings7
GET
/offeringsList offerings (classes, appointments and rentals), including archived ones
offerings_listPOST
/offeringsCreate 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.
offerings_createGET
/offerings/{id}Get one offering
offerings_getPATCH
/offerings/{id}Update an offering. Set active: false to archive it.
offerings_updateDELETE
/offerings/{id}Delete an offering that has never been booked (otherwise archive it)
offerings_deleteGET
/offerings/{id}/hoursWeekly opening hours of an appointment or rental, in the org's local time
offerings_get_hoursPUT
/offerings/{id}/hoursReplace weekly opening hours, e.g. [{ weekday: 6, start_time: '08:00', end_time: '16:00' }]
offerings_set_hoursavailability1
GET
/offerings/{id}/availabilityBookable slots with remaining capacity between two local dates (default: the next 14 days)
availability_getsessions3
GET
/sessionsList class sessions with the number of seats booked
sessions_listPOST
/sessionsSchedule a class session; repeat_weeks repeats it weekly at the same local time
sessions_createPOST
/sessions/{id}/cancelCancel a class session and every booking in it
sessions_cancelbookings5
GET
/bookingsList bookings filtered by time range, status, offering, customer or session (order=desc for newest first)
bookings_listGET
/bookings/{id}Get one booking with its offering and customer
bookings_getPOST
/bookingsBook 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.
bookings_createPOST
/bookings/{id}/cancelCancel a booking or a hold and free its capacity (safe to repeat)
bookings_cancelPOST
/bookings/{id}/confirmConfirm a held booking (safe to repeat). A hold past its expires_at confirms only if its place is still free.
bookings_confirmcustomers5
GET
/customersList customers, newest first; q searches name, email and phone
customers_listGET
/customers/{id}Get a customer with their bookings
customers_getPOST
/customersCreate a customer, or update the given fields when the email already exists
customers_upsertPATCH
/customers/{id}Update a customer's name, phone, notes or metadata
customers_updateDELETE
/customers/{id}Erase a customer's personal data (name, email, phone, notes) on request; their bookings stay, anonymized
customers_deleteorg3
GET
/orgYour organization: name, slug (booking page /book/{slug}), time zone and currency
org_getPATCH
/orgRename the organization or change its time zone (IANA, e.g. America/Denver) or currency
org_updateGET
/org/exportExport all your data as JSON: org, offerings, hours, sessions, customers, bookings, webhooks and the last 1000 events
org_exportevents1
GET
/eventsEverything that happened, oldest first. Poll with after=<next_after> to follow along.
events_listwebhooks3
GET
/webhooksList webhook endpoints and their last delivery status
webhooks_listPOST
/webhooksSend events to an HTTPS URL, signed with HMAC-SHA256. Returns the signing secret once.
webhooks_createDELETE
/webhooks/{id}Stop sending events to a webhook endpoint
webhooks_deletebilling1
GET
/billingYour plan (free or pro) and this month's confirmed bookings against the free plan's soft cap of 25
billing_get