Bookings inside your product.

coss.com Scheduling is a booking API for platforms. Providers set hours and connect calendars, customers book in your app, and overlaps are refused.

Read the docs

How it works

For marketplaces and other platforms whose providers get booked: add them as hosts, and their customers book them in your app.

// On your server: a provider, their hours and what can be booked.
await ...({
  : "tutor_482",
  : "Ana Reyes",
  : "ana@example.com",
  : "America/New_York",
});

await ...("tutor_482", {
  : [{ : [1, 2, 3, 4, 5], : "09:00", : "17:00" }],
});

const { :  } = await ...({
  : "Trial lesson",
  : 30,
  : 10,
  : [{ : "tutor_482" }],
});

// When a signed-in customer wants to book: a session for their browser.
const { :  } = await ...({
  : !.,
  : { : "customer_77", : "Sam Lee", : "sam@example.com", : "UTC" },
});
// Hand session.url to the browser.
// In the browser: the booker in an iframe on your own page.
({
  : ,
  ,
  : () => {
    if (. === "booked") .("Booked", .);
  },
});

Hosts get the same treatment: coss.scheduling.sessions.createForHost and mountHostPortal put their hours, calendars and bookings on a page in your app. To book from your own UI, list times with coss.scheduling.slots.list and book with coss.scheduling.bookings.create. See the Scheduling guide and the API reference.

A slot can't be taken twice

Double-booking is refused by the database, so it holds under races, retries and second tabs.

Every booking takes a claim on its host's time, with its buffers. A Postgres exclusion constraint, scheduling_claim_no_overlap, refuses any two claims of one host that overlap. It's the only enforcement: slot lists and re-checks just avoid asking a question it would refuse, so there's no gap between checking and writing for a booking to slip through.

When two people go for one time, one gets the booking and the other gets slot_unavailable, and you show them the times again. The same claim backs holds, pending approvals, series and group sessions. Every attempt the SDK retries carries the same Idempotency-Key, so a lost response can't book twice.

Events made outside coss.com are a different case. Something added to a host's calendar can still land on a booked time. The booking stands, coss.com never cancels on its own, and you get scheduling.booking.external_conflict.

From the team behind Cal.com

Scheduling is a hard problem we've spent years on.

coss.com Scheduling is built by the team behind Cal.com, on the same scheduling technology that has processed tens of millions of bookings there. Time zones, availability and calendar connections have already met the real world, and you get them through one API key alongside the rest of coss.com.

Everything a booking needs

Availability, calendars, the booking itself and telling people about it, on the key you already use for the rest of coss.com.

Calendars

Hosts connect Google, Microsoft or CalDAV calendars. Their busy times block slots, and bookings go on the calendar they choose. Changes arrive by push from Google and Microsoft, CalDAV is read every five minutes, and a calendar that can't be read never counts as free. See calendars.

An embeddable booker and host portal

mountBooker and mountHostPortal open coss.com's pages in an iframe in your app, under your brand name. Your API key stays on your server: a session is short-lived and reaches one thing, and only your allowed origins can embed it or call the client API. See embedding.

Your own UI, if you prefer

Every step is an API call, and a session's token also works from a browser against the client API. coss.com's own pages are built on that API, so nothing they do is out of your reach.

Holds and approvals

Hold a slot while you take payment with your own provider, then confirm it. Or set requires_approval: the time stays blocked until the host approves or declines, and a booking nobody answers is declined at its deadline. See bookings.

Pools and collective bookings

A pool books any available host, sharing bookings by weight, narrowed by attributes such as language. collective needs every required host free at once. availability.search answers who's free across up to 500 hosts, for a marketplace's search page. See pools and groups.

Group sessions

With kind: "group" and a capacity, each attendee books a seat in the same session, with a booking and manage link of their own. The host's time is taken by the first seat and given back with the last, and a full session answers slot_unavailable.

Recurring series

Book up to 52 weekly or monthly occurrences at the attendee's local time, so 10:00 stays 10:00 across a clock change. Preview first, then skip or refuse occurrences that are taken. Each is an ordinary booking that can move on its own.

Hours that hold up

Weekly hours in the host's time zone, date overrides, buffers, minimum notice, booking windows, and limits per host per day or week. Listing slots and booking both use one definition of what can be booked, so they can't disagree. See hosts and availability.

Who gets told

Attendees and hosts get emails with calendar invitations, from your own domain or from coss.com on your behalf. Attendees get a reminder a day before, by SMS too from your own number if you turn it on, and you can turn off any of them. Google Meet and Microsoft Teams links come with the calendar event. Bookings and calendars send webhooks and log lines. See notifications.

