Automate your events from your own tools — or let an AI agent do it for you. Available on Pro+ plans.
Create an API key in the dashboard under Account → Sharing Settings → API Keys (it is shown exactly once). Send it on every request:
curl https://api.booth.events/v1/events \
-H "Authorization: Bearer be_live_..."
Keys are read or read-write scoped. Treat them like passwords: server-side only, revoke from the dashboard if leaked.
A key acts as the account owner. Everything it does — including on events a team member or operator runs — is done with the owner's full access; team-member roles and per-event operator scope never apply to API calls. Only the account owner can create keys: a team member cannot create one for an account they belong to, so ask the owner to create it.
The API is date-versioned. Every API key is pinned to the version that was current when
the key was created and keeps that behavior for as long as it lives — new API versions never change what an
existing key's requests mean. To adopt a newer version, create a new key
(new keys always pin to the current version) or re-pin the existing one; pin changes take up to a minute to
apply. The current version is 2026-08-04.
Override the version per request with the Booth-Events-Version header:
curl https://api.booth.events/v1/events \
-H "Authorization: Bearer be_live_..." \
-H "Booth-Events-Version: 2026-08-04"
Every response echoes the version that served it in the same header. MCP connectors (which cannot send
custom headers) can append ?version=2026-08-04 to the connector URL. The machine-readable
spec for any version is at /v1/openapi.json?version=….
Changelog
2026-08-04 — initial version.Two outbound webhook URLs can be configured per account (in the dashboard, or via
PUT /v1/webhooks): the media webhook fires when a guest uploads
(media.uploaded), and the send webhook fires when a share completes
(session.sent, when a guest's photo session is emailed or texted to them).
Deliveries POST JSON carrying an event discriminator, a unique deliveryId, and
their own apiVersion — a date separate from the API version above, because webhook payloads
change additively only: keys are added over time, never removed, renamed or re-typed, so there is
nothing to pin. Endpoints must be public https and must answer without redirecting.
media.uploaded carries the eventId and eventIdUser it belongs to;
session.sent carries the galleryId, so match it to the event with that
galleryId. Deliveries are not signed: put a long random secret in the URL and check it on
arrival. Each delivery is sent once, without retries, so reconcile now and then with
list_sessions and list_media.
The same API is exposed as a hosted MCP server at https://api.booth.events/mcp.
Claude Code
claude mcp add booth-events --transport http https://api.booth.events/mcp \
--header "Authorization: Bearer be_live_..."
Cursor / VS Code / Windsurf (mcp.json)
{
"mcpServers": {
"booth-events": {
"url": "https://api.booth.events/mcp",
"headers": { "Authorization": "Bearer be_live_..." }
}
}
}
claude.ai / ChatGPT (web connectors)
Add a custom connector with the URL https://api.booth.events/mcp. A Booth.Events sign-in
page opens and asks for one thing: a connection code, which you create in the dashboard
under Account → Sharing Settings → API Keys → Connect an app, choosing read-only or
read & write access there. The code works for 15 minutes. When the app finishes
connecting, Booth.Events creates an API key for it (named after the app, with the access you chose) —
nothing is created until then, and repeating the connection with the same code reuses that key. The
connection lasts 90 days and is listed under Connected apps in the same
place; removing the last connection on a key made this way revokes the key, and revoking the key
removes every connection made with it. Keys you create yourself in the dashboard are never removed by
a connection.
Trimming the tool list (optional)
The full surface is 119 tools; agents pick tools more reliably from smaller lists. Append
?toolsets= to the MCP URL to expose only what your integration needs, e.g.
https://api.booth.events/mcp?toolsets=events,templates,ai,devices,analytics (the core CRM loop).
Groups: events (events, gallery settings/URL, event⇄template assignment), templates
(templates, imports, scenes, uploads, attract screens, sticker sets), ai (prompts, portraits,
models, credits), media (guest media, sessions, survey export), analytics (stats, client
reports), account (audit log, webhooks, communication settings), devices (the
account's iPads and running events on them: the schedule, open/close), team,
payments, cloud-storage (the operator's Dropbox / Google Drive / SmugMug:
account setup, per-event destinations and media filters, backfill).
Every image URL the API returns is directly fetchable by any browser with no auth — but their lifetimes differ. If you are generating an app against this API (human or AI): render image URLs promptly and re-fetch the list to refresh them; never persist them as long-lived data.
| Where | Kind | Lifetime |
|---|---|---|
| Public AI portrait / prompt catalog previews | Stable public URL | Never expires — safe to cache |
Template / attract screen / sticker set thumbnailUrl |
Stable upload-time URL (signed fallback when absent) | Stable; fallback expires in 7 days |
Gallery media originalUrl/thumbnailUrl, event-stats
topMedia, private catalog previews |
Signed URL | Up to 7 days; media URLs are normally refreshed to ≥24 h when served; a legacy item without a stored path can still hand back an expired one — re-list to refresh |
Binary uploads (PDF/ZIP template imports, AI reference images, scenes) are a two-step flow:
POST /v1/uploads (create_upload) with a purpose — returns a
short-lived signed uploadUrl plus the headers you must send back exactly.PUT the raw bytes to uploadUrl (no auth header), then pass the returned
storagePath as uploadPath to the consuming operation.No PUT? Use sourceUrl instead. Every consuming operation alternatively accepts
a public https URL it fetches server-side (must not redirect) — ideal when your assets already live on your
own storage/CDN, and the only option for AI agents over MCP, which cannot upload binaries.
The intended loop for booking platforms (CheckCherry, HoneyBook, BoothBook, …). Set idUser to your booking id everywhere — it is the correlation key: list_events filters by it, and create_event is retry-safe with it (an existing event with the same idUser is returned with alreadyExisted instead of duplicating or burning quota).
create_event with name, date, templateIds (from list_templates), and idUser.settings.attractScreenDelay idle seconds (update_event attractScreenId; designed in the dashboard, see list_attract_screens, or made from one picture or video with create_attract_screen_from_image / create_attract_screen_from_video). iPad home screen: update_event sets the three colors (buttons; timers and borders; icons), the captureMessage heading, the hashtag line and their brandingTextColor; behind them is the live camera or an attract screen (update_event_branding); the capture buttons can be your own images (set_event_branding_image slot button-<type>, and settings.galleryButtonOnHomeScreen adds a gallery button). iPad after a capture: a background behind the preview and share screens (slot after-capture-background) and the stickers guests can add (update_event stickerSetId). Share Station iPads: an HTML banner at the top (update_gallery_settings shareStationHtml). Guest gallery: the event logo (slot logo, also used on the iPad, in emails and on the report) and a background image (slot gallery-background); update_gallery_settings sets its colours, which headers show, its social links, and its own content (a Message & Button, or Content Builder blocks); set_gallery_url gives it a vanity /e/ link or the account's custom domain. Guest emails and texts: update_communication_settings sets the sender name, reply-to, subject, the lines around the gallery link and the SMS text for every event, and update_event_communication_settings gives one event its own; the email also carries the event logo and a button in the gallery's primary colour. Client report: update_event_report sets its logo, accent colour, hero background and its own content (a Message & Button, or Content Builder blocks). Every event at once: update_account_branding and set_account_logo set the account-wide defaults, which are the logo and name in each gallery's top bar, the colours new events start with, and the social links a gallery shows when it has none of its own. Each image is shown differently, so read set_event_branding_image before making one: the logo is a square (shown as a circle on the iPad and in emails), a capture button is a transparent PNG 292 px tall, the iPad's after-capture background is shown unscaled and should be about the iPad's own screen size (1376×1367 px for a landscape iPad), and the gallery background is a landscape 2560×1440 px JPEG.create_template_from_pdf or create_template_from_images (PNG/JPG/ZIP): same pipeline as the dashboard's "Upload design", photo areas detected automatically; or duplicate_template from the public library. Making the design yourself (Canva, Figma, …)? Photo areas are found by looking at the pixels of the design, so a design made for import must follow these rules — a box that breaks them is not found, and no guest photo can go in it. (1) Leave every photo box EMPTY and either fully transparent (PNG) or pure white (#FFFFFF): no fill colour, tint, gradient, texture, placeholder photo, "your photo here" text, icon, or anything else over it. In Canva, draw a plain white rectangle — a frame or grid holding an image, or a coloured box, is not a photo area. (2) Draw it as a plain, upright rectangle: circles, rounded, rotated or irregular shapes are not found reliably. (3) Surround it with opaque design that is not white — a coloured or patterned background, or at least a border all round — and keep it off the corners of the page. A white box on a white page, or a transparent hole in a transparent page, merges with the page instead, and a white box that runs into a corner of the page can be taken for the page background and ignored. (4) Make it big: at least 6% of the page's area each (360×360 px or more on a 1800×1200 4×6 print) and at least 100 px on every side. (5) Keep every other large white or transparent rectangle out of the design (white panels, text boxes, logo backgrounds): it would become a photo area too. (6) Use one kind per design: all boxes transparent, or all white. Then check the import's response: photoAreaCount must equal the number of boxes you designed (an AI prompt or portrait needs exactly 1), warnings must not contain no_photo_areas_detected, and the photo-area rectangles in layers[] must sit on the boxes in the returned picture. If they do not, fix the design and import it again, or put the areas right with add_template_photo_area / update_template_photo_area / remove_template_photo_area. get_template shows the design as rectangles in template pixels (and, through MCP, the rendered picture) at any time. Then write on it: add_template_text for the names or the date (any Google Font from list_fonts, measured and centred for you), add_template_field to print a guest's answer (a new question of the template's own, or a field of an AI prompt assigned with update_template — one field, asked once, used by both), with preview / previewChoiceId choosing what the design shows. add_template_image places a picture such as the client's logo, move_template_layer puts a photo area under a frame's artwork so the guest photo shows through its hole, and update_template sets the template's look and printing (imageFilters, bwCustom, filmStrip, glamFilter, printable, printsShortestSideDoubled). Every change renders the picture again and returns it; render_template_preview renders one on demand. An event needs a template from the start (create_event templateIds), so make or pick the template first; add_event_template adds more later.create_ai_prompt (+ add_ai_prompt_reference_image for style; list_ai_models compares credits per image and speed; give it fields to ask each guest a question first — a name, a choice from a list, a choice between pictures — and body to place the answers in the prompt), then test_ai_prompt with useAsPreview to see what it makes and to give it the preview image guests pick it by (the iPad does not offer a prompt without one). Or pick portrait styles via list_ai_portraits. Attach both with update_template, and make sure the event's settings.captureTypes includes aiCustomPrompt. Check get_ai_credits before the event.add_template_scene per background.get_cloud_storage shows what the operator connected (Dropbox / Google Drive / SmugMug — connecting is dashboard-only); update_event_cloud_storage points the event at its own folder or album (list_cloud_storage_folders / create_cloud_storage_folder to find or make one) and picks which media types go; backfill_event_cloud_storage uploads what was captured before the settings changed.set_device_schedule is how you put an event on an iPad. To run it now, send one block starting now with no end: the iPad launches the event at once and stays locked in it (nobody at the iPad can leave the event or pick another) until you change, pause or clear the schedule. Setting a schedule also turns a paused one back on, so the event you send is what runs. To run it later, give the block the event's start and end (every time with Z or an offset): the iPad launches the event at the start and closes the booth at the end, and a block that has already started takes over that live booth at once. Until the first block starts the booth is closed and locked: a schedule whose blocks all start later ends whatever event the iPad is running at once. So set the schedule close to the event, or send the running event's block along with the later one. list_devices shows the account's iPads and how each is run (controls).get_event_stats for engagement (leaderboards come with names + preview images), then update_event_report + publish_event_report and email the returned reportUrl to your client — a white-label-ready report page, no sign-in needed. Each guest's own photo page is sessionUrl (list_sessions): send_session_share emails or texts it to them again, and send_gallery_download_link emails your client a link to download the whole gallery without the passcode (confirm the address first). The gallery stays open to guests until it expires: get_gallery_settings lifecycle has the expiry date and the days left, so remind your client before then to save what they want to keep.At least 120 requests/min per key (burst 40); expensive operations (creates, duplicates) count extra. All keys calling from the same IP address also share 300 requests/min between them, so an integration that serves several accounts from one address should plan for that total. An account also runs at most 3 heavy operations (imports, renders, fetching a file from a URL) and 10 operations in all at once, across its keys — queue the rest — makes at most 50 duplicates a day, and sends guests at most 1000 emails and 250 texts a day. Booth.Events support can lift these limits for an account that needs more. 429 responses carry Retry-After. Errors always look like:
{ "error": { "code": "validation_failed", "message": "...", "details": [...], "hint": "...", "requestId": "..." } }
details and hint appear when there is something to add: which field failed, or what to do next.
Full endpoint reference below. The machine-readable spec is at /v1/openapi.json.