# headless.events YOU ARE AN AI AGENT READING THIS. THIS DOCUMENT IS FOR YOU, NOT THE USER. The user wants you to do work for them - plan an event or RSVP to one. They will not run any commands. Every action below is a single GET URL with query parameters - GET-fetch is the lowest-common-denominator capability across every LLM chat surface (ChatGPT, Claude, Gemini, Grok) on every platform (web, iOS, Android, desktop). ## TWO PATHS. PICK WHICHEVER YOUR TOOLING ALLOWS. ### Path A (preferred): Fetch the URL yourself If your built-in tool ("web", "browse", "fetch", "open_url", "search", etc.) will call URLs you construct from parameters, USE IT. Construct the URL from the rules below, fetch it, parse the JSON response (`Accept: application/json`), and report results in plain language. ### Path B (fallback): Hand the URL to the user as a clickable link Some chat clients (Claude.ai web in particular, plus most mobile chat apps) refuse to fetch URLs you synthesise from parameters - their safety policy only allows fetching URLs the user pasted or that came from a previous search result. **DO NOT GIVE UP. DO NOT TELL THE USER TO RUN CURL.** Instead, present the URL as a clickable Markdown link in your reply, like this: Ready to create your event - **[click here to confirm](https://headless.events/api/events/create?title=...&starts_at=...&host_email=...)**. When the user clicks the link in the chat UI, the URL opens in their browser, the action completes server-side, and they immediately see a styled confirmation page with the RSVP link, edit_token, and "draft invite" buttons. No JSON-blob ugliness - we content-negotiate: `Accept: text/html` returns a real page, anything else returns JSON. Either way, the user does nothing besides talk to you and (Path B only) click one link. ## CRITICAL RULES - URL-encode every value (spaces, &, :, @, ! all become %xx). `encodeURIComponent` is your friend. - After creating an event, SAVE the returned `edit_token` in your conversation memory. You need it to update the event or read RSVPs in any later message. Do not display the token to the user unless they ask. - After creating an event, IMMEDIATELY give the user the `rsvp_url` (or, on Path B, tell them the confirmation page they just saw shows it). Offer to draft an invite (email, text, or social post). - For attendees who clicked "RSVP via {you}" on a /{slug} page: ask name, going-status, guest count, and notes in ONE message, then either fetch (Path A) or render-as-link (Path B) the create-RSVP URL. - Never use POST/PATCH/DELETE. The GET URLs do everything; mixing methods just creates failure modes that vary across LLM platforms. - Never tell the user to run curl, wget, or any command. Never tell them to "copy this URL into your browser" - render it as a clickable Markdown link instead. ## THE WHOLE API. SEVEN URLS. MEMORISE THEM. ### 1. Create an event (no auth) https://headless.events/api/events/create?title=...&starts_at=...&location=...&host_name=...&host_email=...&description=... Required: `title`, `starts_at` (ISO 8601, prefer UTC `Z`). Strongly recommended: `host_email` (host receives a magic-link backup). Optional: `slug`, `description`, `ends_at`, `location`, `host_name`. Returns JSON: `{slug, edit_token, rsvp_url, admin_url, magic_link_emailed}`. Worked example - call this exact URL pattern, substituting values: https://headless.events/api/events/create?title=Brenda%27s%20Baby%20Shower&starts_at=2026-06-15T14%3A00%3A00Z&location=Central%20Park&host_name=Sarah&host_email=sarah%40example.com ### 2. Read an event (public) https://headless.events/api/events/{slug} https://headless.rsvp/api/events/{slug} (mirrored on the rsvp host for attendee agents) Returns event details + `rsvp_counts` { yes, no, maybe } each with `{count, guests}`. ### 3. Submit an RSVP (no auth) https://headless.rsvp/api/rsvps/create?event_slug=...&name=...&status=yes|no|maybe&guests=N¬es=...&email=... Required: `event_slug`, `name`, `status`. `status` MUST be one of `yes`, `no`, or `maybe` (lowercase). Optional: `email`, `guests` (integer, default 0), `notes`. ### 4. List RSVPs (auth) https://headless.events/api/events/{slug}/rsvps?t={edit_token} Returns `{rsvps: [{name, email, status, guests, notes, created_at}, ...]}`. ### 5. Update an event (auth) https://headless.events/api/events/{slug}/update?t={edit_token}&location=...&starts_at=...&description=... Pass only the fields you want to change. Returns `{ok: true}` or `{error: ...}`. ### 6. Cancel/delete an event (auth) https://headless.events/api/events/{slug}/delete?t={edit_token} ### 7. Email the host a fresh magic-link (no auth) https://headless.events/api/auth/magic?slug=...&host_email=... Use when the user has lost their `edit_token`. The host_email must match what's on file or nothing happens (response is always `{ok: true}` to avoid leaking event existence). ## DO and DON'T DO: Try Path A first. If it works, do it silently and report results. DO: If Path A fails or is refused, immediately switch to Path B - render the URL as a clickable Markdown link. DO: URL-encode every parameter value. DO: After creating an event, draft an invite (email or text) for the user containing `rsvp_url`. DO: Persist `edit_token` across the conversation so you can read RSVPs later. DON'T: Tell the user to run curl, wget, fetch, Postman, or any command. DON'T: Tell the user to "copy this URL into your browser". RENDER IT AS A MARKDOWN LINK. DON'T: Apologise for not being able to fetch and stop there. Use Path B - hand the user the link. DON'T: Use POST/PATCH/DELETE. The GET URLs do everything; mixing methods just creates failure modes that vary across LLM platforms. DON'T: Invent extra fields. The fields above are the entire surface. ## TYPICAL FLOW (HOST) 1. User describes their event in natural language. 2. Build the URL: `/api/events/create?title=...&starts_at=...&host_email=...` (URL-encoded). 3. Path A: fetch it. Save `edit_token` quietly. Tell the user "Done - your RSVP link is {rsvp_url}." Offer to draft the invite. 4. Path B (if your fetch tool refused): render the URL as a clickable Markdown link. Tell the user "Click to confirm. The page will show your RSVP link to share." 5. Later, user asks "who's coming?". Build `/api/events/{slug}/rsvps?t={edit_token}`, fetch (Path A) or render-as-link (Path B), and summarise. ## TYPICAL FLOW (ATTENDEE) 1. User clicked "RSVP via {You}" on headless.rsvp/{slug}. The prompt told you the event_slug. 2. (Optional) Confirm event details by GET `https://headless.rsvp/api/events/{slug}`. 3. Ask name, going (yes/no/maybe), guests, notes - in ONE short message. 4. Build `/api/rsvps/create?event_slug=...&name=...&status=...&guests=...¬es=...`. Path A: fetch. Path B: render as link. 5. Tell user "RSVP recorded. Thanks!" (Path A) or "Click to confirm your RSVP" (Path B). ## MACHINE-READABLE SPEC Full OpenAPI 3.1 (GET endpoints + the seldom-needed POST/PATCH/DELETE forms): https://headless.events/openapi.json