Built to hold little about people

Calendars, attendees and the events on your webhooks each carry as little as the job needs.

From calendars, coss.com stores when they're busy and nothing else: no titles, descriptions or attendees of other events. A booking goes on a host's calendar as a private event titled "Appointment", with no attendees, so their colleagues learn nothing from it.

Attendee contact details and answers are encrypted, and a booking only needs a time_zone, so you can use your own ids rather than names. Answers come back only when you retrieve one booking, never in lists, webhooks, logs, emails or calendars, and webhooks carry ids, times and statuses. coss.scheduling.attendees.erase removes someone's contact details and answers from their bookings right away.

Our Terms don't allow special categories of personal data or protected health information without a written agreement. See privacy for what's stored and for how long.

Specifications

The limits of a booking. The API reference lists each one with its field.

Calendars
Google, Microsoft, CalDAV
Event type kinds
one-on-one, pool, collective, group
Duration
5 minutes to 24 hours
Buffers
up to 8 hours before and after
Holds
10 minutes by default, 1 minute to 24 hours
Approval deadline
1 day by default, up to 30 days
Series
up to 52 occurrences, weekly or monthly
Group session
2 to 10,000 seats
Slots per request
up to 62 days
Availability search
up to 500 hosts
Calendars read ahead
400 days
Questions per event type
25
Host session
1 hour by default, at most 24 hours
Booker session
30 minutes by default, at most 24 hours
Embedding origins
up to 10, https
Attendee details kept
90 days after the booking ends
Bookings kept
395 days after the booking ends

Pricing

You pay per confirmed booking. Holds and pending approvals cost nothing until they're confirmed.

The Free plan includes 100 scheduling bookings a month. A group session is one booking however many attend, and each occurrence of a series is one. Rescheduling is free, and a cancelled booking stays charged. Booking emails sent from your own domain are billed like any email, and SMS reminders like any text message. See pricing for the plans.

Test it in a sandbox

Sandbox environments run the same code, including the double-booking guarantee and the hosted pages, and bill nothing.

With a sandbox (coss_test_…) key, emails go to the fake provider, so nobody's mailed, and the dashboard shows them with their links. Connect a simulated calendar and set its busy times with coss.scheduling.calendars.setSimulatedBusy, and test your handlers with coss trigger scheduling.booking.confirmed. See testing.

Questions

How do I add scheduling to my app?
Add each person who can be booked as a host with coss.scheduling.hosts.create(), give them a schedule, and create an event type. Your customers then book through the API, or in a booker you embed with mountBooker from coss/scheduling. Hosts set their hours and connect their calendars through your own UI or the host portal you embed with mountHostPortal.
How does coss.com prevent double-booking?
Every booking takes a claim on its host's time, buffers included, and a Postgres exclusion constraint refuses two claims of one host that overlap. That constraint is the enforcement, so two people clicking at once, a second tab or a retry can't both win. The one who loses gets slot_unavailable.
Which calendars can hosts connect?
Google and Microsoft through their consent screens, and CalDAV servers such as Apple's with an app-specific password. coss.com stores when each calendar is busy and adds bookings to the calendar the host chooses, as private events titled "Appointment" with no attendees. It stores no titles, descriptions or attendees of other events.
What happens if a calendar can't be read?
It never counts as free. By default, if access is lost the host stops being offered at once, you get a scheduling.calendar.needs_reauth webhook and the host gets an email with a link to reconnect. If a provider is having trouble, what's known keeps being used for up to seven hours, or twenty minutes for calendars without push notifications, then the host stops being offered until a read works.
How do I take payment before a booking is confirmed?
Hold the slot with coss.scheduling.holds.create(), charge the customer with your own payment provider, then confirm with coss.scheduling.holds.confirm(). A hold keeps the time for ten minutes by default, so nobody else can take it, and it tells nobody and isn't charged.
Do I need to build a booking page?
No. Your server creates a session for one event type, host or booking, and the page opens from its link or in an iframe in your app. If you'd rather build your own UI, the API and the browser client API do everything those pages do.
What counts as a billable booking?
One per confirmed booking, per occurrence of a series and per group session, however many people attend. Holds and pending approvals aren't charged, rescheduling is free and a cancelled booking stays charged. Sandbox environments are free, and the Free plan includes bookings every month. The pricing page has the rate.
On the same key, SDK, webhooks and logs: nothing more to set up.

Start with Scheduling today.

Development keys record every request and event and deliver nothing, so you can build and test before you verify a domain. Hand your coding agent this prompt:

Add coss.com to this project: read https://coss.com/llms.txt, get a development key with `npx coss-cli init --agents`, and build and test with it.

Pricing