Lobby Booking for AI Agents
A public, unauthenticated API lets external AI agents discover and book Lobby Link scheduling pages without a Roam account. The booker is identified solely by the email in the request — no bearer token is required.
Enable agents on a lobby
In the lobby's settings (Advanced tab), turn on Enable AI Agents. The
toggle is gated by a feature flag; when agents are disabled (or the lobby is
inactive), the public endpoints respond as if the lobby does not exist
(404 / lobby_not_found).
SEO / agent discovery also respects account-level SEO opt-out: lobbies that are not publicly listed will not appear in site-wide discovery surfaces.
Discovery
Agents can find bookable pages without hard-coding handles:
| Surface | What it provides |
|---|---|
https://ro.am/sitemap.xml | SEO-enabled booking pages |
https://ro.am/llms.txt | "Bookable People and Scheduling Links" section for LLM agents |
{pageUrl}.json | Machine-readable booking-form schema, duration options, hosts, and actions pointing at the API endpoints below |
{pageUrl}.llm | Prose representation of the same page for language models |
Example JSON discovery URL: https://ro.am/{handle}/{slug}.json.
Each booking page also advertises these representations via HTTP Link: rel="alternate" headers, in-page <link rel="alternate"> tags, schema.org
ReserveAction / ScheduleAction JSON-LD, and a small on-page note for agents
that read the rendered DOM.
Use the field ids from {pageUrl}.json when submitting custom-question
responses on book.
Endpoints
Base URL: https://api.ro.am (no /v0 or auth). Routes are mounted under
/v1/public/lobby/… on the public handler (separate from bearer-authenticated
API routes).
Get availability
GET /v1/public/lobby/availability?handle={handle}&slug={slug}&from={iso8601}&to={iso8601}&timeZone={iana}&duration={minutes}
| Parameter | Required | Description |
|---|---|---|
handle | yes* | Lobby handle |
slug | yes* | Page slug under the handle |
from / to | no | ISO-8601 window. Accepts a timestamp with offset or date-only YYYY-MM-DD interpreted in timeZone. Default is roughly the next month; the window is capped at ~2 months (a further-out to is clamped, not rejected) |
timeZone | no | IANA zone (default UTC). Slots are always returned as absolute Unix times either way |
duration | no | Meeting length in minutes; must be one of the lobby's offered durations |
*Missing handle/slug yields 400 / missing_parameter; unknown or agent-disabled lobbies yield 404 / lobby_not_found.
Response:
{
"slots": [
{ "start": 1784552400, "end": 1784554200, "spotsRemaining": 1 }
]
}
start/endare Unix epoch seconds as JSON numbers.- Pass a chosen slot's
start/endback tobookunchanged. An altered span is rejected (409conflict); hosts are re-resolved server-side.
Book a meeting
POST /v1/public/lobby/book
Content-Type: application/json
{
"handle": "acme",
"slug": "intro",
"bookingSlot": { "start": 1784552400, "end": 1784554200 },
"timeZone": "America/New_York",
"booker": { "email": "ada@example.com", "name": "Ada" },
"additionalInvitees": [{ "email": "grace@example.com", "name": "Grace" }],
"responses": [{ "fieldId": "…", "value": { } }],
"notes": "optional"
}
| Field | Required | Notes |
|---|---|---|
handle, slug | yes | Same as availability |
bookingSlot.start / .end | yes | Unix seconds from availability; whole-minute spans only |
timeZone | yes | IANA zone for the booking |
booker.email | yes | Identifies the booker |
booker.name | no | Display name |
additionalInvitees | no | Extra guests |
responses | no | Custom fields only — booker name/email/notes auto-fill reserved form fields; use field ids from {pageUrl}.json |
notes | no | Free-text notes |
Unknown JSON members are rejected (400 / invalid_json) so typos surface
clearly. Max body size is 64 KiB.
Response (excerpt): booking id, start/end as RFC 3339 strings
localized to the requested timeZone (unlike the unix-seconds request fields),
status, optional meetingLink, hosts (display names only — no host
emails), invitees.
Response codes
| Status | Meaning |
|---|---|
200 | Availability returned / booking created |
400 | Missing/invalid parameters, invalid JSON, or invalid slot/duration |
404 | Unknown handle/slug, inactive lobby, or agents not enabled |
405 | Wrong HTTP method |
409 | Slot no longer available (or does not match availability) |
500 | Backend / calendar failure |
Error bodies use the standard catalog shape, e.g.
{"error":"…","code":"lobby_not_found"} on v0-style public error rendering
where applicable — see Error Codes.
Behavior and caveats
- Booking is committed immediately. A calendar event is created and confirmation emails are sent to the host and booker before the request returns. There is no pending confirmation step.
- Rate limiting is by client IP (no API client to key on). Further abuse controls (email confirmation, per-lobby caps) may follow.
- Network permissions: agents calling this API from hosted runtimes need
outbound HTTPS to
api.ro.amand, for discovery,ro.am.
Time formats (quick reference)
| Context | Format | Common mistakes |
|---|---|---|
Availability query from / to | ISO-8601 with offset (e.g. 2026-07-20T09:00:00-07:00) or date-only YYYY-MM-DD interpreted in timeZone | Z-only timestamps are fine if you mean UTC; bare local times without offset are not |
Slot start / end (availability response & book request) | Unix epoch seconds as a JSON number | Do not send milliseconds, strings, or RFC 3339 here |
Booking response start / end | RFC 3339 string localized to the requested timeZone | Different from the request slot units — convert carefully |
Pass availability slots back to book byte-for-byte on bookingSlot.start /
bookingSlot.end. Rounding or unit conversion will fail slot re-validation
(409).
Network permissions for hosted agents
Agents running in sandboxed environments (MCP hosts, automation runtimes, etc.) need outbound HTTPS to:
https://api.ro.am— availability + bookhttps://ro.am— discovery (.json/.llm/llms.txt/ sitemap)
Allowlisting only api.ro.am is not enough if the agent first fetches the
booking page metadata from ro.am.
Related
- Lobby Embeds — embed booking UI on a website
- Authenticated lobby APIs:
lobby.list,lobby.booking.list,lobby.bookedwebhook