{"openapi":"3.1.0","info":{"title":"Booth.Events API","version":"2026-08-04","x-api-versions":["2026-08-04"],"description":"External customer API for Booth.Events (Pro+). Authenticate with `Authorization: Bearer <api key>` — create keys in the dashboard under Account → Sharing Settings → API Keys. An MCP endpoint for AI agents lives at POST /mcp with the same key. The API is date-versioned: every key is pinned to the version current at its creation, and requests can override with a `Booth-Events-Version` header (see the Versioning section at /v1/docs)."},"servers":[{"url":"https://api.booth.events/v1"}],"security":[{"apiKey":[]}],"paths":{"/events":{"get":{"operationId":"list_events","summary":"List events on the account","description":"Newest first by default. Filter by EXACTLY ONE of: your own idUser key, a name prefix (search), or a date range — combining them returns 400 rather than silently ignoring one. idUser and search results are ordered by that field, not by sort/order. Returns summaries without the settings map — use get_event for the full record.","parameters":[{"name":"limit","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","default":25,"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string"}},{"name":"idUser","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","description":"Exact match on your own external key (surrounding spaces ignored)","type":"string","minLength":1},"description":"Exact match on your own external key (surrounding spaces ignored)"},{"name":"search","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","description":"Case-insensitive name prefix","type":"string","minLength":1},"description":"Case-insensitive name prefix"},{"name":"dateFrom","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},{"name":"dateTo","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},{"name":"sort","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","default":"createdDate","type":"string","enum":["createdDate","date"]}},{"name":"order","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","default":"desc","type":"string","enum":["asc","desc"]}}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"date":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"hashtag":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A smaller line (36 points) under the capture message on the iPad home screen"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}]},"idUser":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The customer's own external key for this event"},"colors":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"The iPad's three colours for this event, in order: [0] the main buttons (and the outline of the built-in capture buttons), [1] the shutter countdown, timers and borders, [2] icons and actions such as the share and Done buttons. Exactly three, each '#rrggbb'. They sit on white or carry white text, so a very light colour is refused. A new event starts with the account's colours"},"captureMessage":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The large heading on the iPad home screen, above the capture buttons, e.g. 'Tap to start'. It is big and bold (72 points), centred, and wraps onto as many lines as it needs without being cut off, so keep it to a few words: a long message covers the camera view"},"brandingTextColor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"'#rrggbb'. The colour of the capture message, the hashtag, the get-ready text and the countdown digits on the iPad. They are drawn over the live camera view or the attract screen, so pick one that reads there; white is the usual choice"},"templateIds":{"type":"array","items":{"type":"string"}},"attractScreenId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The attract screen (list_attract_screens) the iPad shows full-screen once it has been idle for settings.attractScreenDelay seconds, to draw guests in; null = the iPad's built-in one. To put an attract screen behind the home screen instead of the camera, see update_event_branding"},"stickerSetId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The sticker set (list_sticker_sets) guests can add to their photos on the iPad; null = guests cannot add stickers"},"paySetId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pay-per-use pricing set; null = pay off"},"printCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Prints made for this event so far, across all its iPads: the number settings.printCountMax is compared with, e.g. to show a client '34 of 100 prints used' or to bill extra prints. Counted when each print job finishes, one per sheet of paper (a template printed twice on one sheet counts once); a print sent through the iPad's own AirPrint dialog counts as 1, whatever number of copies was chosen in that dialog. 0 before the first print"},"galleryId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Gallery id. The gallery's settings (passcode, privacy, sharing) are a separate subresource — get_gallery_settings — deliberately NOT embedded here: they live in a different (regional) database, so embedding would put a cross-database round-trip on the most-called read."}},"required":["id","name","date","createdDate","modifiedDate","hashtag","notes","idUser","colors","captureMessage","brandingTextColor","templateIds","attractScreenId","stickerSetId","paySetId","printCount","galleryId"],"additionalProperties":false}},"hasMore":{"type":"boolean"},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["data","hasMore","nextCursor"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}},"post":{"operationId":"create_event","summary":"Create an event (also creates its shared gallery)","description":"Consumes plan quota or a purchased event credit, like creating from the dashboard. templateIds must reference templates you own or public ones — use list_templates to find ids. RETRY-SAFE with idUser: set idUser to YOUR system's id for the booking (the CRM correlation key — list_events filters by it); if an event with that idUser already exists, it is returned UNMODIFIED with alreadyExisted:true — other fields sent on the retry are ignored, no duplicate is created, no quota is burned (use update_event to change it). The response can include account-level defaults you did not send (e.g. stickerSetId), matching dashboard creation. The new event offers every capture type its templates can serve (settings.captureTypes); check_event says what its guests will get. An event uses the template itself, not a copy: a change to a template shows on every event that uses it from then on. To personalise a design for one client (their names, date or logo), copy it first (duplicate_template) and change the copy.","x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"date":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"hashtag":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A smaller line (36 points) under the capture message on the iPad home screen"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}]},"idUser":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The customer's own external key for this event"},"colors":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"The iPad's three colours for this event, in order: [0] the main buttons (and the outline of the built-in capture buttons), [1] the shutter countdown, timers and borders, [2] icons and actions such as the share and Done buttons. Exactly three, each '#rrggbb'. They sit on white or carry white text, so a very light colour is refused. A new event starts with the account's colours"},"captureMessage":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The large heading on the iPad home screen, above the capture buttons, e.g. 'Tap to start'. It is big and bold (72 points), centred, and wraps onto as many lines as it needs without being cut off, so keep it to a few words: a long message covers the camera view"},"brandingTextColor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"'#rrggbb'. The colour of the capture message, the hashtag, the get-ready text and the countdown digits on the iPad. They are drawn over the live camera view or the attract screen, so pick one that reads there; white is the usual choice"},"templateIds":{"type":"array","items":{"type":"string"}},"attractScreenId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The attract screen (list_attract_screens) the iPad shows full-screen once it has been idle for settings.attractScreenDelay seconds, to draw guests in; null = the iPad's built-in one. To put an attract screen behind the home screen instead of the camera, see update_event_branding"},"stickerSetId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The sticker set (list_sticker_sets) guests can add to their photos on the iPad; null = guests cannot add stickers"},"paySetId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pay-per-use pricing set; null = pay off"},"printCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Prints made for this event so far, across all its iPads: the number settings.printCountMax is compared with, e.g. to show a client '34 of 100 prints used' or to bill extra prints. Counted when each print job finishes, one per sheet of paper (a template printed twice on one sheet counts once); a print sent through the iPad's own AirPrint dialog counts as 1, whatever number of copies was chosen in that dialog. 0 before the first print"},"galleryId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Gallery id. The gallery's settings (passcode, privacy, sharing) are a separate subresource — get_gallery_settings — deliberately NOT embedded here: they live in a different (regional) database, so embedding would put a cross-database round-trip on the most-called read."},"settings":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}],"description":"The full iOS EventSettings map as stored"},"alreadyExisted":{"type":"boolean","description":"true = an event with this idUser already existed and was returned unchanged"}},"required":["id","name","date","createdDate","modifiedDate","hashtag","notes","idUser","colors","captureMessage","brandingTextColor","templateIds","attractScreenId","stickerSetId","paySetId","printCount","galleryId","settings","alreadyExisted"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"date":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"Event date, ISO-8601"},"templateIds":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"string","minLength":1},"description":"The templates guests can choose from. An event uses the template itself, not a copy: a change to a template shows on every event that uses it from then on. To personalise a design for one client (their names, date or logo), copy it first (duplicate_template) and change the copy."},"hashtag":{"description":"A smaller line (36 points) under the capture message on the iPad home screen","type":"string","maxLength":100},"notes":{"type":"string","maxLength":5000},"idUser":{"description":"Your own external key for this event (stored trimmed)","type":"string","maxLength":200},"colors":{"description":"The iPad's three colours for this event, in order: [0] the main buttons (and the outline of the built-in capture buttons), [1] the shutter countdown, timers and borders, [2] icons and actions such as the share and Done buttons. Exactly three, each '#rrggbb'. They sit on white or carry white text, so a very light colour is refused. A new event starts with the account's colours","minItems":3,"maxItems":3,"type":"array","items":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"}},"captureMessage":{"description":"The large heading on the iPad home screen, above the capture buttons, e.g. 'Tap to start'. It is big and bold (72 points), centred, and wraps onto as many lines as it needs without being cut off, so keep it to a few words: a long message covers the camera view","type":"string","maxLength":500},"brandingTextColor":{"description":"'#rrggbb'. The colour of the capture message, the hashtag, the get-ready text and the countdown digits on the iPad. They are drawn over the live camera view or the attract screen, so pick one that reads there; white is the usual choice","type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"attractScreenId":{"description":"The attract screen (list_attract_screens) the iPad shows full-screen once it has been idle for settings.attractScreenDelay seconds, to draw guests in; null = the iPad's built-in one. To put an attract screen behind the home screen instead of the camera, see update_event_branding","type":"string"},"stickerSetId":{"description":"The sticker set (list_sticker_sets) guests can add to their photos on the iPad; null = guests cannot add stickers","type":"string"}},"required":["name","date","templateIds"]}}}}}},"/events/{eventId}":{"get":{"operationId":"get_event","summary":"Get one event, including its full settings map","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}},{"name":"response_format","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","default":"detailed","description":"'concise' returns settings: null (skip the ~25-key settings map when you only need the summary fields)","type":"string","enum":["detailed","concise"]},"description":"'concise' returns settings: null (skip the ~25-key settings map when you only need the summary fields)"}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"date":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"hashtag":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A smaller line (36 points) under the capture message on the iPad home screen"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}]},"idUser":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The customer's own external key for this event"},"colors":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"The iPad's three colours for this event, in order: [0] the main buttons (and the outline of the built-in capture buttons), [1] the shutter countdown, timers and borders, [2] icons and actions such as the share and Done buttons. Exactly three, each '#rrggbb'. They sit on white or carry white text, so a very light colour is refused. A new event starts with the account's colours"},"captureMessage":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The large heading on the iPad home screen, above the capture buttons, e.g. 'Tap to start'. It is big and bold (72 points), centred, and wraps onto as many lines as it needs without being cut off, so keep it to a few words: a long message covers the camera view"},"brandingTextColor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"'#rrggbb'. The colour of the capture message, the hashtag, the get-ready text and the countdown digits on the iPad. They are drawn over the live camera view or the attract screen, so pick one that reads there; white is the usual choice"},"templateIds":{"type":"array","items":{"type":"string"}},"attractScreenId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The attract screen (list_attract_screens) the iPad shows full-screen once it has been idle for settings.attractScreenDelay seconds, to draw guests in; null = the iPad's built-in one. To put an attract screen behind the home screen instead of the camera, see update_event_branding"},"stickerSetId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The sticker set (list_sticker_sets) guests can add to their photos on the iPad; null = guests cannot add stickers"},"paySetId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pay-per-use pricing set; null = pay off"},"printCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Prints made for this event so far, across all its iPads: the number settings.printCountMax is compared with, e.g. to show a client '34 of 100 prints used' or to bill extra prints. Counted when each print job finishes, one per sheet of paper (a template printed twice on one sheet counts once); a print sent through the iPad's own AirPrint dialog counts as 1, whatever number of copies was chosen in that dialog. 0 before the first print"},"galleryId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Gallery id. The gallery's settings (passcode, privacy, sharing) are a separate subresource — get_gallery_settings — deliberately NOT embedded here: they live in a different (regional) database, so embedding would put a cross-database round-trip on the most-called read."},"settings":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}],"description":"The full iOS EventSettings map as stored"}},"required":["id","name","date","createdDate","modifiedDate","hashtag","notes","idUser","colors","captureMessage","brandingTextColor","templateIds","attractScreenId","stickerSetId","paySetId","printCount","galleryId","settings"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}},"patch":{"operationId":"update_event","summary":"Update event fields and/or a partial settings object","description":"Partial update: only the fields you send change. name and date are also written to the event's gallery, which guests see; renaming before the gallery's first upload re-mints its default /of/ link from the new name, renaming after leaves the link as it is. settings merges per key (e.g. {\"settings\":{\"countdownTimer\":5}}); null clears idUser/attractScreenId/stickerSetId/paySetId (a null paySetId turns pay-per-use off). Operators cannot run events that take payments: setting a paySetId on an event an operator holds (a live operator or an open operator invite names it) is a conflict whose details.holders lists them ({ kind: \"operator\" | \"invite\", name }) — remove the operator or revoke the invite first. Clearing paySetId is always allowed. Whether the iPad will open the event and what its guests will get depends on its templates as well as its settings: check_event says, so run it after a change. When templateIds or settings.captureTypes change, a capture-type list holding all seven types is trimmed to the ones the templates serve (see settings.captureTypes).","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"date":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"hashtag":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A smaller line (36 points) under the capture message on the iPad home screen"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}]},"idUser":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The customer's own external key for this event"},"colors":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"The iPad's three colours for this event, in order: [0] the main buttons (and the outline of the built-in capture buttons), [1] the shutter countdown, timers and borders, [2] icons and actions such as the share and Done buttons. Exactly three, each '#rrggbb'. They sit on white or carry white text, so a very light colour is refused. A new event starts with the account's colours"},"captureMessage":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The large heading on the iPad home screen, above the capture buttons, e.g. 'Tap to start'. It is big and bold (72 points), centred, and wraps onto as many lines as it needs without being cut off, so keep it to a few words: a long message covers the camera view"},"brandingTextColor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"'#rrggbb'. The colour of the capture message, the hashtag, the get-ready text and the countdown digits on the iPad. They are drawn over the live camera view or the attract screen, so pick one that reads there; white is the usual choice"},"templateIds":{"type":"array","items":{"type":"string"}},"attractScreenId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The attract screen (list_attract_screens) the iPad shows full-screen once it has been idle for settings.attractScreenDelay seconds, to draw guests in; null = the iPad's built-in one. To put an attract screen behind the home screen instead of the camera, see update_event_branding"},"stickerSetId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The sticker set (list_sticker_sets) guests can add to their photos on the iPad; null = guests cannot add stickers"},"paySetId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pay-per-use pricing set; null = pay off"},"printCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Prints made for this event so far, across all its iPads: the number settings.printCountMax is compared with, e.g. to show a client '34 of 100 prints used' or to bill extra prints. Counted when each print job finishes, one per sheet of paper (a template printed twice on one sheet counts once); a print sent through the iPad's own AirPrint dialog counts as 1, whatever number of copies was chosen in that dialog. 0 before the first print"},"galleryId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Gallery id. The gallery's settings (passcode, privacy, sharing) are a separate subresource — get_gallery_settings — deliberately NOT embedded here: they live in a different (regional) database, so embedding would put a cross-database round-trip on the most-called read."},"settings":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}],"description":"The full iOS EventSettings map as stored"},"warnings":{"description":"Non-blocking: the pay set's currency matches no connected payment rail — charges would be refused at the booth until that changes","type":"array","items":{"type":"string","const":"currencyMismatch"}}},"required":["id","name","date","createdDate","modifiedDate","hashtag","notes","idUser","colors","captureMessage","brandingTextColor","templateIds","attractScreenId","stickerSetId","paySetId","printCount","galleryId","settings"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"date":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},"templateIds":{"description":"The templates guests can choose from. An event uses the template itself, not a copy: a change to a template shows on every event that uses it from then on. To personalise a design for one client (their names, date or logo), copy it first (duplicate_template) and change the copy.","minItems":1,"maxItems":50,"type":"array","items":{"type":"string","minLength":1}},"hashtag":{"description":"A smaller line (36 points) under the capture message on the iPad home screen","anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"notes":{"anyOf":[{"type":"string","maxLength":5000},{"type":"null"}]},"idUser":{"description":"Stored trimmed; null or blank clears it","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"colors":{"description":"The iPad's three colours for this event, in order: [0] the main buttons (and the outline of the built-in capture buttons), [1] the shutter countdown, timers and borders, [2] icons and actions such as the share and Done buttons. Exactly three, each '#rrggbb'. They sit on white or carry white text, so a very light colour is refused. A new event starts with the account's colours","minItems":3,"maxItems":3,"type":"array","items":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"}},"captureMessage":{"description":"The large heading on the iPad home screen, above the capture buttons, e.g. 'Tap to start'. It is big and bold (72 points), centred, and wraps onto as many lines as it needs without being cut off, so keep it to a few words: a long message covers the camera view","anyOf":[{"type":"string","maxLength":500},{"type":"null"}]},"brandingTextColor":{"description":"'#rrggbb'. The colour of the capture message, the hashtag, the get-ready text and the countdown digits on the iPad. They are drawn over the live camera view or the attract screen, so pick one that reads there; white is the usual choice","anyOf":[{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},{"type":"null"}]},"attractScreenId":{"description":"The attract screen (list_attract_screens) the iPad shows full-screen once it has been idle for settings.attractScreenDelay seconds, to draw guests in; null = the iPad's built-in one. To put an attract screen behind the home screen instead of the camera, see update_event_branding","anyOf":[{"type":"string"},{"type":"null"}]},"stickerSetId":{"description":"The sticker set (list_sticker_sets) guests can add to their photos on the iPad; null = guests cannot add stickers","anyOf":[{"type":"string"},{"type":"null"}]},"paySetId":{"description":"Pay-per-use pricing set for this event; null turns pay off","anyOf":[{"type":"string","minLength":1},{"type":"null"}]},"settings":{"type":"object","properties":{"version":{"description":"Managed by the system; not writable","not":{}},"captureTypes":{"minItems":1,"type":"array","items":{"type":"string","enum":["photo","boomerang","slowmo","video","gif","aiPhoto","aiCustomPrompt"]},"description":"Capture modes offered to guests, in this order. Each needs a template on the event that can serve it: photo works with any template; boomerang, slowmo, video and gif need a template with exactly 1 photo area; aiPhoto (AI Portrait) also needs AI Portrait styles on that template (update_template aiPortraitIds) and the online gallery; aiCustomPrompt (AI Prompt) also needs an AI prompt on that template that is switched on and has a preview image (update_template aiCustomPromptIds; test_ai_prompt useAsPreview). A template's disabledCaptureTypes switches a type off for it. The iPad REFUSES TO OPEN an event with a type none of its templates can serve ('No compatible templates'), with one exception: it quietly leaves out AI Portrait when no template has AI Portrait styles (or the online gallery is off), and AI Prompt when no template has a usable AI prompt; a template that has them but not exactly 1 photo area still blocks the event. An event whose settings.captureTypes lists all seven types is trimmed to the ones its templates can serve, as the iPad does when the event's settings are opened on it (a schedule launch never does, and refuses an event listing a type no template serves). A shorter list is kept as sent: to offer a type a newly added template serves, add it to settings.captureTypes (update_event). AI generations use the account's AI credits, one balance for every event (get_ai_credits): there is no limit per event, so one busy event can use them all, after which guests are told the account does not have enough AI credits. Run check_event after changing this, the event's templates or their AI prompts"},"uploadOriginals":{"type":"boolean"},"countdownTimer":{"type":"integer","minimum":0,"maximum":30,"description":"Seconds before capture"},"soundEnabled":{"type":"boolean"},"takeMoreEnabled":{"type":"boolean"},"soundDuringBoomerangRecord":{"type":"boolean"},"actions":{"type":"array","items":{"type":"string","enum":["email","sms","qrCode","airdrop"]},"description":"Share options offered to guests after a capture, on an iPad that shares: one in Capture & Share mode, or a Share Station when a guest picks their photos there. airdrop is offered on every such iPad. email, sms and qrCode need sharedGalleryEnabled. Capture Station mode (stationType 'camera' in set_device_schedule; the dashboard's Station Type 'Capture') is capture only: after each capture the iPad goes straight back to its home screen, with no share, print or gallery screen and no gallery button, so guests get none of the share options and nothing prints there, whatever the event's settings say. Their photos upload to the event's online gallery, where guests find, share and print them at a Share Station iPad (stationType 'sharingStation') or in the gallery itself. So Capture Station mode needs the online gallery (sharedGalleryEnabled): without it the photos reach no one. sms is also left out when texts are switched off for the account (unless the account sends through its own Twilio) or for this event's gallery (even with its own Twilio). An empty list offers no sharing at all: guests get only the Print button (when the iPad has a printer) and Done — a prints-only event. Each iPad applies these rules silently; check_event says which options guests will actually get"},"sharedGalleryEnabled":{"type":"boolean","description":"Whether captures go to the event's online gallery. Off, the iPad offers no email, text or QR code (only AirDrop and Print), and photos taken in Capture Station mode reach no one: a Share Station finds them in the online gallery"},"attractScreenDelay":{"anyOf":[{"type":"number","minimum":5,"maximum":3600},{"type":"null"}],"description":"Idle seconds before the attract screen; null disables it"},"videoMaxDuration":{"type":"integer","minimum":3,"maximum":300,"description":"Seconds"},"autoGifFromPhotos":{"type":"boolean"},"gifNumberOfFrames":{"type":"integer","minimum":2,"maximum":12},"gifBoomerang":{"type":"boolean"},"gifCountdownTimer":{"type":"integer","minimum":0,"maximum":30},"boomerangRecordDuration":{"type":"number","minimum":0.5,"maximum":10,"description":"Seconds"},"gifPlaybackSpeed":{"type":"string","enum":["slow","medium","fast"]},"dataCollectionPosition":{"type":"string","enum":["beforeCapture","afterCapture"],"description":"Which survey stage older iPad builds ask. Saving a survey (update_event_survey) sets it; current builds follow the survey itself"},"galleryButtonOnHomeScreen":{"type":"string","enum":["none","cameraRoll","cloud"],"description":"A gallery button on the iPad home screen, beside the capture buttons: 'none', 'cameraRoll' (what was captured on this iPad) or 'cloud' (the event's online gallery, from every iPad). Its image can be replaced with set_event_branding_image slot 'button-gallery'. On the iPad only: the links to the gallery on a guest's own web page are update_gallery_settings"},"aiRerollCount":{"type":"integer","minimum":0,"maximum":20},"passcodeOnQRScreen":{"type":"boolean","description":"Shows the gallery passcode under the QR code guests scan on the iPad, when the gallery has one (update_gallery_settings passcode). Not the guest's email or text: those are update_gallery_settings shareHidesPasscode"},"menuGalleryButtonsShown":{"type":"boolean","description":"Whether the iPad's in-event menu, opened from its settings cog, has the buttons that open the gallery on the iPad: this iPad's captures, or the event's online gallery. Not on a Share Station. On the iPad only: the links to the gallery on a guest's own web page are update_gallery_settings"},"attractScreenRoles":{"minItems":1,"type":"array","items":{"type":"string","enum":["allInOne","camera","sharingStation"]},"description":"Which iPads show the attract screen when idle: 'allInOne' (Capture & Share mode), 'camera' (Capture Station mode, capture only), 'sharingStation' (Share Station mode)"},"cropOriginals":{"type":"string","enum":["none","template"]},"addTemplatedOriginalToAiSessions":{"type":"boolean"},"aiPhotoChildlike":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"templatePreviewDisabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"printCountMax":{"anyOf":[{"type":"integer","minimum":1,"maximum":1000000},{"type":"null"}],"description":"Total prints allowed for the event, across all its iPads (compared with the event's printCount); null = no limit. Each iPad checks it against the count it last saw, so several iPads printing at the same moment can go a few prints over. Once it is reached, guests are told 'The maximum number of prints has been reached for this event. Please contact your event organizer.' How many prints each guest may make, and automatic printing, are set on each iPad, not through the API, and an iPad prints only when a printer is set up on it. There is no 0: for an event without printing, give it templates set not to print (update_template printable); for prints without digital sharing, send an empty actions list"},"captureModesHaveOrder":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"tapToContinue":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"takeMoreLimit":{"anyOf":[{"type":"integer","minimum":0,"maximum":10},{"type":"null"}]},"touchToStartScreen":{"anyOf":[{"type":"string","enum":["auto","always","never"]},{"type":"null"}],"description":"The \"Ready? Touch to start!\" screen before a capture (SOL-3191). auto = the long-standing heuristic (skipped when the guest has nothing left to choose), always = shown (a remote-triggered capture still skips it), never = straight into the countdown. null = default (auto)"}},"additionalProperties":false}}}}}}},"delete":{"operationId":"delete_event","summary":"Delete an event and its gallery (irreversible)","description":"Removes the event, its shared gallery (all guest media), and its per-event communication overrides. There is no undo.","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"deleted":{"type":"boolean","const":true},"id":{"type":"string"}},"required":["deleted","id"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/events/{eventId}/duplicate":{"post":{"operationId":"duplicate_event","summary":"Duplicate an event (templates shared by reference; new gallery)","description":"The fastest way to create a recurring event: copies configuration, survey, and communication overrides, creates a fresh gallery. Consumes quota/credits like create_event. If the source event has an idUser, the copy's idUser gets a '-duplicated' suffix so it never collides with create_event's retry-safe lookup — set your own key with update_event. A copy whose capture types list all seven is trimmed to the ones its templates serve (see update_event settings.captureTypes). An account can make 50 duplicates a day (UTC), all duplicate_* operations together; past that, rate_limited with Retry-After until midnight UTC. Booth.Events support can lift this limit for an account that needs more.","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"Source event id"}}],"x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"date":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"hashtag":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A smaller line (36 points) under the capture message on the iPad home screen"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}]},"idUser":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The customer's own external key for this event"},"colors":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"The iPad's three colours for this event, in order: [0] the main buttons (and the outline of the built-in capture buttons), [1] the shutter countdown, timers and borders, [2] icons and actions such as the share and Done buttons. Exactly three, each '#rrggbb'. They sit on white or carry white text, so a very light colour is refused. A new event starts with the account's colours"},"captureMessage":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The large heading on the iPad home screen, above the capture buttons, e.g. 'Tap to start'. It is big and bold (72 points), centred, and wraps onto as many lines as it needs without being cut off, so keep it to a few words: a long message covers the camera view"},"brandingTextColor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"'#rrggbb'. The colour of the capture message, the hashtag, the get-ready text and the countdown digits on the iPad. They are drawn over the live camera view or the attract screen, so pick one that reads there; white is the usual choice"},"templateIds":{"type":"array","items":{"type":"string"}},"attractScreenId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The attract screen (list_attract_screens) the iPad shows full-screen once it has been idle for settings.attractScreenDelay seconds, to draw guests in; null = the iPad's built-in one. To put an attract screen behind the home screen instead of the camera, see update_event_branding"},"stickerSetId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The sticker set (list_sticker_sets) guests can add to their photos on the iPad; null = guests cannot add stickers"},"paySetId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pay-per-use pricing set; null = pay off"},"printCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Prints made for this event so far, across all its iPads: the number settings.printCountMax is compared with, e.g. to show a client '34 of 100 prints used' or to bill extra prints. Counted when each print job finishes, one per sheet of paper (a template printed twice on one sheet counts once); a print sent through the iPad's own AirPrint dialog counts as 1, whatever number of copies was chosen in that dialog. 0 before the first print"},"galleryId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Gallery id. The gallery's settings (passcode, privacy, sharing) are a separate subresource — get_gallery_settings — deliberately NOT embedded here: they live in a different (regional) database, so embedding would put a cross-database round-trip on the most-called read."},"settings":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}],"description":"The full iOS EventSettings map as stored"}},"required":["id","name","date","createdDate","modifiedDate","hashtag","notes","idUser","colors","captureMessage","brandingTextColor","templateIds","attractScreenId","stickerSetId","paySetId","printCount","galleryId","settings"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"date":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},"required":["name","date"]}}}}}},"/events/{eventId}/branding":{"get":{"operationId":"get_event_branding","summary":"The event's logo, its iPad capture-screen branding and gallery background","description":"The logo, the custom capture-button images, the iPad's after-capture background, the attract screen used as the home-screen background, and the guest gallery's background image. set_event_branding_image says what shape and size each image should be. The event's colours, text colour, capture message and hashtag are on the event itself (get_event); the guest gallery's colours and content are on get_gallery_settings.","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"eventId":{"type":"string"},"logoUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The event logo: a circle on the iPad and in the emails guests receive, shown whole at the top of the guest gallery"},"buttonImagesAreRetina":{"type":"boolean","description":"How the iPad sizes custom capture-button images. false = 1 pixel to 1 point, up to 292 points tall. true = half size, up to 146 points tall: sharp on a Retina screen"},"homeScreenBackgroundAttractScreenId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The attract screen shown full-screen behind the iPad home screen; null = the live camera view"},"afterCaptureBackgroundUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The iPad's after-capture background, behind the preview, filters and share screens; null = the built-in one. 7-day signed URL — re-fetch to refresh"},"captureButtonUrls":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"},"description":"Custom capture-button images by button (photo, boomerang, slowmo, video, gif, aiPhoto, aiCustomPrompt, gallery); a button not listed uses the built-in look. 7-day signed URLs"},"galleryBackgroundUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The background of the guest gallery's web pages; null = none"}},"required":["eventId","logoUrl","buttonImagesAreRetina","homeScreenBackgroundAttractScreenId","afterCaptureBackgroundUrl","captureButtonUrls","galleryBackgroundUrl"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}},"patch":{"operationId":"update_event_branding","summary":"Set the iPad home-screen background and the capture buttons' size","description":"Partial update. homeScreenBackgroundAttractScreenId puts one of your attract screens full-screen behind the iPad home screen, in place of the live camera view (null goes back to the camera). Pick one designed for the way the iPad is mounted: list_attract_screens returns each one's sizePixels, [width, height] in iPad screen points, e.g. [1032, 1376] for a portrait 13-inch iPad Pro and [1376, 1032] for landscape. The iPad lays an attract screen out to its own screen, so one made for another shape may display incorrectly. Attract screens are designed in the dashboard. buttonImagesAreRetina shows the custom capture-button images at half size: a 292 px tall image is then 146 points tall and sharp, instead of 292 points tall and soft. Requires a Pro+ event. Images are set with set_event_branding_image.","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"eventId":{"type":"string"},"logoUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The event logo: a circle on the iPad and in the emails guests receive, shown whole at the top of the guest gallery"},"buttonImagesAreRetina":{"type":"boolean","description":"How the iPad sizes custom capture-button images. false = 1 pixel to 1 point, up to 292 points tall. true = half size, up to 146 points tall: sharp on a Retina screen"},"homeScreenBackgroundAttractScreenId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The attract screen shown full-screen behind the iPad home screen; null = the live camera view"},"afterCaptureBackgroundUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The iPad's after-capture background, behind the preview, filters and share screens; null = the built-in one. 7-day signed URL — re-fetch to refresh"},"captureButtonUrls":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"},"description":"Custom capture-button images by button (photo, boomerang, slowmo, video, gif, aiPhoto, aiCustomPrompt, gallery); a button not listed uses the built-in look. 7-day signed URLs"},"galleryBackgroundUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The background of the guest gallery's web pages; null = none"}},"required":["eventId","logoUrl","buttonImagesAreRetina","homeScreenBackgroundAttractScreenId","afterCaptureBackgroundUrl","captureButtonUrls","galleryBackgroundUrl"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"homeScreenBackgroundAttractScreenId":{"description":"An attract screen id (list_attract_screens), one whose sizePixels matches how the iPad is mounted; null = the live camera view","anyOf":[{"type":"string","minLength":1},{"type":"null"}]},"buttonImagesAreRetina":{"description":"true = show custom capture-button images at half size (292 px tall becomes 146 points, sharp). false = 1 pixel to 1 point (292 points, soft on a Retina screen)","type":"boolean"}}}}}}}},"/events/{eventId}/branding/images":{"post":{"operationId":"set_event_branding_image","summary":"Set the event's logo, a custom capture button, or a background image","description":"Replaces the image in one slot. Provide the image (PNG or JPEG, up to 8 MB) via uploadPath (create_upload with purpose 'event-branding-image', then PUT) or a public https sourceUrl. Each slot is shown in a different way, so each wants a different shape and size:\n\n- `logo`: the event logo. Supply it SQUARE, 1024×1024 px (at least 600×600; anything over 1024 px on a side is scaled down to fit). The iPad app, the emails guests receive and the dashboard show it as a circle cut from the middle, so keep the mark inside that circle: a wide logo loses its ends there. The guest gallery shows it whole, 184 px tall, and the client report up to 144 px tall. A PNG keeps its transparency. The gallery's single-photo page puts the logo on black; elsewhere it sits on the gallery's background or on white. A logo that only reads on one of them should carry its own background.\n\n- `after-capture-background`: the iPad's background after a capture, behind the preview, filters, AI prompt and share screens, in place of the built-in party image. It is not the home or capture screen: those show the live camera, or an attract screen (update_event_branding). The iPad does NOT resize it: 1 image pixel is 1 screen point, the image is centred, and whatever does not fit is cut off. So make it the size of the iPad's screen in points or a little larger, and never a full-size photo, which would show only its centre. The controls take the bottom 335 points, and on some of these screens the image is centred in the space above them, so the sizes that cover the whole screen are 1376×1367 px for an iPad mounted landscape and 1032×1711 px for portrait, or 1376×1711 px when the orientation is not known. These are for the largest iPad; a smaller one shows the middle. Keep the subject in the middle: clear of the controls, guests see about the middle 1376×697 px in landscape and 1032×1041 px in portrait. Stored as sent, up to 8000 px on a side.\n\n- `button-<type>`: a custom image for one capture button on the iPad home screen (photo, boomerang, slowmo, video, gif, aiPhoto, aiCustomPrompt, gallery; `gallery` is the gallery button). It replaces the whole built-in button, white tile, icon and label included, so the image has to say what it does. Use a transparent PNG: the camera view or the attract screen shows through. HEIGHT is what counts: make every button 292 px tall, any width (292×292 px for a square one). The iPad shows it at 1 pixel to 1 point, up to 292 points tall, which is large and looks soft on a Retina screen. With buttonImagesAreRetina (update_event_branding) it is shown at half size: 146 points tall and sharp. The built-in buttons are 200 points. More than 292 px of height adds nothing, the iPad shrinks it. Buttons sit in one row, 40 points apart, and the row scrolls sideways when it is wider than the screen (744 to 1376 points): four 146-point buttons fit any iPad. Give every capture type the event offers its own image: with buttonImagesAreRetina a built-in button left beside custom ones is drawn at 146 points without its icon. An animated PNG plays, as long as it is within 1024 px on each side (a larger image is scaled down to fit and becomes a still).\n\n- `gallery-background`: the background of the guest gallery's web pages. Supply it LANDSCAPE, 2560×1440 px (at least 1920×1080), as a JPEG. It fills the browser window, centred and cropped to fit, and stays in place while the page scrolls: a desktop sees its full width and a phone only the middle strip, so keep the subject in the centre third. Text is drawn straight on it in the gallery's textColor, with nothing in between: choose a calm image and a textColor that reads on it (update_gallery_settings); backgroundColor shows while it loads. It is fitted to 2560 px on its long edge and must end up under 2 MB. It shows on the gallery and session pages, not on the single-photo page, and not at all while the gallery's own header is hidden (customContent.hidesHeader). The client report puts it behind its heading when showHeroBackground is on.\n\nA logo or button image over 1024 px on a side is scaled down to fit, and a PNG keeps its transparency. The logo and the gallery background need an event above the Basic plan; buttons and the after-capture background need a Pro+ event.","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"eventId":{"type":"string"},"logoUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The event logo: a circle on the iPad and in the emails guests receive, shown whole at the top of the guest gallery"},"buttonImagesAreRetina":{"type":"boolean","description":"How the iPad sizes custom capture-button images. false = 1 pixel to 1 point, up to 292 points tall. true = half size, up to 146 points tall: sharp on a Retina screen"},"homeScreenBackgroundAttractScreenId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The attract screen shown full-screen behind the iPad home screen; null = the live camera view"},"afterCaptureBackgroundUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The iPad's after-capture background, behind the preview, filters and share screens; null = the built-in one. 7-day signed URL — re-fetch to refresh"},"captureButtonUrls":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"},"description":"Custom capture-button images by button (photo, boomerang, slowmo, video, gif, aiPhoto, aiCustomPrompt, gallery); a button not listed uses the built-in look. 7-day signed URLs"},"galleryBackgroundUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The background of the guest gallery's web pages; null = none"}},"required":["eventId","logoUrl","buttonImagesAreRetina","homeScreenBackgroundAttractScreenId","afterCaptureBackgroundUrl","captureButtonUrls","galleryBackgroundUrl"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"slot":{"type":"string","enum":["logo","after-capture-background","button-photo","button-boomerang","button-slowmo","button-video","button-gif","button-aiPhoto","button-aiCustomPrompt","button-gallery","gallery-background"],"description":"Which image, and in short what to send: 'logo' = the event logo, SQUARE 1024×1024 px, mark inside the centre circle; 'after-capture-background' = the iPad's background after a capture, shown unscaled at 1 pixel to 1 point, so about the iPad's own screen size: 1376×1367 px for a landscape iPad or 1032×1711 px for portrait; 'button-<type>' = a custom capture button (photo, boomerang, slowmo, video, gif, aiPhoto, aiCustomPrompt, gallery; 'gallery' is the home screen's gallery button), a transparent PNG 292 px tall; 'gallery-background' = the guest gallery's page background, LANDSCAPE 2560×1440 px JPEG. The tool description explains each."},"uploadPath":{"description":"storagePath from create_upload (after PUTting the bytes)","type":"string"},"sourceUrl":{"description":"Public https URL fetched server-side (must not redirect) — alternative to uploadPath","type":"string"}},"required":["slot"]}}}}},"delete":{"operationId":"remove_event_branding_image","summary":"Remove a custom capture button or a background image","description":"The button goes back to its built-in look; the after-capture background goes back to the built-in party image; the gallery goes back to its backgroundColor. Removing a slot that is already empty changes nothing. The logo can be replaced (set_event_branding_image) but not removed.","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}},{"name":"slot","in":"query","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","enum":["after-capture-background","button-photo","button-boomerang","button-slowmo","button-video","button-gif","button-aiPhoto","button-aiCustomPrompt","button-gallery","gallery-background"],"description":"'after-capture-background' (the iPad's background after a capture), 'button-<type>' (a custom capture button) or 'gallery-background' (the guest gallery's page background)"},"description":"'after-capture-background' (the iPad's background after a capture), 'button-<type>' (a custom capture button) or 'gallery-background' (the guest gallery's page background)"}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"eventId":{"type":"string"},"logoUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The event logo: a circle on the iPad and in the emails guests receive, shown whole at the top of the guest gallery"},"buttonImagesAreRetina":{"type":"boolean","description":"How the iPad sizes custom capture-button images. false = 1 pixel to 1 point, up to 292 points tall. true = half size, up to 146 points tall: sharp on a Retina screen"},"homeScreenBackgroundAttractScreenId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The attract screen shown full-screen behind the iPad home screen; null = the live camera view"},"afterCaptureBackgroundUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The iPad's after-capture background, behind the preview, filters and share screens; null = the built-in one. 7-day signed URL — re-fetch to refresh"},"captureButtonUrls":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"},"description":"Custom capture-button images by button (photo, boomerang, slowmo, video, gif, aiPhoto, aiCustomPrompt, gallery); a button not listed uses the built-in look. 7-day signed URLs"},"galleryBackgroundUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The background of the guest gallery's web pages; null = none"}},"required":["eventId","logoUrl","buttonImagesAreRetina","homeScreenBackgroundAttractScreenId","afterCaptureBackgroundUrl","captureButtonUrls","galleryBackgroundUrl"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/events/{eventId}/check":{"get":{"operationId":"check_event","summary":"Check what guests will get on the iPad: capture types, share options, printing","description":"Reads the event, its templates, AI prompts and gallery, and applies the iPad's own rules to say whether the iPad will open the event, and which of the chosen capture types, share options and printing guests will actually get, with the reason for anything they will not. The iPad applies these rules silently at the event, so check before it: after creating or changing an event, its templates, or their AI prompts. It changes nothing. Share options and printing are what an iPad in Capture & Share or Share Station mode offers; an iPad in Capture Station mode (stationType 'camera') offers neither. Some things depend on each iPad and cannot be checked here; deviceDependent lists the ones that matter for this event.","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"eventId":{"type":"string"},"opensOnIpad":{"type":"boolean","description":"false = the iPad shows the event as unable to open, with the reasons in problems"},"problems":{"type":"array","items":{"type":"string"},"description":"Why the iPad will not open the event; empty when it will"},"captureTypes":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["photo","boomerang","slowmo","video","gif","aiPhoto","aiCustomPrompt"]},"status":{"type":"string","enum":["offered","left_out","blocks_event"],"description":"offered = guests get it. left_out = the iPad quietly leaves it out and runs the event without it. blocks_event = the iPad refuses to open the event because of it (opensOnIpad is false) — fix it or take it out of settings.captureTypes"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Why it is not offered; null when it is"},"templateIds":{"type":"array","items":{"type":"string"},"description":"The event's templates guests can use with it"}},"required":["type","status","reason","templateIds"],"additionalProperties":false},"description":"Each capture type in settings.captureTypes, in its order"},"shareActions":{"type":"array","items":{"type":"object","properties":{"action":{"type":"string","enum":["email","sms","qrCode","airdrop"]},"status":{"type":"string","enum":["offered","left_out"]},"reason":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["action","status","reason"],"additionalProperties":false},"description":"Each share option in settings.actions, as an iPad in Capture & Share or Share Station mode offers it. An iPad in Capture Station mode offers none (deviceDependent)"},"printing":{"type":"object","properties":{"offered":{"type":"boolean","description":"Whether guests can print anything at this event, on an iPad with a printer set up in Capture & Share or Share Station mode. An iPad in Capture Station mode prints nothing (deviceDependent)"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Why not, when offered is false"},"notPrintableTemplateIds":{"type":"array","items":{"type":"string"},"description":"The event's templates set not to print (update_template printable: false): captures made with them get no Print button"},"printCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Prints made so far (get_event printCount)"},"printCountMax":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"settings.printCountMax; null = no limit"},"limitReached":{"type":"boolean","description":"true = the iPads now refuse to print ('The maximum number of prints has been reached for this event')"}},"required":["offered","reason","notPrintableTemplateIds","printCount","printCountMax","limitReached"],"additionalProperties":false},"deviceDependent":{"type":"array","items":{"type":"string"},"description":"What depends on each iPad and cannot be checked here, for this event"}},"required":["eventId","opensOnIpad","problems","captureTypes","shareActions","printing","deviceDependent"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/events/{eventId}/survey":{"get":{"operationId":"get_event_survey","summary":"The event's survey: the questions guests answer, in two stages","description":"Both stages with their questions in order, whether a stage is switched on or not, and the questions the event's templates and AI prompts own that could be placed (`available`). Answers are read with export_survey_responses.","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"eventId":{"type":"string"},"preSession":{"type":"object","properties":{"enabled":{"type":"boolean"},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"text":{"anyOf":[{"type":"string"},{"type":"null"}]},"optional":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"options":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"For a choice or an access code"},"choices":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"}},"required":["id","text"],"additionalProperties":false}},{"type":"null"}],"description":"For a template or prompt choice field"},"content":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"For rich text: the HTML, inside its styling wrapper"},"owner":{"type":"object","properties":{"kind":{"type":"string"},"id":{"type":"string"}},"required":["kind","id"],"additionalProperties":false,"description":"Who owns the question: this event, or a template or AI prompt on it ('prompt')"},"editable":{"type":"boolean","description":"true = the event's own, changed through update_event_survey. false = a template's or prompt's: it can be placed or taken out here, not edited"}},"required":["id","type","name","text","optional","options","choices","content","owner","editable"],"additionalProperties":false}}},"required":["enabled","items"],"additionalProperties":false,"description":"Asked before the session starts"},"postCapture":{"type":"object","properties":{"enabled":{"type":"boolean"},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"text":{"anyOf":[{"type":"string"},{"type":"null"}]},"optional":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"options":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"For a choice or an access code"},"choices":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"}},"required":["id","text"],"additionalProperties":false}},{"type":"null"}],"description":"For a template or prompt choice field"},"content":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"For rich text: the HTML, inside its styling wrapper"},"owner":{"type":"object","properties":{"kind":{"type":"string"},"id":{"type":"string"}},"required":["kind","id"],"additionalProperties":false,"description":"Who owns the question: this event, or a template or AI prompt on it ('prompt')"},"editable":{"type":"boolean","description":"true = the event's own, changed through update_event_survey. false = a template's or prompt's: it can be placed or taken out here, not edited"}},"required":["id","type","name","text","optional","options","choices","content","owner","editable"],"additionalProperties":false}}},"required":["enabled","items"],"additionalProperties":false,"description":"Asked after the capture"},"available":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"text":{"anyOf":[{"type":"string"},{"type":"null"}]},"optional":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"options":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"For a choice or an access code"},"choices":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"}},"required":["id","text"],"additionalProperties":false}},{"type":"null"}],"description":"For a template or prompt choice field"},"content":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"For rich text: the HTML, inside its styling wrapper"},"owner":{"type":"object","properties":{"kind":{"type":"string"},"id":{"type":"string"}},"required":["kind","id"],"additionalProperties":false,"description":"Who owns the question: this event, or a template or AI prompt on it ('prompt')"},"editable":{"type":"boolean","description":"true = the event's own, changed through update_event_survey. false = a template's or prompt's: it can be placed or taken out here, not edited"}},"required":["id","type","name","text","optional","options","choices","content","owner","editable"],"additionalProperties":false},"description":"Questions owned by the event's templates and AI prompts that are not in either stage. Place one by sending { fieldId } in a stage. A template's or prompt's question may be asked in both stages: send its { fieldId } in each"}},"required":["eventId","preSession","postCapture","available"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}},"patch":{"operationId":"update_event_survey","summary":"Replace the event's survey","description":"REPLACES the whole survey, like Save in the dashboard's survey editor — send both stages complete, in order. In each stage: a question with no `id` is created; one with its `id` is updated (its type cannot change); { fieldId } keeps an existing question as it is, or places one that a template or AI prompt on the event owns (such a question may be in both stages); one of the event's own questions that is left out is DELETED. A question as get_event_survey returns it can be sent back as it is. The response returns every question's id — send ids back on the next call, or the questions are created again. A stage switched off keeps its questions and asks none. Older iPad builds ask one stage only: the pre-session one when both have questions. Not available on an event created on the Basic plan.","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"eventId":{"type":"string"},"preSession":{"type":"object","properties":{"enabled":{"type":"boolean"},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"text":{"anyOf":[{"type":"string"},{"type":"null"}]},"optional":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"options":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"For a choice or an access code"},"choices":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"}},"required":["id","text"],"additionalProperties":false}},{"type":"null"}],"description":"For a template or prompt choice field"},"content":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"For rich text: the HTML, inside its styling wrapper"},"owner":{"type":"object","properties":{"kind":{"type":"string"},"id":{"type":"string"}},"required":["kind","id"],"additionalProperties":false,"description":"Who owns the question: this event, or a template or AI prompt on it ('prompt')"},"editable":{"type":"boolean","description":"true = the event's own, changed through update_event_survey. false = a template's or prompt's: it can be placed or taken out here, not edited"}},"required":["id","type","name","text","optional","options","choices","content","owner","editable"],"additionalProperties":false}}},"required":["enabled","items"],"additionalProperties":false,"description":"Asked before the session starts"},"postCapture":{"type":"object","properties":{"enabled":{"type":"boolean"},"items":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"text":{"anyOf":[{"type":"string"},{"type":"null"}]},"optional":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"options":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"For a choice or an access code"},"choices":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"}},"required":["id","text"],"additionalProperties":false}},{"type":"null"}],"description":"For a template or prompt choice field"},"content":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"For rich text: the HTML, inside its styling wrapper"},"owner":{"type":"object","properties":{"kind":{"type":"string"},"id":{"type":"string"}},"required":["kind","id"],"additionalProperties":false,"description":"Who owns the question: this event, or a template or AI prompt on it ('prompt')"},"editable":{"type":"boolean","description":"true = the event's own, changed through update_event_survey. false = a template's or prompt's: it can be placed or taken out here, not edited"}},"required":["id","type","name","text","optional","options","choices","content","owner","editable"],"additionalProperties":false}}},"required":["enabled","items"],"additionalProperties":false,"description":"Asked after the capture"},"available":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"text":{"anyOf":[{"type":"string"},{"type":"null"}]},"optional":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"options":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"For a choice or an access code"},"choices":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"}},"required":["id","text"],"additionalProperties":false}},{"type":"null"}],"description":"For a template or prompt choice field"},"content":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"For rich text: the HTML, inside its styling wrapper"},"owner":{"type":"object","properties":{"kind":{"type":"string"},"id":{"type":"string"}},"required":["kind","id"],"additionalProperties":false,"description":"Who owns the question: this event, or a template or AI prompt on it ('prompt')"},"editable":{"type":"boolean","description":"true = the event's own, changed through update_event_survey. false = a template's or prompt's: it can be placed or taken out here, not edited"}},"required":["id","type","name","text","optional","options","choices","content","owner","editable"],"additionalProperties":false},"description":"Questions owned by the event's templates and AI prompts that are not in either stage. Place one by sending { fieldId } in a stage. A template's or prompt's question may be asked in both stages: send its { fieldId } in each"}},"required":["eventId","preSession","postCapture","available"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"preSession":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Off keeps the questions but asks none of them"},"items":{"maxItems":100,"type":"array","items":{"anyOf":[{"oneOf":[{"type":"object","properties":{"id":{"description":"The question's id, to update it. Leave out to create a new question","type":"string","minLength":1,"maxLength":64},"fieldId":{"not":{}},"type":{"type":"string","const":"full_name"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (email, phone, consent, …)","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"text":{"description":"What the guest reads","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"optional":{"description":"Whether a guest may skip it. Default false","anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["type"],"description":"The guest's name"},{"type":"object","properties":{"id":{"description":"The question's id, to update it. Leave out to create a new question","type":"string","minLength":1,"maxLength":64},"fieldId":{"not":{}},"type":{"type":"string","const":"email"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (email, phone, consent, …)","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"text":{"description":"What the guest reads","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"optional":{"description":"Whether a guest may skip it. Default false","anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["type"],"description":"An email address"},{"type":"object","properties":{"id":{"description":"The question's id, to update it. Leave out to create a new question","type":"string","minLength":1,"maxLength":64},"fieldId":{"not":{}},"type":{"type":"string","const":"phone_number"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (email, phone, consent, …)","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"text":{"description":"What the guest reads","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"optional":{"description":"Whether a guest may skip it. Default false","anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["type"],"description":"A phone number"},{"type":"object","properties":{"id":{"description":"The question's id, to update it. Leave out to create a new question","type":"string","minLength":1,"maxLength":64},"fieldId":{"not":{}},"type":{"type":"string","const":"date"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (email, phone, consent, …)","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"text":{"description":"What the guest reads","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"optional":{"description":"Whether a guest may skip it. Default false","anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["type"],"description":"A date"},{"type":"object","properties":{"id":{"description":"The question's id, to update it. Leave out to create a new question","type":"string","minLength":1,"maxLength":64},"fieldId":{"not":{}},"type":{"type":"string","const":"string"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (email, phone, consent, …)","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"text":{"description":"What the guest reads","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"optional":{"description":"Whether a guest may skip it. Default false","anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["type"],"description":"A line of free text"},{"type":"object","properties":{"id":{"description":"The question's id, to update it. Leave out to create a new question","type":"string","minLength":1,"maxLength":64},"fieldId":{"not":{}},"type":{"type":"string","const":"checkbox"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (email, phone, consent, …)","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"What the guest ticks, e.g. \"I agree\""},"optional":{"description":"Whether a guest may skip it. Default false","anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["type","text"],"description":"A tick box, typically consent"},{"type":"object","properties":{"id":{"description":"The question's id, to update it. Leave out to create a new question","type":"string","minLength":1,"maxLength":64},"fieldId":{"not":{}},"type":{"type":"string","const":"segment"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (email, phone, consent, …)","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"text":{"description":"What the guest reads","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"optional":{"description":"Whether a guest may skip it. Default false","anyOf":[{"type":"boolean"},{"type":"null"}]},"options":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"string","minLength":1,"maxLength":200},"description":"The choices offered"}},"required":["type","options"],"description":"A choice from a short list"},{"type":"object","properties":{"id":{"description":"The question's id, to update it. Leave out to create a new question","type":"string","minLength":1,"maxLength":64},"fieldId":{"not":{}},"type":{"type":"string","const":"password"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (email, phone, consent, …)","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"text":{"description":"What the guest reads","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"optional":{"description":"Whether a guest may skip it. Default false","anyOf":[{"type":"boolean"},{"type":"null"}]},"options":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"string","minLength":1,"maxLength":200},"description":"The codes that are accepted"}},"required":["type","options"],"description":"An access code the guest must enter"},{"type":"object","properties":{"id":{"description":"The question's id, to update it. Leave out to create a new question","type":"string","minLength":1,"maxLength":64},"fieldId":{"not":{}},"type":{"type":"string","const":"section_title"},"text":{"type":"string","minLength":1,"maxLength":1000}},"required":["type","text"],"description":"A heading. It asks nothing"},{"type":"object","properties":{"id":{"description":"The question's id, to update it. Leave out to create a new question","type":"string","minLength":1,"maxLength":64},"fieldId":{"not":{}},"type":{"type":"string","const":"html_block"},"name":{"description":"A label for the block in the editor. Left out (or null): none","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"content":{"type":"string","minLength":1,"maxLength":25000,"description":"HTML, shown to the guest as written"}},"required":["type","content"],"description":"Rich text, e.g. terms the guest reads before agreeing. It asks nothing"}]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64},"editable":{"type":"boolean","const":false}},"required":["id","editable"],"description":"A template's or AI prompt's question as get_event_survey returns it (editable false): kept as it is, like { fieldId }"},{"type":"object","properties":{"fieldId":{"type":"string","minLength":1,"maxLength":64}},"required":["fieldId"],"additionalProperties":false,"description":"An existing question, kept as it is: one of the event's own, or one a template or AI prompt on the event owns (see `available`)"}]},"description":"In the order they are asked"}},"required":["enabled","items"],"description":"Asked before the session starts"},"postCapture":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Off keeps the questions but asks none of them"},"items":{"maxItems":100,"type":"array","items":{"anyOf":[{"oneOf":[{"type":"object","properties":{"id":{"description":"The question's id, to update it. Leave out to create a new question","type":"string","minLength":1,"maxLength":64},"fieldId":{"not":{}},"type":{"type":"string","const":"full_name"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (email, phone, consent, …)","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"text":{"description":"What the guest reads","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"optional":{"description":"Whether a guest may skip it. Default false","anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["type"],"description":"The guest's name"},{"type":"object","properties":{"id":{"description":"The question's id, to update it. Leave out to create a new question","type":"string","minLength":1,"maxLength":64},"fieldId":{"not":{}},"type":{"type":"string","const":"email"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (email, phone, consent, …)","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"text":{"description":"What the guest reads","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"optional":{"description":"Whether a guest may skip it. Default false","anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["type"],"description":"An email address"},{"type":"object","properties":{"id":{"description":"The question's id, to update it. Leave out to create a new question","type":"string","minLength":1,"maxLength":64},"fieldId":{"not":{}},"type":{"type":"string","const":"phone_number"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (email, phone, consent, …)","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"text":{"description":"What the guest reads","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"optional":{"description":"Whether a guest may skip it. Default false","anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["type"],"description":"A phone number"},{"type":"object","properties":{"id":{"description":"The question's id, to update it. Leave out to create a new question","type":"string","minLength":1,"maxLength":64},"fieldId":{"not":{}},"type":{"type":"string","const":"date"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (email, phone, consent, …)","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"text":{"description":"What the guest reads","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"optional":{"description":"Whether a guest may skip it. Default false","anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["type"],"description":"A date"},{"type":"object","properties":{"id":{"description":"The question's id, to update it. Leave out to create a new question","type":"string","minLength":1,"maxLength":64},"fieldId":{"not":{}},"type":{"type":"string","const":"string"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (email, phone, consent, …)","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"text":{"description":"What the guest reads","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"optional":{"description":"Whether a guest may skip it. Default false","anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["type"],"description":"A line of free text"},{"type":"object","properties":{"id":{"description":"The question's id, to update it. Leave out to create a new question","type":"string","minLength":1,"maxLength":64},"fieldId":{"not":{}},"type":{"type":"string","const":"checkbox"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (email, phone, consent, …)","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"What the guest ticks, e.g. \"I agree\""},"optional":{"description":"Whether a guest may skip it. Default false","anyOf":[{"type":"boolean"},{"type":"null"}]}},"required":["type","text"],"description":"A tick box, typically consent"},{"type":"object","properties":{"id":{"description":"The question's id, to update it. Leave out to create a new question","type":"string","minLength":1,"maxLength":64},"fieldId":{"not":{}},"type":{"type":"string","const":"segment"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (email, phone, consent, …)","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"text":{"description":"What the guest reads","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"optional":{"description":"Whether a guest may skip it. Default false","anyOf":[{"type":"boolean"},{"type":"null"}]},"options":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"string","minLength":1,"maxLength":200},"description":"The choices offered"}},"required":["type","options"],"description":"A choice from a short list"},{"type":"object","properties":{"id":{"description":"The question's id, to update it. Leave out to create a new question","type":"string","minLength":1,"maxLength":64},"fieldId":{"not":{}},"type":{"type":"string","const":"password"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (email, phone, consent, …)","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"text":{"description":"What the guest reads","anyOf":[{"type":"string","maxLength":1000},{"type":"null"}]},"optional":{"description":"Whether a guest may skip it. Default false","anyOf":[{"type":"boolean"},{"type":"null"}]},"options":{"minItems":1,"maxItems":100,"type":"array","items":{"type":"string","minLength":1,"maxLength":200},"description":"The codes that are accepted"}},"required":["type","options"],"description":"An access code the guest must enter"},{"type":"object","properties":{"id":{"description":"The question's id, to update it. Leave out to create a new question","type":"string","minLength":1,"maxLength":64},"fieldId":{"not":{}},"type":{"type":"string","const":"section_title"},"text":{"type":"string","minLength":1,"maxLength":1000}},"required":["type","text"],"description":"A heading. It asks nothing"},{"type":"object","properties":{"id":{"description":"The question's id, to update it. Leave out to create a new question","type":"string","minLength":1,"maxLength":64},"fieldId":{"not":{}},"type":{"type":"string","const":"html_block"},"name":{"description":"A label for the block in the editor. Left out (or null): none","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"content":{"type":"string","minLength":1,"maxLength":25000,"description":"HTML, shown to the guest as written"}},"required":["type","content"],"description":"Rich text, e.g. terms the guest reads before agreeing. It asks nothing"}]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64},"editable":{"type":"boolean","const":false}},"required":["id","editable"],"description":"A template's or AI prompt's question as get_event_survey returns it (editable false): kept as it is, like { fieldId }"},{"type":"object","properties":{"fieldId":{"type":"string","minLength":1,"maxLength":64}},"required":["fieldId"],"additionalProperties":false,"description":"An existing question, kept as it is: one of the event's own, or one a template or AI prompt on the event owns (see `available`)"}]},"description":"In the order they are asked"}},"required":["enabled","items"],"description":"Asked after the capture"}},"required":["preSession","postCapture"]}}}}}},"/events/{eventId}/gallery":{"get":{"operationId":"get_gallery_settings","summary":"Get the event gallery's passcode, privacy settings, look and lifecycle","description":"lifecycle says where the gallery is in its life, as the dashboard's event page shows it: whether guests can still open it, when it expires and how many days are left. Check it before telling a client their photos are online, and warn them before expiryDate. update_gallery_settings says what guests see on the gallery and on their own page, and which settings change it.","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}},{"name":"include_passcodes","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","description":"Return the passcodes themselves, not just hasPasscode/hasSuperPasscode. Off by default because this is an MCP tool too, and cleartext passcodes would otherwise land in a third-party model's context on every call.","type":"boolean"},"description":"Return the passcodes themselves, not just hasPasscode/hasSuperPasscode. Off by default because this is an MCP tool too, and cleartext passcodes would otherwise land in a third-party model's context on every call."}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"galleryId":{"type":"string"},"hasPasscode":{"type":"boolean","description":"Whether a guest access passcode is set"},"hasSuperPasscode":{"type":"boolean","description":"Whether a download-tier passcode is set"},"passcode":{"description":"Only present with include_passcodes=true. Guest access passcode; null = none","anyOf":[{"type":"string"},{"type":"null"}]},"superPasscode":{"description":"Only present with include_passcodes=true. Download-tier passcode; null = none","anyOf":[{"type":"string"},{"type":"null"}]},"passcodeAppliesToSessions":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"The dashboard's 'Also require the passcode for individual sessions'. true = guests must type the gallery passcode to open their own link and single-photo links too. null = never set (off)"},"sessionsOnlyFlag":{"type":"boolean","description":"true = each guest sees only their own photos: the dashboard's 'Restrict gallery to admins only' (its event page says 'Guests limited to own photos'). A guest's own session link, from their email, text or QR code, still shows their photos, but none of its three links to the gallery. The gallery link and the slideshow show 'This gallery is private: The event organizer configured it so that guests can only see their own photos.' instead of the photos. The gallery passcode does not open it. The admin passcode (superPasscode) does, and it also lets whoever holds it delete photos and download the ZIP; so do a ZIP-download link emailed from the dashboard, the account and its team when signed in, and the iPad running the event (a Share Station still shows every photo). The admin passcode is typed at the passcode prompt, which the gallery link shows only when a passcode is set too, so set passcode, superPasscode and sessionsOnlyFlag together, as the dashboard's 'Admin access only' presets do (they also set shareHidesPasscode, and passcodeAppliesToSessions makes guests enter the passcode for their own session link as well). hideTenantHeader and hideLoginAction also remove the sign-in button from that page. The client report and this API's list_media / list_sessions are not affected. false (also when never set) = anyone with the gallery link sees every photo, behind the passcode when one is set"},"downloadGalleryZipRestricted":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true or null (never set, as for a new gallery) = the gallery page offers its ZIP of every photo only to admins (the admin passcode, or the account signed in) and to a send_gallery_download_link link. false = to every guest who can open the gallery: the dashboard's 'Guests can download the gallery ZIP'. It does not change a guest's own page, whose menu offers a ZIP of their own photos"},"shareHidesPasscode":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true = guests' emails and texts leave the passcode out, so you give it out yourself: the dashboard's 'Send the passcode to guests via email and text' switched off. null = never set (the passcode is sent)"},"hideTenantHeader":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true = hide the bar across the top of the gallery: the account's logo and name, the sign-in button and, on a guest's own page, its 'Back to gallery' link. The event's own header under it (event logo, name, date, social links) stays"},"hideLoginAction":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true = hide only the sign-in button at the right of that top bar (hiding the whole bar hides it too)"},"hideEventDate":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true = hide the event's date in the event header, under its name"},"hideEventName":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true = hide the event's name in the event header, under its logo. On a guest's own page the name is a link to the gallery, which goes with it"},"numberOfDaysTilExpiry":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"How long the gallery lasts, in days from its first upload (see lifecycle.expiryDate); null = not recorded"},"lifecycle":{"type":"object","properties":{"status":{"type":"string","enum":["active","expired","files-deleted"],"description":"active = guests can open the gallery and their own session links. expired = the gallery has expired: the gallery link, every guest's session link, single-photo links and the slideshow all show a 'Gallery Expired' page ('This gallery has expired and is no longer available. Please contact the event organizer for more information.'), and the iPad will not open the event. The photos and videos are still stored for a limited time (the dashboard says 'Expired, available for purchase for a limited time'), then deleted. files-deleted = expired, and the photos and videos have been deleted (the dashboard says 'Expired'); guests see the same 'Gallery Expired' page. Expiry cannot be undone through the API, so warn the client before expiryDate"},"expiryDate":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"While active, when the gallery expires (ISO 8601): the dashboard's 'Expires on {date}'. Guests can open the gallery until then; it closes at shared.gallery's nightly expiry run after this time. null while active = nothing has been uploaded yet: the date is set at the first upload, numberOfDaysTilExpiry days after it, and uploads during the next 14 days move it later by as much. null once expired"},"daysLeft":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Whole days from now until expiryDate, rounded down; 0 = less than a day left (the dashboard says 'Expires today'). null when expiryDate is null. shared.gallery emails the account once, about 4 days before a gallery with photos expires (for a paid event, only while zipDownloadedAt is null); tell the client earlier if they need time to collect the photos"},"zipDownloadedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When the account owner, signed in, last downloaded the whole gallery as a ZIP (ISO 8601); the dashboard adds 'Downloaded by you on {date}' to the expiry line. null = never"}},"required":["status","expiryDate","daysLeft","zipDownloadedAt"],"additionalProperties":false,"description":"Where the gallery is in its life, as the dashboard's event page shows it in its expiry line. Use it to tell the client what guests can see now and to warn them before the gallery expires"},"galleryUrlMode":{"anyOf":[{"type":"string","enum":["custom-domain","default","slug-vanity"]},{"type":"null"}],"description":"null = legacy default (custom domain when one is enabled)"},"slugVanity":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The /e/<slug> vanity path; set via set_gallery_url"},"galleryUrl":{"type":"string","description":"The guest-facing gallery URL under the current mode"},"primaryColor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"#rrggbb. The gallery's main colour: the bar at the top, the buttons, the social icons and the links, and the button in the email guests receive. White text sits on it everywhere, so a very light colour is refused"},"secondaryColor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"#rrggbb. The colour of the Message & Button button, and the default for content buttons. White text sits on it too, so a very light colour is refused"},"textColor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"#rrggbb. The event name and the text of the page, drawn on backgroundColor or straight on the background image, so it has to read on whichever shows. null = black"},"backgroundColor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"#rrggbb. The page background, also seen while a background image loads. null = white"},"backgroundImageUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The page background image, landscape, filling the browser window. Set and removed with set_event_branding_image, slot 'gallery-background', which says what size to send"},"socialUrls":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},{"type":"null"}],"description":"The gallery's own social links, shown as round icons in the primary colour under the event name; null = the account's links are shown"},"customContent":{"type":"object","properties":{"mode":{"type":"string","enum":["off","header","blocks"]},"header":{"anyOf":[{"type":"object","properties":{"headerText":{"description":"The heading: bold, centred, one short line","type":"string","maxLength":200},"text":{"description":"The message under the heading","type":"string","maxLength":1000},"buttonText":{"description":"A full-width button in secondaryColor with white text. Shown only with a buttonLink","type":"string","maxLength":120},"buttonLink":{"description":"Where the button goes","type":"string","maxLength":2048}},"additionalProperties":false},{"type":"null"}]},"topBlocks":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"}},"required":["type"],"additionalProperties":{}}},"bottomBlocks":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"}},"required":["type"],"additionalProperties":{}}},"hidesHeader":{"type":"boolean"}},"required":["mode","header","topBlocks","bottomBlocks","hidesHeader"],"additionalProperties":false,"description":"The gallery's own content: none, a Message & Button, or Content Builder blocks"},"shareStationHtml":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"HTML shown at the top of the gallery on an iPad running as a Share Station. It takes the full width of the screen and grows to the height of its content, pushing the gallery down, so keep it short. Links in it do not open. Give images an absolute https address"}},"required":["galleryId","hasPasscode","hasSuperPasscode","passcodeAppliesToSessions","sessionsOnlyFlag","downloadGalleryZipRestricted","shareHidesPasscode","hideTenantHeader","hideLoginAction","hideEventDate","hideEventName","numberOfDaysTilExpiry","lifecycle","galleryUrlMode","slugVanity","galleryUrl","primaryColor","secondaryColor","textColor","backgroundColor","backgroundImageUrl","socialUrls","customContent","shareStationHtml"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}},"patch":{"operationId":"update_gallery_settings","summary":"Set the gallery's passcode, privacy settings and look","description":"Partial update: only the fields you send change. null or an empty string clears passcode/superPasscode. The look is the dashboard's Branding tab. Colours are #rrggbb: primaryColor (the top bar, buttons, icons and links; white text sits on it, so it must not be too light), secondaryColor (the Message & Button button, also under white text), textColor and backgroundColor (null = black on white). With a background image the text is drawn straight on the image, so set a textColor that reads on it. socialUrls are the gallery's own links (null = show the account's). customContent is exactly one of mode 'off', 'header' (a Message & Button: a heading, a message and one full-width button, in a centred column) or 'blocks' (Content Builder: section titles, rich-text HTML and buttons above and below the guest media); choosing one clears the other two, blank blocks are not stored, no two blocks may share an id, and the blocks may come to at most 200 KB. Guests reach the photos on two kinds of page. The gallery link (galleryUrl) shows every guest's photos. Each guest's own link (sessionUrl in list_sessions), which their email, their text and the iPad's QR code carry, opens their own page. By default it shows only their photos and videos, each opening full size with a Download button, plus a ZIP of them in its menu when there are 2 or more. It also has three links to the whole gallery: 'Back to gallery' in the top bar, the event name in the header, and a 'Back to gallery' button under the photos. Choices: passcode (the gallery link asks for it), passcodeAppliesToSessions (the guest's own link asks too), shareHidesPasscode (the passcode is left out of emails and texts), downloadGalleryZipRestricted (only admins get the gallery's ZIP), superPasscode (an admin passcode) and sessionsOnlyFlag (guests see only their own photos, and their page loses all three links to the gallery). No setting removes those links while guests can still open the gallery: hideTenantHeader removes the top bar with its link, and hideEventName the event name, but the button under the photos stays. The dashboard's Access tab presets set these together (every one not named is off): 'Anyone with the link' = downloadGalleryZipRestricted; 'Password-protected' = passcode, downloadGalleryZipRestricted; 'Password-protected, admin access on' = passcode, superPasscode, downloadGalleryZipRestricted; 'Password-protected for a client' = passcode, shareHidesPasscode, downloadGalleryZipRestricted; 'Admin access only' = passcode, superPasscode, sessionsOnlyFlag, shareHidesPasscode, downloadGalleryZipRestricted; 'Admin access only, sessions protected' = the same plus passcodeAppliesToSessions. All of this is the guests' web pages. The iPad has its own gallery buttons, set with update_event: settings.galleryButtonOnHomeScreen (on its home screen) and settings.menuGalleryButtonsShown (in its in-event menu); settings.passcodeOnQRScreen shows the passcode beside its QR code. Read sessionsOnlyFlag's description before switching it on. shareStationHtml is the HTML shown at the top of the gallery on an iPad running as a Share Station. The logo and the background image are set with set_event_branding_image, which says what shape and size to send. Plans: an event created on Basic takes colours only; blocks and shareStationHtml need a Pro+ event.","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"galleryId":{"type":"string"},"hasPasscode":{"type":"boolean","description":"Whether a guest access passcode is set"},"hasSuperPasscode":{"type":"boolean","description":"Whether a download-tier passcode is set"},"passcode":{"description":"Only present with include_passcodes=true. Guest access passcode; null = none","anyOf":[{"type":"string"},{"type":"null"}]},"superPasscode":{"description":"Only present with include_passcodes=true. Download-tier passcode; null = none","anyOf":[{"type":"string"},{"type":"null"}]},"passcodeAppliesToSessions":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"The dashboard's 'Also require the passcode for individual sessions'. true = guests must type the gallery passcode to open their own link and single-photo links too. null = never set (off)"},"sessionsOnlyFlag":{"type":"boolean","description":"true = each guest sees only their own photos: the dashboard's 'Restrict gallery to admins only' (its event page says 'Guests limited to own photos'). A guest's own session link, from their email, text or QR code, still shows their photos, but none of its three links to the gallery. The gallery link and the slideshow show 'This gallery is private: The event organizer configured it so that guests can only see their own photos.' instead of the photos. The gallery passcode does not open it. The admin passcode (superPasscode) does, and it also lets whoever holds it delete photos and download the ZIP; so do a ZIP-download link emailed from the dashboard, the account and its team when signed in, and the iPad running the event (a Share Station still shows every photo). The admin passcode is typed at the passcode prompt, which the gallery link shows only when a passcode is set too, so set passcode, superPasscode and sessionsOnlyFlag together, as the dashboard's 'Admin access only' presets do (they also set shareHidesPasscode, and passcodeAppliesToSessions makes guests enter the passcode for their own session link as well). hideTenantHeader and hideLoginAction also remove the sign-in button from that page. The client report and this API's list_media / list_sessions are not affected. false (also when never set) = anyone with the gallery link sees every photo, behind the passcode when one is set"},"downloadGalleryZipRestricted":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true or null (never set, as for a new gallery) = the gallery page offers its ZIP of every photo only to admins (the admin passcode, or the account signed in) and to a send_gallery_download_link link. false = to every guest who can open the gallery: the dashboard's 'Guests can download the gallery ZIP'. It does not change a guest's own page, whose menu offers a ZIP of their own photos"},"shareHidesPasscode":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true = guests' emails and texts leave the passcode out, so you give it out yourself: the dashboard's 'Send the passcode to guests via email and text' switched off. null = never set (the passcode is sent)"},"hideTenantHeader":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true = hide the bar across the top of the gallery: the account's logo and name, the sign-in button and, on a guest's own page, its 'Back to gallery' link. The event's own header under it (event logo, name, date, social links) stays"},"hideLoginAction":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true = hide only the sign-in button at the right of that top bar (hiding the whole bar hides it too)"},"hideEventDate":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true = hide the event's date in the event header, under its name"},"hideEventName":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true = hide the event's name in the event header, under its logo. On a guest's own page the name is a link to the gallery, which goes with it"},"numberOfDaysTilExpiry":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"How long the gallery lasts, in days from its first upload (see lifecycle.expiryDate); null = not recorded"},"lifecycle":{"type":"object","properties":{"status":{"type":"string","enum":["active","expired","files-deleted"],"description":"active = guests can open the gallery and their own session links. expired = the gallery has expired: the gallery link, every guest's session link, single-photo links and the slideshow all show a 'Gallery Expired' page ('This gallery has expired and is no longer available. Please contact the event organizer for more information.'), and the iPad will not open the event. The photos and videos are still stored for a limited time (the dashboard says 'Expired, available for purchase for a limited time'), then deleted. files-deleted = expired, and the photos and videos have been deleted (the dashboard says 'Expired'); guests see the same 'Gallery Expired' page. Expiry cannot be undone through the API, so warn the client before expiryDate"},"expiryDate":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"While active, when the gallery expires (ISO 8601): the dashboard's 'Expires on {date}'. Guests can open the gallery until then; it closes at shared.gallery's nightly expiry run after this time. null while active = nothing has been uploaded yet: the date is set at the first upload, numberOfDaysTilExpiry days after it, and uploads during the next 14 days move it later by as much. null once expired"},"daysLeft":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Whole days from now until expiryDate, rounded down; 0 = less than a day left (the dashboard says 'Expires today'). null when expiryDate is null. shared.gallery emails the account once, about 4 days before a gallery with photos expires (for a paid event, only while zipDownloadedAt is null); tell the client earlier if they need time to collect the photos"},"zipDownloadedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When the account owner, signed in, last downloaded the whole gallery as a ZIP (ISO 8601); the dashboard adds 'Downloaded by you on {date}' to the expiry line. null = never"}},"required":["status","expiryDate","daysLeft","zipDownloadedAt"],"additionalProperties":false,"description":"Where the gallery is in its life, as the dashboard's event page shows it in its expiry line. Use it to tell the client what guests can see now and to warn them before the gallery expires"},"galleryUrlMode":{"anyOf":[{"type":"string","enum":["custom-domain","default","slug-vanity"]},{"type":"null"}],"description":"null = legacy default (custom domain when one is enabled)"},"slugVanity":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The /e/<slug> vanity path; set via set_gallery_url"},"galleryUrl":{"type":"string","description":"The guest-facing gallery URL under the current mode"},"primaryColor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"#rrggbb. The gallery's main colour: the bar at the top, the buttons, the social icons and the links, and the button in the email guests receive. White text sits on it everywhere, so a very light colour is refused"},"secondaryColor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"#rrggbb. The colour of the Message & Button button, and the default for content buttons. White text sits on it too, so a very light colour is refused"},"textColor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"#rrggbb. The event name and the text of the page, drawn on backgroundColor or straight on the background image, so it has to read on whichever shows. null = black"},"backgroundColor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"#rrggbb. The page background, also seen while a background image loads. null = white"},"backgroundImageUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The page background image, landscape, filling the browser window. Set and removed with set_event_branding_image, slot 'gallery-background', which says what size to send"},"socialUrls":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},{"type":"null"}],"description":"The gallery's own social links, shown as round icons in the primary colour under the event name; null = the account's links are shown"},"customContent":{"type":"object","properties":{"mode":{"type":"string","enum":["off","header","blocks"]},"header":{"anyOf":[{"type":"object","properties":{"headerText":{"description":"The heading: bold, centred, one short line","type":"string","maxLength":200},"text":{"description":"The message under the heading","type":"string","maxLength":1000},"buttonText":{"description":"A full-width button in secondaryColor with white text. Shown only with a buttonLink","type":"string","maxLength":120},"buttonLink":{"description":"Where the button goes","type":"string","maxLength":2048}},"additionalProperties":false},{"type":"null"}]},"topBlocks":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"}},"required":["type"],"additionalProperties":{}}},"bottomBlocks":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"}},"required":["type"],"additionalProperties":{}}},"hidesHeader":{"type":"boolean"}},"required":["mode","header","topBlocks","bottomBlocks","hidesHeader"],"additionalProperties":false,"description":"The gallery's own content: none, a Message & Button, or Content Builder blocks"},"shareStationHtml":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"HTML shown at the top of the gallery on an iPad running as a Share Station. It takes the full width of the screen and grows to the height of its content, pushing the gallery down, so keep it short. Links in it do not open. Give images an absolute https address"}},"required":["galleryId","hasPasscode","hasSuperPasscode","passcodeAppliesToSessions","sessionsOnlyFlag","downloadGalleryZipRestricted","shareHidesPasscode","hideTenantHeader","hideLoginAction","hideEventDate","hideEventName","numberOfDaysTilExpiry","lifecycle","galleryUrlMode","slugVanity","galleryUrl","primaryColor","secondaryColor","textColor","backgroundColor","backgroundImageUrl","socialUrls","customContent","shareStationHtml"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"passcode":{"description":"The dashboard's 'Passcode-protect gallery'. The gallery link asks for it; a guest's own link and single-photo links do not, unless passcodeAppliesToSessions. Guests' emails and texts include it, unless shareHidesPasscode. null or '' clears it","anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"superPasscode":{"description":"The dashboard's 'Admin passcode access': typed at the gallery's passcode prompt, it shows every photo, even with sessionsOnlyFlag, and lets whoever has it delete photos and download the gallery ZIP. Never sent to guests: you give it out yourself. null or '' clears it","anyOf":[{"type":"string","maxLength":100},{"type":"null"}]},"passcodeAppliesToSessions":{"description":"The dashboard's 'Also require the passcode for individual sessions'. true = guests must type the gallery passcode to open their own link and single-photo links too. null = never set (off)","type":"boolean"},"sessionsOnlyFlag":{"description":"true = each guest sees only their own photos: the dashboard's 'Restrict gallery to admins only' (its event page says 'Guests limited to own photos'). A guest's own session link, from their email, text or QR code, still shows their photos, but none of its three links to the gallery. The gallery link and the slideshow show 'This gallery is private: The event organizer configured it so that guests can only see their own photos.' instead of the photos. The gallery passcode does not open it. The admin passcode (superPasscode) does, and it also lets whoever holds it delete photos and download the ZIP; so do a ZIP-download link emailed from the dashboard, the account and its team when signed in, and the iPad running the event (a Share Station still shows every photo). The admin passcode is typed at the passcode prompt, which the gallery link shows only when a passcode is set too, so set passcode, superPasscode and sessionsOnlyFlag together, as the dashboard's 'Admin access only' presets do (they also set shareHidesPasscode, and passcodeAppliesToSessions makes guests enter the passcode for their own session link as well). hideTenantHeader and hideLoginAction also remove the sign-in button from that page. The client report and this API's list_media / list_sessions are not affected. false (also when never set) = anyone with the gallery link sees every photo, behind the passcode when one is set","type":"boolean"},"downloadGalleryZipRestricted":{"description":"true or null (never set, as for a new gallery) = the gallery page offers its ZIP of every photo only to admins (the admin passcode, or the account signed in) and to a send_gallery_download_link link. false = to every guest who can open the gallery: the dashboard's 'Guests can download the gallery ZIP'. It does not change a guest's own page, whose menu offers a ZIP of their own photos","type":"boolean"},"shareHidesPasscode":{"description":"true = guests' emails and texts leave the passcode out, so you give it out yourself: the dashboard's 'Send the passcode to guests via email and text' switched off. null = never set (the passcode is sent)","type":"boolean"},"hideTenantHeader":{"description":"true = hide the bar across the top of the gallery: the account's logo and name, the sign-in button and, on a guest's own page, its 'Back to gallery' link. The event's own header under it (event logo, name, date, social links) stays","type":"boolean"},"hideLoginAction":{"description":"true = hide only the sign-in button at the right of that top bar (hiding the whole bar hides it too)","type":"boolean"},"hideEventDate":{"description":"true = hide the event's date in the event header, under its name","type":"boolean"},"hideEventName":{"description":"true = hide the event's name in the event header, under its logo. On a guest's own page the name is a link to the gallery, which goes with it","type":"boolean"},"primaryColor":{"description":"#rrggbb. The gallery's main colour: the bar at the top, the buttons, the social icons and the links, and the button in the email guests receive. White text sits on it everywhere, so a very light colour is refused","type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"secondaryColor":{"description":"#rrggbb. The colour of the Message & Button button, and the default for content buttons. White text sits on it too, so a very light colour is refused","type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"textColor":{"description":"#rrggbb. The event name and the text of the page, drawn on backgroundColor or straight on the background image, so it has to read on whichever shows. null = black","anyOf":[{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},{"type":"null"}]},"backgroundColor":{"description":"#rrggbb. The page background, also seen while a background image loads. null = white","anyOf":[{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"},{"type":"null"}]},"socialUrls":{"description":"The gallery's own social links, shown as round icons in the primary colour under the event name; null = the account's links are shown","anyOf":[{"type":"object","properties":{"facebookUrl":{"type":"string","maxLength":2048},"twitterUrl":{"description":"X (formerly Twitter)","type":"string","maxLength":2048},"linkedinUrl":{"type":"string","maxLength":2048},"instagramUrl":{"type":"string","maxLength":2048},"tiktokUrl":{"type":"string","maxLength":2048},"youtubeUrl":{"type":"string","maxLength":2048},"websiteUrl":{"type":"string","maxLength":2048}}},{"type":"null"}]},"customContent":{"oneOf":[{"type":"object","properties":{"mode":{"type":"string","const":"off"}},"required":["mode"]},{"type":"object","properties":{"mode":{"type":"string","const":"header"},"header":{"type":"object","properties":{"headerText":{"description":"The heading: bold, centred, one short line","type":"string","maxLength":200},"text":{"description":"The message under the heading","type":"string","maxLength":1000},"buttonText":{"description":"A full-width button in secondaryColor with white text. Shown only with a buttonLink","type":"string","maxLength":120},"buttonLink":{"description":"Where the button goes","type":"string","maxLength":2048}}}},"required":["mode","header"]},{"type":"object","properties":{"mode":{"type":"string","const":"blocks"},"topBlocks":{"description":"Above the guest media, on the gallery page and on every session page","maxItems":40,"type":"array","items":{"oneOf":[{"type":"object","properties":{"id":{"description":"Leave out for a new block","type":"string","minLength":1,"maxLength":64},"type":{"type":"string","const":"section_title"},"text":{"type":"string","maxLength":200}},"required":["type","text"]},{"type":"object","properties":{"id":{"description":"Leave out for a new block","type":"string","minLength":1,"maxLength":64},"type":{"type":"string","const":"rich_text"},"content":{"type":"string","maxLength":25000,"description":"HTML, shown as written"}},"required":["type","content"]},{"type":"object","properties":{"id":{"description":"Leave out for a new block","type":"string","minLength":1,"maxLength":64},"type":{"type":"string","const":"button"},"buttonText":{"type":"string","maxLength":120},"buttonLink":{"type":"string","maxLength":2048},"buttonColor":{"description":"White text sits on it, so it must not be too light. Default: the secondary colour, else the primary, else black","type":"string","pattern":"^#[0-9a-fA-F]{6}$"}},"required":["type","buttonText","buttonLink"]}]}},"bottomBlocks":{"description":"Below the guest media, on the session page","maxItems":40,"type":"array","items":{"oneOf":[{"type":"object","properties":{"id":{"description":"Leave out for a new block","type":"string","minLength":1,"maxLength":64},"type":{"type":"string","const":"section_title"},"text":{"type":"string","maxLength":200}},"required":["type","text"]},{"type":"object","properties":{"id":{"description":"Leave out for a new block","type":"string","minLength":1,"maxLength":64},"type":{"type":"string","const":"rich_text"},"content":{"type":"string","maxLength":25000,"description":"HTML, shown as written"}},"required":["type","content"]},{"type":"object","properties":{"id":{"description":"Leave out for a new block","type":"string","minLength":1,"maxLength":64},"type":{"type":"string","const":"button"},"buttonText":{"type":"string","maxLength":120},"buttonLink":{"type":"string","maxLength":2048},"buttonColor":{"description":"White text sits on it, so it must not be too light. Default: the secondary colour, else the primary, else black","type":"string","pattern":"^#[0-9a-fA-F]{6}$"}},"required":["type","buttonText","buttonLink"]}]}},"hidesHeader":{"description":"Hide the gallery's own header: the logo, the event name and date, the social links. The gallery's background image is hidden with it","type":"boolean"}},"required":["mode"]}]},"shareStationHtml":{"description":"HTML shown at the top of the gallery on an iPad running as a Share Station. It takes the full width of the screen and grows to the height of its content, pushing the gallery down, so keep it short. Links in it do not open. Give images an absolute https address","anyOf":[{"type":"string","maxLength":100000},{"type":"null"}]}}}}}}}},"/events/{eventId}/gallery-url":{"patch":{"operationId":"set_gallery_url","summary":"Set the event gallery's URL form (vanity /e/ slug, default, or custom domain)","description":"mode 'slug-vanity' publishes the gallery at /e/<slugVanity> and requires slugVanity: 3-80 lowercase letters, numbers, hyphens or underscores, starting and ending with a letter or number, no consecutive hyphens, and not a reserved word (e, of, m, s, api, login, photos, …). 'default' forces the /of/ form even when the account has a custom domain; 'custom-domain' uses the account's domain when one is enabled. Before the gallery's first upload, renaming the slug or leaving 'slug-vanity' mode releases the old vanity slug. Once it has had an upload, guests may already hold /e/ links, so the slug is locked: renaming it fails with conflict (code slug-vanity-locked in details), and leaving the mode keeps the slug reserved so switching back to 'slug-vanity' restores it. Fails with conflict when the slug is already taken by another gallery. Note the default /of/ slug is minted from the event name: renaming the event (update_event) before the gallery's first upload re-mints it, and after the first upload it stays as it is (link stability).","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"galleryId":{"type":"string"},"hasPasscode":{"type":"boolean","description":"Whether a guest access passcode is set"},"hasSuperPasscode":{"type":"boolean","description":"Whether a download-tier passcode is set"},"passcode":{"description":"Only present with include_passcodes=true. Guest access passcode; null = none","anyOf":[{"type":"string"},{"type":"null"}]},"superPasscode":{"description":"Only present with include_passcodes=true. Download-tier passcode; null = none","anyOf":[{"type":"string"},{"type":"null"}]},"passcodeAppliesToSessions":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"The dashboard's 'Also require the passcode for individual sessions'. true = guests must type the gallery passcode to open their own link and single-photo links too. null = never set (off)"},"sessionsOnlyFlag":{"type":"boolean","description":"true = each guest sees only their own photos: the dashboard's 'Restrict gallery to admins only' (its event page says 'Guests limited to own photos'). A guest's own session link, from their email, text or QR code, still shows their photos, but none of its three links to the gallery. The gallery link and the slideshow show 'This gallery is private: The event organizer configured it so that guests can only see their own photos.' instead of the photos. The gallery passcode does not open it. The admin passcode (superPasscode) does, and it also lets whoever holds it delete photos and download the ZIP; so do a ZIP-download link emailed from the dashboard, the account and its team when signed in, and the iPad running the event (a Share Station still shows every photo). The admin passcode is typed at the passcode prompt, which the gallery link shows only when a passcode is set too, so set passcode, superPasscode and sessionsOnlyFlag together, as the dashboard's 'Admin access only' presets do (they also set shareHidesPasscode, and passcodeAppliesToSessions makes guests enter the passcode for their own session link as well). hideTenantHeader and hideLoginAction also remove the sign-in button from that page. The client report and this API's list_media / list_sessions are not affected. false (also when never set) = anyone with the gallery link sees every photo, behind the passcode when one is set"},"downloadGalleryZipRestricted":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true or null (never set, as for a new gallery) = the gallery page offers its ZIP of every photo only to admins (the admin passcode, or the account signed in) and to a send_gallery_download_link link. false = to every guest who can open the gallery: the dashboard's 'Guests can download the gallery ZIP'. It does not change a guest's own page, whose menu offers a ZIP of their own photos"},"shareHidesPasscode":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true = guests' emails and texts leave the passcode out, so you give it out yourself: the dashboard's 'Send the passcode to guests via email and text' switched off. null = never set (the passcode is sent)"},"hideTenantHeader":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true = hide the bar across the top of the gallery: the account's logo and name, the sign-in button and, on a guest's own page, its 'Back to gallery' link. The event's own header under it (event logo, name, date, social links) stays"},"hideLoginAction":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true = hide only the sign-in button at the right of that top bar (hiding the whole bar hides it too)"},"hideEventDate":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true = hide the event's date in the event header, under its name"},"hideEventName":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true = hide the event's name in the event header, under its logo. On a guest's own page the name is a link to the gallery, which goes with it"},"numberOfDaysTilExpiry":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"How long the gallery lasts, in days from its first upload (see lifecycle.expiryDate); null = not recorded"},"lifecycle":{"type":"object","properties":{"status":{"type":"string","enum":["active","expired","files-deleted"],"description":"active = guests can open the gallery and their own session links. expired = the gallery has expired: the gallery link, every guest's session link, single-photo links and the slideshow all show a 'Gallery Expired' page ('This gallery has expired and is no longer available. Please contact the event organizer for more information.'), and the iPad will not open the event. The photos and videos are still stored for a limited time (the dashboard says 'Expired, available for purchase for a limited time'), then deleted. files-deleted = expired, and the photos and videos have been deleted (the dashboard says 'Expired'); guests see the same 'Gallery Expired' page. Expiry cannot be undone through the API, so warn the client before expiryDate"},"expiryDate":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"While active, when the gallery expires (ISO 8601): the dashboard's 'Expires on {date}'. Guests can open the gallery until then; it closes at shared.gallery's nightly expiry run after this time. null while active = nothing has been uploaded yet: the date is set at the first upload, numberOfDaysTilExpiry days after it, and uploads during the next 14 days move it later by as much. null once expired"},"daysLeft":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Whole days from now until expiryDate, rounded down; 0 = less than a day left (the dashboard says 'Expires today'). null when expiryDate is null. shared.gallery emails the account once, about 4 days before a gallery with photos expires (for a paid event, only while zipDownloadedAt is null); tell the client earlier if they need time to collect the photos"},"zipDownloadedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When the account owner, signed in, last downloaded the whole gallery as a ZIP (ISO 8601); the dashboard adds 'Downloaded by you on {date}' to the expiry line. null = never"}},"required":["status","expiryDate","daysLeft","zipDownloadedAt"],"additionalProperties":false,"description":"Where the gallery is in its life, as the dashboard's event page shows it in its expiry line. Use it to tell the client what guests can see now and to warn them before the gallery expires"},"galleryUrlMode":{"anyOf":[{"type":"string","enum":["custom-domain","default","slug-vanity"]},{"type":"null"}],"description":"null = legacy default (custom domain when one is enabled)"},"slugVanity":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The /e/<slug> vanity path; set via set_gallery_url"},"galleryUrl":{"type":"string","description":"The guest-facing gallery URL under the current mode"},"primaryColor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"#rrggbb. The gallery's main colour: the bar at the top, the buttons, the social icons and the links, and the button in the email guests receive. White text sits on it everywhere, so a very light colour is refused"},"secondaryColor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"#rrggbb. The colour of the Message & Button button, and the default for content buttons. White text sits on it too, so a very light colour is refused"},"textColor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"#rrggbb. The event name and the text of the page, drawn on backgroundColor or straight on the background image, so it has to read on whichever shows. null = black"},"backgroundColor":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"#rrggbb. The page background, also seen while a background image loads. null = white"},"backgroundImageUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The page background image, landscape, filling the browser window. Set and removed with set_event_branding_image, slot 'gallery-background', which says what size to send"},"socialUrls":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"}},{"type":"null"}],"description":"The gallery's own social links, shown as round icons in the primary colour under the event name; null = the account's links are shown"},"customContent":{"type":"object","properties":{"mode":{"type":"string","enum":["off","header","blocks"]},"header":{"anyOf":[{"type":"object","properties":{"headerText":{"description":"The heading: bold, centred, one short line","type":"string","maxLength":200},"text":{"description":"The message under the heading","type":"string","maxLength":1000},"buttonText":{"description":"A full-width button in secondaryColor with white text. Shown only with a buttonLink","type":"string","maxLength":120},"buttonLink":{"description":"Where the button goes","type":"string","maxLength":2048}},"additionalProperties":false},{"type":"null"}]},"topBlocks":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"}},"required":["type"],"additionalProperties":{}}},"bottomBlocks":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"}},"required":["type"],"additionalProperties":{}}},"hidesHeader":{"type":"boolean"}},"required":["mode","header","topBlocks","bottomBlocks","hidesHeader"],"additionalProperties":false,"description":"The gallery's own content: none, a Message & Button, or Content Builder blocks"},"shareStationHtml":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"HTML shown at the top of the gallery on an iPad running as a Share Station. It takes the full width of the screen and grows to the height of its content, pushing the gallery down, so keep it short. Links in it do not open. Give images an absolute https address"}},"required":["galleryId","hasPasscode","hasSuperPasscode","passcodeAppliesToSessions","sessionsOnlyFlag","downloadGalleryZipRestricted","shareHidesPasscode","hideTenantHeader","hideLoginAction","hideEventDate","hideEventName","numberOfDaysTilExpiry","lifecycle","galleryUrlMode","slugVanity","galleryUrl","primaryColor","secondaryColor","textColor","backgroundColor","backgroundImageUrl","socialUrls","customContent","shareStationHtml"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"mode":{"type":"string","enum":["custom-domain","default","slug-vanity"]},"slugVanity":{"type":"string","minLength":3,"maxLength":80}},"required":["mode"]}}}}}},"/events/{eventId}/templates":{"post":{"operationId":"add_event_template","summary":"Add a template to an event (concurrency-safe)","description":"Transactional arrayUnion-style add — safe against concurrent dashboard/iPad edits, unlike PATCHing the whole templateIds list. Idempotent when the template is already assigned. An event whose settings.captureTypes lists all seven types is trimmed to the ones its templates can serve, as the iPad does when the event's settings are opened on it (a schedule launch never does, and refuses an event listing a type no template serves). A shorter list is kept as sent: to offer a type a newly added template serves, add it to settings.captureTypes (update_event).","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"Event id — use list_events to find one"}}],"x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"templateIds":{"type":"array","items":{"type":"string"},"description":"The full list after the change"}},"required":["id","templateIds"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"templateId":{"type":"string","minLength":1,"description":"Template id — use list_templates to find one"}},"required":["templateId"]}}}}},"delete":{"operationId":"remove_event_template","summary":"Remove a template from an event (concurrency-safe)","description":"Events always keep at least one template — removing the last one is refused (409). Idempotent when the template is not assigned. An event whose settings.captureTypes lists all seven types is trimmed to the ones its templates can serve, as the iPad does when the event's settings are opened on it (a schedule launch never does, and refuses an event listing a type no template serves). A shorter list is kept as sent: to offer a type a newly added template serves, add it to settings.captureTypes (update_event).","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"Event id — use list_events to find one"}},{"name":"templateId","in":"query","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"Template id — use list_templates to find one"},"description":"Template id — use list_templates to find one"}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"templateIds":{"type":"array","items":{"type":"string"},"description":"The full list after the change"}},"required":["id","templateIds"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/events/{eventId}/media":{"get":{"operationId":"list_media","summary":"List the event gallery's media (newest first)","description":"Read-only. originalUrl/thumbnailUrl are directly servable SIGNED URLs that expire (up to 7 days; always ≥24 h left when served) — re-list to refresh them rather than storing them. Paginate with cursor; do not fetch the whole gallery in one call.","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}},{"name":"limit","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","default":50,"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string"}}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"captureType":{"anyOf":[{"type":"string"},{"type":"null"}]},"contentType":{"anyOf":[{"type":"string"},{"type":"null"}]},"filename":{"anyOf":[{"type":"string"},{"type":"null"}]},"width":{"anyOf":[{"type":"number"},{"type":"null"}]},"height":{"anyOf":[{"type":"number"},{"type":"null"}]},"isCollage":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"templateId":{"anyOf":[{"type":"string"},{"type":"null"}]},"sessionId":{"anyOf":[{"type":"string"},{"type":"null"}]},"kBytes":{"anyOf":[{"type":"number"},{"type":"null"}]},"originalUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The original media file — a signed URL valid up to 7 days. Usually refreshed to ≥24 h when served, but a legacy item with no stored storage path can return an already-expired URL — treat a 403 on the URL as \"re-list\", not as a missing photo. Re-list to refresh; never store long-term."},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The media thumbnail file — a signed URL valid up to 7 days. Usually refreshed to ≥24 h when served, but a legacy item with no stored storage path can return an already-expired URL — treat a 403 on the URL as \"re-list\", not as a missing photo. Re-list to refresh; never store long-term."},"mediaRole":{"anyOf":[{"type":"string","enum":["capture","collage","aiTemplatedOriginal","montage"]},{"type":"null"}],"description":"What the file is within its capture session: capture (a camera capture — photo, video, boomerang, slow-mo), collage (the templated photo the guest gets), aiTemplatedOriginal (AI flows: the templated photo before AI), montage (an animation built from photos, e.g. the GIF capture type). null when unknown (older uploads)."},"templateApplied":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Whether the template was rendered into this file"},"aiPromptId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The AI prompt that generated this media (get_ai_prompt), if any"},"aiPortraitFilterId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The AI Portrait style that generated this media (list_ai_portraits), if any"},"aiPortraitPersonType":{"anyOf":[{"type":"string","enum":["boy","girl","man","woman","auto"]},{"type":"null"}],"description":"The person type chosen for an AI Portrait"},"aiRerolls":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"How many times the guest re-rolled the AI result"},"captureIndex":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"0-based position among the batch's captures"},"takeMoreBatch":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"0 = the visit's first batch; each 'take more' adds one"},"sessionStartedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 — when the guest session that produced this media started"},"filter":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The colour filter's id, as the iPad names it"},"sceneApplied":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Whether a green-screen scene was applied"},"glamIntensity":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"0…1: the template's glam intensity configured for the batch (0 = glam off, or a video). The setting, not what was applied — the iPad skips glam on a photo with no detected face."},"captureTrigger":{"anyOf":[{"type":"string","enum":["tap","tethered","motion","orcavue"]},{"type":"null"}],"description":"What started the capture: tap (a touch on the iPad), tethered (the camera's own shutter), motion (a spinner booth's auto-start), orcavue (the OrcaVue 360 controller)"}},"required":["id","createdDate","captureType","contentType","filename","width","height","isCollage","templateId","sessionId","kBytes","originalUrl","thumbnailUrl","mediaRole","templateApplied","aiPromptId","aiPortraitFilterId","aiPortraitPersonType","aiRerolls","captureIndex","takeMoreBatch","sessionStartedAt","filter","sceneApplied","glamIntensity","captureTrigger"],"additionalProperties":false}},"hasMore":{"type":"boolean"},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["data","hasMore","nextCursor"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/events/{eventId}/sessions":{"get":{"operationId":"list_sessions","summary":"List capture sessions incl. guest data-collection responses","description":"Read-only. Each session carries the guest email(s)/phone number(s) it was shared to (`email`/`phoneNumber` = first, `emails`/`phoneNumbers` = all, since a guest can share the same photos to several people), the raw dataCollectionResponse, and `sessionUrl`, the link to that guest's own photos; use export_survey_responses for flattened CRM-friendly rows. Deleted sessions are excluded (a page may return fewer than limit).","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}},{"name":"limit","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","default":50,"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string"}}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"index":{"anyOf":[{"type":"number"},{"type":"null"}]},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"First email address this session was shared to; null when none. See `emails`."},"emails":{"type":"array","items":{"type":"string"},"description":"Every email address this session was shared to, in share order (a guest can share the same photos to several people). Empty when none."},"phoneNumber":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"First phone number this session was shared to; null when none. See `phoneNumbers`."},"phoneNumbers":{"type":"array","items":{"type":"string"},"description":"Every phone number this session was shared to, in share order. Empty when none."},"sessionUrl":{"type":"string","description":"This guest's own page in the event gallery, showing only their photos: the dashboard's \"View photos\" link and the guest CSV's Link column. Give it to the guest, or have send_session_share email or text it. It is not a signed URL and does not expire, but it follows the gallery's URL form (set_gallery_url). When the gallery has a passcode and passcodeAppliesToSessions is on (get_gallery_settings), the guest still needs the passcode to open it."},"mediaIds":{"type":"array","items":{"type":"string"}},"media":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"captureType":{"anyOf":[{"type":"string"},{"type":"null"}]},"contentType":{"anyOf":[{"type":"string"},{"type":"null"}]},"filename":{"anyOf":[{"type":"string"},{"type":"null"}]},"width":{"anyOf":[{"type":"number"},{"type":"null"}]},"height":{"anyOf":[{"type":"number"},{"type":"null"}]},"isCollage":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"templateId":{"anyOf":[{"type":"string"},{"type":"null"}]},"sessionId":{"anyOf":[{"type":"string"},{"type":"null"}]},"kBytes":{"anyOf":[{"type":"number"},{"type":"null"}]},"originalUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The original media file — a signed URL valid up to 7 days. Usually refreshed to ≥24 h when served, but a legacy item with no stored storage path can return an already-expired URL — treat a 403 on the URL as \"re-list\", not as a missing photo. Re-list to refresh; never store long-term."},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The media thumbnail file — a signed URL valid up to 7 days. Usually refreshed to ≥24 h when served, but a legacy item with no stored storage path can return an already-expired URL — treat a 403 on the URL as \"re-list\", not as a missing photo. Re-list to refresh; never store long-term."},"mediaRole":{"anyOf":[{"type":"string","enum":["capture","collage","aiTemplatedOriginal","montage"]},{"type":"null"}],"description":"What the file is within its capture session: capture (a camera capture — photo, video, boomerang, slow-mo), collage (the templated photo the guest gets), aiTemplatedOriginal (AI flows: the templated photo before AI), montage (an animation built from photos, e.g. the GIF capture type). null when unknown (older uploads)."},"templateApplied":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Whether the template was rendered into this file"},"aiPromptId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The AI prompt that generated this media (get_ai_prompt), if any"},"aiPortraitFilterId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The AI Portrait style that generated this media (list_ai_portraits), if any"},"aiPortraitPersonType":{"anyOf":[{"type":"string","enum":["boy","girl","man","woman","auto"]},{"type":"null"}],"description":"The person type chosen for an AI Portrait"},"aiRerolls":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"How many times the guest re-rolled the AI result"},"captureIndex":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"0-based position among the batch's captures"},"takeMoreBatch":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"0 = the visit's first batch; each 'take more' adds one"},"sessionStartedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 — when the guest session that produced this media started"},"filter":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The colour filter's id, as the iPad names it"},"sceneApplied":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Whether a green-screen scene was applied"},"glamIntensity":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"0…1: the template's glam intensity configured for the batch (0 = glam off, or a video). The setting, not what was applied — the iPad skips glam on a photo with no detected face."},"captureTrigger":{"anyOf":[{"type":"string","enum":["tap","tethered","motion","orcavue"]},{"type":"null"}],"description":"What started the capture: tap (a touch on the iPad), tethered (the camera's own shutter), motion (a spinner booth's auto-start), orcavue (the OrcaVue 360 controller)"}},"required":["id","createdDate","captureType","contentType","filename","width","height","isCollage","templateId","sessionId","kBytes","originalUrl","thumbnailUrl","mediaRole","templateApplied","aiPromptId","aiPortraitFilterId","aiPortraitPersonType","aiRerolls","captureIndex","takeMoreBatch","sessionStartedAt","filter","sceneApplied","glamIntensity","captureTrigger"],"additionalProperties":false},"description":"Denormalized media objects when present"},"dataCollectionResponse":{"anyOf":[{},{"type":"null"}]}},"required":["id","createdDate","index","email","emails","phoneNumber","phoneNumbers","sessionUrl","mediaIds","media","dataCollectionResponse"],"additionalProperties":false}},"hasMore":{"type":"boolean"},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]},"hiddenLegacySessions":{"description":"Present on the first page only, and only when >0: sessions written without a createdDate, which paging by that field cannot return. They are missing from this list.","type":"number"}},"required":["data","hasMore","nextCursor"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/events/{eventId}/survey-responses":{"get":{"operationId":"export_survey_responses","summary":"Flattened survey/data-collection answers, one row per response","description":"CRM-friendly export: each guest answer becomes a row with the session id, guest email/phone (`email`/`phoneNumber` = first address the session was shared to, `emails`/`phoneNumbers` = all of them), the link to the guest's own photos (`sessionUrl`), question text, and answer. Paginates over sessions — keep calling with nextCursor until hasMore is false.","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}},{"name":"limit","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","default":50,"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string"}}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"sessionId":{"type":"string"},"sessionCreatedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"First email address this session was shared to; null when none. See `emails`."},"emails":{"type":"array","items":{"type":"string"},"description":"Every email address this session was shared to, in share order (a guest can share the same photos to several people). Empty when none."},"phoneNumber":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"First phone number this session was shared to; null when none. See `phoneNumbers`."},"phoneNumbers":{"type":"array","items":{"type":"string"},"description":"Every phone number this session was shared to, in share order. Empty when none."},"sessionUrl":{"type":"string","description":"This guest's own page in the event gallery, showing only their photos: the dashboard's \"View photos\" link and the guest CSV's Link column. Give it to the guest, or have send_session_share email or text it. It is not a signed URL and does not expire, but it follows the gallery's URL form (set_gallery_url). When the gallery has a passcode and passcodeAppliesToSessions is on (get_gallery_settings), the guest still needs the passcode to open it."},"stage":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"'preSession' | 'dynamicElements' | 'postCapture' when known"},"fieldId":{"anyOf":[{"type":"string"},{"type":"null"}]},"question":{"anyOf":[{"type":"string"},{"type":"null"}]},"answer":{"anyOf":[{},{"type":"null"}],"description":"The answer as a person would read it. For choice questions (multi_select / image_multi_select) this is the text of the chosen option; `answerId` carries the stable id it resolves from."},"answerId":{"anyOf":[{},{"type":"null"}],"description":"Stable machine value: the choice id (ch_<ULID>) for choice questions, otherwise identical to `answer`. Use this to compare answers across events; use `answer` to display them."},"capturedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["sessionId","sessionCreatedDate","email","emails","phoneNumber","phoneNumbers","sessionUrl","stage","fieldId","question","answer","answerId","capturedAt"],"additionalProperties":false}},"hasMore":{"type":"boolean"},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]},"hiddenLegacySessions":{"description":"Present on the first page only, and only when >0: sessions written without a createdDate cannot be returned by a query ordered on it. Their responses are missing from this export.","type":"number"}},"required":["data","hasMore","nextCursor"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/events/{eventId}/media/{mediaId}":{"delete":{"operationId":"delete_media","summary":"Delete one photo or video from an event's gallery (irreversible)","description":"Deletes it as deleting it in the dashboard does: it leaves the gallery and the guest's own page, its files are deleted, and a copy already sent to the account's cloud storage (Dropbox, Google Drive, SmugMug) is deleted there too. A guest session left with nothing in it is removed. mediaId comes from list_media. There is no undo: confirm with your client first. Copies guests already downloaded, received or printed are not affected; a ZIP of the gallery made before still holds it until the ZIP is made again.","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}},{"name":"mediaId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"deleted":{"type":"boolean","const":true},"id":{"type":"string"}},"required":["deleted","id"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/events/{eventId}/sessions/{sessionId}":{"delete":{"operationId":"delete_session","summary":"Delete one guest's photo session and everything in it (irreversible)","description":"Deletes the session as deleting it in the dashboard does: every photo and video in it is deleted (as delete_media does each one, cloud-storage copies included), and its link (sessionUrl) no longer shows them. sessionId comes from list_sessions. There is no undo: confirm with your client first. Copies guests already downloaded, received or printed are not affected; a ZIP of the gallery made before still holds it until the ZIP is made again.","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}},{"name":"sessionId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"deleted":{"type":"boolean","const":true},"id":{"type":"string"},"mediaDeleted":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"How many photos and videos went with it"}},"required":["deleted","id","mediaDeleted"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/events/{eventId}/sessions/{sessionId}/send":{"post":{"operationId":"send_session_share","summary":"Email or text one guest the link to their photos","description":"The dashboard's per-guest \"Share a link\": sends this session's own page (sessionUrl in list_sessions) to one email address or one phone number, as the event's share email or text (get_event_communication_settings), with the gallery passcode in it unless the gallery hides it on shares (shareHidesPasscode). The address is also added to the session's emails or phoneNumbers, and the account's share webhook fires, as when a guest shares from the booth. Give exactly one of email or phoneNumber; a phone number in international format (+ and the country code).\n\nTexts cost money. A text to a destination priced above what the event's plan allows is not sent: the call still succeeds, the number is still added to the session, and smsFailureReason says why. Events on the free plan (a gallery kept 10 days or less) share at most 250 times a day per account, emails and texts together; past that, rate_limited until midnight UTC. Through this API an account sends at most 1000 emails a day (UTC) to guests, send_session_share by email and send_gallery_download_link together, and 250 texts (send_session_share by text); past that, rate_limited with Retry-After until midnight UTC. Booth.Events support can lift this limit for an account that needs more. Each call counts as 5 requests against this key's rate limit, and a text as 10.\n\nFails with not_found for a session of another event or a deleted one, and with conflict when the gallery has expired. Each call sends again: a timeout does not mean nothing was sent, so check the session (list_sessions) before repeating it.","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}},{"name":"sessionId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"eventId":{"type":"string"},"sessionId":{"type":"string"},"channel":{"type":"string","enum":["email","sms"],"description":"How it was sent"},"to":{"type":"string","description":"The address it was sent to; a phone number as the session stores it (E.164)"},"sessionUrl":{"type":"string","description":"The guest's own page the message links to (a text carries a shortened link to it)"},"smsFailureReason":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Texts only; null for an email. null when the text went out as far as the gallery service can tell, otherwise why it did not: priceTooHigh (the destination costs more than any plan allows), priceTooHighForQuota (more than this event's plan allows per text), notAMobileNumber or unknown. The number is added to the session either way."}},"required":["eventId","sessionId","channel","to","sessionUrl","smsFailureReason"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"email":{"description":"Email the link to this address","type":"string","maxLength":320,"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"phoneNumber":{"description":"Text the link to this number, in international format, e.g. +14155550123","type":"string","maxLength":40}}}}}}}},"/events/{eventId}/gallery/download-link/send":{"post":{"operationId":"send_gallery_download_link","summary":"Email someone a link to download the whole event gallery","description":"The dashboard's \"Email Gallery download link\": emails one address a link that opens the event gallery's ZIP downloads — every photo and video in it — WITHOUT the gallery passcode or any other privacy setting. Anyone who has the email, or is forwarded it, can download the whole gallery, and the link cannot be withdrawn once sent (every send reuses the same one). Confirm the address with the operator before calling this.\n\nThe email is the event's ZIP-link email (emailZip in get_event_communication_settings). Fails with conflict when the gallery has expired or its files have been deleted. Each call sends again; it counts as 5 requests against this key's rate limit. Through this API an account sends at most 1000 emails a day (UTC) to guests, send_session_share by email and send_gallery_download_link together, and 250 texts (send_session_share by text); past that, rate_limited with Retry-After until midnight UTC. Booth.Events support can lift this limit for an account that needs more.","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"eventId":{"type":"string"},"email":{"type":"string","description":"The address the link was sent to"},"sent":{"type":"boolean","const":true}},"required":["eventId","email","sent"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"email":{"type":"string","maxLength":320,"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$","description":"The one address to send the link to"}},"required":["email"]}}}}}},"/templates":{"get":{"operationId":"list_templates","summary":"List your templates","parameters":[{"name":"limit","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","default":25,"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string"}},{"name":"search","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","description":"Case-insensitive name prefix","type":"string","minLength":1},"description":"Case-insensitive name prefix"},{"name":"includePublic","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","description":"true = also include public library templates (e.g. account defaults reference these)","type":"boolean"},"description":"true = also include public library templates (e.g. account defaults reference these)"}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"},"photoAreaCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Media-layer count; AI prompts/portraits attach only when this is exactly 1"},"aiCustomPromptIds":{"type":"array","items":{"type":"string"},"description":"Attached AI prompts (see list_ai_prompts)"},"aiPortraitIds":{"type":"array","items":{"type":"string"},"description":"Attached AI portrait styles (see list_ai_portraits)"},"disabledCaptureTypes":{"type":"array","items":{"type":"string"}},"sceneSelectionForced":{"type":"boolean"},"transparentScene":{"type":"boolean","description":"Offers a \"no scene\" option to guests"},"scenes":{"type":"array","items":{"type":"object","properties":{"storagePath":{"type":"string","description":"Key for update_template_scene / remove_template_scene"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"7-day signed URL — re-fetch to refresh"},"width":{"anyOf":[{"type":"number"},{"type":"null"}]},"height":{"anyOf":[{"type":"number"},{"type":"null"}]},"crop":{"anyOf":[{"type":"string","enum":["scene","photo"]},{"type":"null"}]},"compositingMethod":{"anyOf":[{"type":"string","enum":["sourceOver","addition","colorBlend","colorDodgeBlend","differenceBlend","exclusionBlend","lightenBlend","linearDodgeBlend","luminosityBlend","minimumCompositing","overlayBlend","screenBlend","softLightBlend"]},{"type":"null"}]},"uploadedOn":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["storagePath","thumbnailUrl","width","height","crop","compositingMethod","uploadedOn"],"additionalProperties":false},"description":"Green-screen background scenes"},"printable":{"type":"boolean","description":"false = photos with this template are not printed"},"printsShortestSideDoubled":{"type":"boolean","description":"Each print carries the design twice along its short side (2x6 → 2x2x6)"},"imageFilters":{"type":"array","items":{"type":"string"},"description":"The colour filters in the order guests see them; exactly one = forced on every capture; [] = none. May hold an id a newer iPad app added"},"bwCustom":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"highlights":{"type":"number"},"shadows":{"type":"number"},"colourResponse":{"type":"number"}},"required":["exposure","contrast","highlights","shadows","colourResponse"],"additionalProperties":false,"description":"Mono Custom (blackAndWhiteCustom) sliders, as the iPad uses them: the defaults until changed"},"filmStrip":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"grain":{"type":"number"},"warmth":{"type":"number"},"vignette":{"type":"number"},"glow":{"type":"number"},"softness":{"type":"number"}},"required":["exposure","contrast","grain","warmth","vignette","glow","softness"],"additionalProperties":false,"description":"Film Custom (blackAndWhiteFilmStrip) sliders, as the iPad uses them: the defaults until changed"},"glamFilter":{"type":"boolean","description":"The Glam Filter is on"},"glamFilterIntensity":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Its strength, 0 to 1. null = never set: the iPad applies no glam until it is, even with glamFilter on"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting","photoAreaCount","aiCustomPromptIds","aiPortraitIds","disabledCaptureTypes","sceneSelectionForced","transparentScene","scenes","printable","printsShortestSideDoubled","imageFilters","bwCustom","filmStrip","glamFilter","glamFilterIntensity"],"additionalProperties":false}},"hasMore":{"type":"boolean"},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["data","hasMore","nextCursor"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/templates/{id}":{"get":{"operationId":"get_template","summary":"Get one template (yours or a public one)","description":"With the design: every layer as a rectangle in template pixels (sizePixels), the fields it owns, and the fields that can be placed on it. Through MCP the rendered thumbnail comes with the result as an image.","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"},"photoAreaCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Media-layer count; AI prompts/portraits attach only when this is exactly 1"},"aiCustomPromptIds":{"type":"array","items":{"type":"string"},"description":"Attached AI prompts (see list_ai_prompts)"},"aiPortraitIds":{"type":"array","items":{"type":"string"},"description":"Attached AI portrait styles (see list_ai_portraits)"},"disabledCaptureTypes":{"type":"array","items":{"type":"string"}},"sceneSelectionForced":{"type":"boolean"},"transparentScene":{"type":"boolean","description":"Offers a \"no scene\" option to guests"},"scenes":{"type":"array","items":{"type":"object","properties":{"storagePath":{"type":"string","description":"Key for update_template_scene / remove_template_scene"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"7-day signed URL — re-fetch to refresh"},"width":{"anyOf":[{"type":"number"},{"type":"null"}]},"height":{"anyOf":[{"type":"number"},{"type":"null"}]},"crop":{"anyOf":[{"type":"string","enum":["scene","photo"]},{"type":"null"}]},"compositingMethod":{"anyOf":[{"type":"string","enum":["sourceOver","addition","colorBlend","colorDodgeBlend","differenceBlend","exclusionBlend","lightenBlend","linearDodgeBlend","luminosityBlend","minimumCompositing","overlayBlend","screenBlend","softLightBlend"]},{"type":"null"}]},"uploadedOn":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["storagePath","thumbnailUrl","width","height","crop","compositingMethod","uploadedOn"],"additionalProperties":false},"description":"Green-screen background scenes"},"printable":{"type":"boolean","description":"false = photos with this template are not printed"},"printsShortestSideDoubled":{"type":"boolean","description":"Each print carries the design twice along its short side (2x6 → 2x2x6)"},"imageFilters":{"type":"array","items":{"type":"string"},"description":"The colour filters in the order guests see them; exactly one = forced on every capture; [] = none. May hold an id a newer iPad app added"},"bwCustom":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"highlights":{"type":"number"},"shadows":{"type":"number"},"colourResponse":{"type":"number"}},"required":["exposure","contrast","highlights","shadows","colourResponse"],"additionalProperties":false,"description":"Mono Custom (blackAndWhiteCustom) sliders, as the iPad uses them: the defaults until changed"},"filmStrip":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"grain":{"type":"number"},"warmth":{"type":"number"},"vignette":{"type":"number"},"glow":{"type":"number"},"softness":{"type":"number"}},"required":["exposure","contrast","grain","warmth","vignette","glow","softness"],"additionalProperties":false,"description":"Film Custom (blackAndWhiteFilmStrip) sliders, as the iPad uses them: the defaults until changed"},"glamFilter":{"type":"boolean","description":"The Glam Filter is on"},"glamFilterIntensity":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Its strength, 0 to 1. null = never set: the iPad applies no glam until it is, even with glamFilter on"},"layers":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position in the stack, 0 = bottom"},"kind":{"type":"string","enum":["background","artwork","photoArea","text","field","other"],"description":"background = the base colour; artwork = a picture of the design; photoArea = where a photo goes; text; field = a dynamic element (a guest answer, printed); other = live view or video"},"x":{"type":"number","description":"Left edge, in template pixels (see sizePixels)"},"y":{"type":"number","description":"Top edge, in template pixels"},"width":{"type":"number"},"height":{"type":"number"},"rotation":{"type":"number","description":"Degrees clockwise around the top-left corner"},"opacity":{"type":"number","description":"0 to 1"},"color":{"description":"background: the colour; text and field: the text colour","type":"string"},"name":{"description":"artwork: the picture file","type":"string"},"order":{"description":"photoArea: the order photos are taken in","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"textId":{"description":"text: the key for update_template_text / remove_template_layer","type":"string"},"text":{"type":"string"},"fontFamily":{"type":"string"},"fontWeight":{"type":"string"},"fontStyle":{"type":"string"},"fontSize":{"description":"text: in template pixels; field: the largest size the answer is set in","type":"number"},"textAlign":{"type":"string"},"fieldId":{"description":"field: the dynamic field shown (see fields / placeableFields)","type":"string"},"display":{"description":"field: the answer as words, or as the chosen picture","type":"string","enum":["text","image"]},"preview":{"description":"field, text display of a free-text field: the sample shown until a guest answers","anyOf":[{"type":"string"},{"type":"null"}]},"previewChoiceId":{"description":"field, choice field: the choice the design shows","anyOf":[{"type":"string"},{"type":"null"}]},"allCaps":{"description":"field: the answer is printed in capitals","type":"boolean"},"type":{"description":"other: the stored layer type","type":"string"}},"required":["index","kind","x","y","width","height","rotation","opacity"],"additionalProperties":false},"description":"The design, bottom to top, each layer as a rectangle in template pixels. Photo areas say where guests land; text and field layers are the ones add_template_text / add_template_field make and update_template_text / remove_template_layer change; artwork is a picture of the design (add_template_image / update_template_image). move_template_layer changes what is drawn over what"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}}},"required":["id","type","name","text"],"additionalProperties":false},"description":"The template's own dynamic fields (made by add_template_field). Each is asked of the guest when this template is used, and offered to update_event_survey on events using it"},"placeableFields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}},"owner":{"type":"object","properties":{"kind":{"type":"string","enum":["template","prompt"]},"id":{"type":"string"}},"required":["kind","id"],"additionalProperties":false,"description":"Who the field belongs to: this template, or an AI prompt assigned to it"}},"required":["id","type","name","text","owner"],"additionalProperties":false},"description":"Every field add_template_field can place by fieldId: the own fields, and the fields of the AI prompts assigned to this template (update_template aiCustomPromptIds)"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting","photoAreaCount","aiCustomPromptIds","aiPortraitIds","disabledCaptureTypes","sceneSelectionForced","transparentScene","scenes","printable","printsShortestSideDoubled","imageFilters","bwCustom","filmStrip","glamFilter","glamFilterIntensity","layers","fields","placeableFields"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}},"patch":{"operationId":"update_template","summary":"Update a template’s settings: name, AI prompt/portrait assignment, scene flags, printing, colour and glam filters","description":"Settings only; the design changes through the layer tools (add_template_text, add_template_field, add_template_image, add_template_photo_area, their update_ tools, move_template_layer and remove_template_layer). Printing (printable, printsShortestSideDoubled) is the dashboard editor’s Advanced → Printing; imageFilters, bwCustom and filmStrip its Color Filters; glamFilter and glamFilterIntensity its Glam Filter. Assigning aiCustomPromptIds/aiPortraitIds requires the template to have exactly ONE photo area (photoAreaCount) and defines what END customers/guests can pick at the booth; empty arrays detach everything. Once a prompt is assigned, its fields appear in placeableFields. Detaching a prompt whose dynamic fields are placed on the canvas is refused (409) — remove those layers first (remove_template_layer). There is no switch on an event to stop printing: to make an event digital-only, give it its own copy of each template (duplicate_template) with printable false — a new copy has no printer settings on any iPad yet, so it does not print anywhere. An event uses the template itself, not a copy: a change to a template shows on every event that uses it from then on. To personalise a design for one client (their names, date or logo), copy it first (duplicate_template) and change the copy.","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"},"photoAreaCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Media-layer count; AI prompts/portraits attach only when this is exactly 1"},"aiCustomPromptIds":{"type":"array","items":{"type":"string"},"description":"Attached AI prompts (see list_ai_prompts)"},"aiPortraitIds":{"type":"array","items":{"type":"string"},"description":"Attached AI portrait styles (see list_ai_portraits)"},"disabledCaptureTypes":{"type":"array","items":{"type":"string"}},"sceneSelectionForced":{"type":"boolean"},"transparentScene":{"type":"boolean","description":"Offers a \"no scene\" option to guests"},"scenes":{"type":"array","items":{"type":"object","properties":{"storagePath":{"type":"string","description":"Key for update_template_scene / remove_template_scene"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"7-day signed URL — re-fetch to refresh"},"width":{"anyOf":[{"type":"number"},{"type":"null"}]},"height":{"anyOf":[{"type":"number"},{"type":"null"}]},"crop":{"anyOf":[{"type":"string","enum":["scene","photo"]},{"type":"null"}]},"compositingMethod":{"anyOf":[{"type":"string","enum":["sourceOver","addition","colorBlend","colorDodgeBlend","differenceBlend","exclusionBlend","lightenBlend","linearDodgeBlend","luminosityBlend","minimumCompositing","overlayBlend","screenBlend","softLightBlend"]},{"type":"null"}]},"uploadedOn":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["storagePath","thumbnailUrl","width","height","crop","compositingMethod","uploadedOn"],"additionalProperties":false},"description":"Green-screen background scenes"},"printable":{"type":"boolean","description":"false = photos with this template are not printed"},"printsShortestSideDoubled":{"type":"boolean","description":"Each print carries the design twice along its short side (2x6 → 2x2x6)"},"imageFilters":{"type":"array","items":{"type":"string"},"description":"The colour filters in the order guests see them; exactly one = forced on every capture; [] = none. May hold an id a newer iPad app added"},"bwCustom":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"highlights":{"type":"number"},"shadows":{"type":"number"},"colourResponse":{"type":"number"}},"required":["exposure","contrast","highlights","shadows","colourResponse"],"additionalProperties":false,"description":"Mono Custom (blackAndWhiteCustom) sliders, as the iPad uses them: the defaults until changed"},"filmStrip":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"grain":{"type":"number"},"warmth":{"type":"number"},"vignette":{"type":"number"},"glow":{"type":"number"},"softness":{"type":"number"}},"required":["exposure","contrast","grain","warmth","vignette","glow","softness"],"additionalProperties":false,"description":"Film Custom (blackAndWhiteFilmStrip) sliders, as the iPad uses them: the defaults until changed"},"glamFilter":{"type":"boolean","description":"The Glam Filter is on"},"glamFilterIntensity":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Its strength, 0 to 1. null = never set: the iPad applies no glam until it is, even with glamFilter on"},"layers":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position in the stack, 0 = bottom"},"kind":{"type":"string","enum":["background","artwork","photoArea","text","field","other"],"description":"background = the base colour; artwork = a picture of the design; photoArea = where a photo goes; text; field = a dynamic element (a guest answer, printed); other = live view or video"},"x":{"type":"number","description":"Left edge, in template pixels (see sizePixels)"},"y":{"type":"number","description":"Top edge, in template pixels"},"width":{"type":"number"},"height":{"type":"number"},"rotation":{"type":"number","description":"Degrees clockwise around the top-left corner"},"opacity":{"type":"number","description":"0 to 1"},"color":{"description":"background: the colour; text and field: the text colour","type":"string"},"name":{"description":"artwork: the picture file","type":"string"},"order":{"description":"photoArea: the order photos are taken in","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"textId":{"description":"text: the key for update_template_text / remove_template_layer","type":"string"},"text":{"type":"string"},"fontFamily":{"type":"string"},"fontWeight":{"type":"string"},"fontStyle":{"type":"string"},"fontSize":{"description":"text: in template pixels; field: the largest size the answer is set in","type":"number"},"textAlign":{"type":"string"},"fieldId":{"description":"field: the dynamic field shown (see fields / placeableFields)","type":"string"},"display":{"description":"field: the answer as words, or as the chosen picture","type":"string","enum":["text","image"]},"preview":{"description":"field, text display of a free-text field: the sample shown until a guest answers","anyOf":[{"type":"string"},{"type":"null"}]},"previewChoiceId":{"description":"field, choice field: the choice the design shows","anyOf":[{"type":"string"},{"type":"null"}]},"allCaps":{"description":"field: the answer is printed in capitals","type":"boolean"},"type":{"description":"other: the stored layer type","type":"string"}},"required":["index","kind","x","y","width","height","rotation","opacity"],"additionalProperties":false},"description":"The design, bottom to top, each layer as a rectangle in template pixels. Photo areas say where guests land; text and field layers are the ones add_template_text / add_template_field make and update_template_text / remove_template_layer change; artwork is a picture of the design (add_template_image / update_template_image). move_template_layer changes what is drawn over what"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}}},"required":["id","type","name","text"],"additionalProperties":false},"description":"The template's own dynamic fields (made by add_template_field). Each is asked of the guest when this template is used, and offered to update_event_survey on events using it"},"placeableFields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}},"owner":{"type":"object","properties":{"kind":{"type":"string","enum":["template","prompt"]},"id":{"type":"string"}},"required":["kind","id"],"additionalProperties":false,"description":"Who the field belongs to: this template, or an AI prompt assigned to it"}},"required":["id","type","name","text","owner"],"additionalProperties":false},"description":"Every field add_template_field can place by fieldId: the own fields, and the fields of the AI prompts assigned to this template (update_template aiCustomPromptIds)"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting","photoAreaCount","aiCustomPromptIds","aiPortraitIds","disabledCaptureTypes","sceneSelectionForced","transparentScene","scenes","printable","printsShortestSideDoubled","imageFilters","bwCustom","filmStrip","glamFilter","glamFilterIntensity","layers","fields","placeableFields"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"aiCustomPromptIds":{"description":"AI prompt ids — use list_ai_prompts to find them; [] detaches all","maxItems":20,"type":"array","items":{"type":"string","minLength":1}},"aiPortraitIds":{"description":"AI portrait style ids — use list_ai_portraits to find them; [] detaches all","maxItems":20,"type":"array","items":{"type":"string","minLength":1}},"sceneSelectionForced":{"type":"boolean"},"transparentScene":{"type":"boolean"},"disabledCaptureTypes":{"type":"array","items":{"type":"string","enum":["photo","boomerang","slowmo","video","gif","aiPhoto","aiCustomPrompt"]}},"printable":{"description":"Whether guests’ photos with this template can be printed at the booth (the dashboard’s Advanced → Printing → \"Printable\"). It is the iPad’s starting point: print options an operator sets for a printer on the iPad itself take precedence. Turned on after being off, it starts undoubled (printsShortestSideDoubled false) unless that is sent too, as the dashboard’s switch does","type":"boolean"},"printsShortestSideDoubled":{"description":"\"Print Doubled\" (2x6 → 2x2x6): each print carries the design twice, side by side along its short side, so a 2x6 strip prints as two strips on one 4x6 sheet, which a printer with a cutter cuts in two. Only matters while the template is printable","type":"boolean"},"imageFilters":{"description":"The colour filters of this template, in the order guests see them (the dashboard editor's Color Filters). Two or more: after each capture the guest picks one on the iPad, the first selected to start with. Exactly one: that filter is FORCED: it is applied to every capture without asking, and the live camera view shows it too. [] = no filters (photos as taken). Filters apply to photos, GIFs, boomerangs, videos and slow-motion videos; never to AI captures. 'none' is the Normal choice (the photo as taken) and comes first when offered; ['none'] alone is stored as []. Ids: none = Normal (the photo as taken); blackAndWhiteManual1 = Simple; blackAndWhiteManual2 = Simple Bright; blackAndWhiteManual3 = Simple Contrast; blackAndWhiteCustom = Mono Custom (tuned with bwCustom); blackAndWhiteFilmStrip = Film Custom (tuned with filmStrip); blackAndWhitePunchy = Punchy; blackAndWhiteFlat = Nitrate; filter1 = Cappuccino; filter2 = Auburn; filter3 = Ocean; filter3b = Sapphire; filter4 = Pinhole; filter5 = Jade; filter6 = Vintage; filter7 = Desert; filter9 = Silver; blackAndWhiteSepia = Sepia","maxItems":18,"type":"array","items":{"type":"string","enum":["none","blackAndWhiteManual1","blackAndWhiteManual2","blackAndWhiteManual3","blackAndWhiteCustom","blackAndWhiteFilmStrip","blackAndWhitePunchy","blackAndWhiteFlat","filter1","filter2","filter3","filter3b","filter4","filter5","filter6","filter7","filter9","blackAndWhiteSepia"]}},"bwCustom":{"description":"Mono Custom (filter blackAndWhiteCustom): the sliders of this black-and-white look, as in the dashboard's Customize panel. Only the sliders sent change; the others keep this template's values (the defaults until changed). Kept whether or not the filter is offered","type":"object","properties":{"exposure":{"description":"Brightness, in stops. -1 to 1, in steps of 0.01; default 0.02","type":"number","minimum":-1,"maximum":1},"contrast":{"description":"Contrast about mid-grey. -50 to 100; default 13","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"highlights":{"description":"How softly the brightest tones roll off into white; 0 clips them hard. 0 to 100; default 32","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"shadows":{"description":"Positive opens up the blacks, negative crushes them. -100 to 100; default 40","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"colourResponse":{"description":"How colours turn to grey, as a lens filter does: -100 blue (darker skin, more texture), 0 neutral, 100 red (lighter skin, darker sky and lips). -100 to 100; default 0","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"additionalProperties":false},"filmStrip":{"description":"Film Custom (filter blackAndWhiteFilmStrip): the sliders of this 1960s chemical photobooth look, as in the dashboard's Customize panel. Only the sliders sent change; the others keep this template's values (the defaults until changed). Kept whether or not the filter is offered","type":"object","properties":{"exposure":{"description":"Brightness, in stops. -1 to 1, in steps of 0.01; default -0.35","type":"number","minimum":-1,"maximum":1},"contrast":{"description":"Contrast about mid-grey. -50 to 100; default 75","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"grain":{"description":"Film grain; 0 is a clean print. 0 to 100; default 25","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"warmth":{"description":"Tone: -100 cool (selenium), 0 neutral black and white, 100 full sepia. -100 to 100; default 0","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"vignette":{"description":"Darkening towards the corners. 0 to 100; default 70","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"glow":{"description":"The soft bloom around highlights (halation). 0 to 100; default 23","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"softness":{"description":"How much an old booth lens blurs the picture; 0 is digitally sharp. 0 to 100; default 60","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"additionalProperties":false},"glamFilter":{"description":"The Glam Filter (the dashboard editor's Glam Filter): on every face in a photo, smooths the skin, softens blemishes and whitens teeth. Photos only, the frames of a GIF included; not videos, boomerangs, slow-motion videos or AI captures. Turned on while no strength is set, it starts at glamFilterIntensity 0.1, the dashboard's starting point","type":"boolean"},"glamFilterIntensity":{"description":"How strong the Glam Filter is: 0.01 to 0.99, in steps of 0.01 (the dashboard's slider, 1 to 99). Kept while the filter is off","type":"number","minimum":0.01,"maximum":0.99}}}}}}},"delete":{"operationId":"delete_template","summary":"Delete a template (fails if events still use it)","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"deleted":{"type":"boolean","const":true},"id":{"type":"string"}},"required":["deleted","id"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/templates/{id}/duplicate":{"post":{"operationId":"duplicate_template","summary":"Duplicate a template (e.g. to customize a public library design)","description":"Copies the template and all its image assets. Works on your templates and public library ones. An event uses the template itself, not a copy: a change to a template shows on every event that uses it from then on. To personalise a design for one client (their names, date or logo), copy it first (duplicate_template) and change the copy. To create a template from a design file instead, use create_template_from_pdf or create_template_from_images. An account can make 50 duplicates a day (UTC), all duplicate_* operations together; past that, rate_limited with Retry-After until midnight UTC. Booth.Events support can lift this limit for an account that needs more.","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"},"photoAreaCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Media-layer count; AI prompts/portraits attach only when this is exactly 1"},"aiCustomPromptIds":{"type":"array","items":{"type":"string"},"description":"Attached AI prompts (see list_ai_prompts)"},"aiPortraitIds":{"type":"array","items":{"type":"string"},"description":"Attached AI portrait styles (see list_ai_portraits)"},"disabledCaptureTypes":{"type":"array","items":{"type":"string"}},"sceneSelectionForced":{"type":"boolean"},"transparentScene":{"type":"boolean","description":"Offers a \"no scene\" option to guests"},"scenes":{"type":"array","items":{"type":"object","properties":{"storagePath":{"type":"string","description":"Key for update_template_scene / remove_template_scene"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"7-day signed URL — re-fetch to refresh"},"width":{"anyOf":[{"type":"number"},{"type":"null"}]},"height":{"anyOf":[{"type":"number"},{"type":"null"}]},"crop":{"anyOf":[{"type":"string","enum":["scene","photo"]},{"type":"null"}]},"compositingMethod":{"anyOf":[{"type":"string","enum":["sourceOver","addition","colorBlend","colorDodgeBlend","differenceBlend","exclusionBlend","lightenBlend","linearDodgeBlend","luminosityBlend","minimumCompositing","overlayBlend","screenBlend","softLightBlend"]},{"type":"null"}]},"uploadedOn":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["storagePath","thumbnailUrl","width","height","crop","compositingMethod","uploadedOn"],"additionalProperties":false},"description":"Green-screen background scenes"},"printable":{"type":"boolean","description":"false = photos with this template are not printed"},"printsShortestSideDoubled":{"type":"boolean","description":"Each print carries the design twice along its short side (2x6 → 2x2x6)"},"imageFilters":{"type":"array","items":{"type":"string"},"description":"The colour filters in the order guests see them; exactly one = forced on every capture; [] = none. May hold an id a newer iPad app added"},"bwCustom":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"highlights":{"type":"number"},"shadows":{"type":"number"},"colourResponse":{"type":"number"}},"required":["exposure","contrast","highlights","shadows","colourResponse"],"additionalProperties":false,"description":"Mono Custom (blackAndWhiteCustom) sliders, as the iPad uses them: the defaults until changed"},"filmStrip":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"grain":{"type":"number"},"warmth":{"type":"number"},"vignette":{"type":"number"},"glow":{"type":"number"},"softness":{"type":"number"}},"required":["exposure","contrast","grain","warmth","vignette","glow","softness"],"additionalProperties":false,"description":"Film Custom (blackAndWhiteFilmStrip) sliders, as the iPad uses them: the defaults until changed"},"glamFilter":{"type":"boolean","description":"The Glam Filter is on"},"glamFilterIntensity":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Its strength, 0 to 1. null = never set: the iPad applies no glam until it is, even with glamFilter on"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting","photoAreaCount","aiCustomPromptIds","aiPortraitIds","disabledCaptureTypes","sceneSelectionForced","transparentScene","scenes","printable","printsShortestSideDoubled","imageFilters","bwCustom","filmStrip","glamFilter","glamFilterIntensity"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200}},"required":["name"]}}}}}},"/attract-screens":{"get":{"operationId":"list_attract_screens","summary":"List your attract screens","description":"An attract screen is a full-screen design the iPad shows to draw guests in: once it has been idle for the event's settings.attractScreenDelay seconds (update_event attractScreenId), and, if chosen, behind the home screen in place of the live camera (update_event_branding). sizePixels is the iPad screen it was designed for, [width, height] in points. Attract screens are designed in the dashboard; through the API one can be made from a single picture (create_attract_screen_from_image) or video (create_attract_screen_from_video), and they can be listed, renamed, duplicated, deleted and assigned to events.","parameters":[{"name":"limit","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","default":25,"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string"}},{"name":"search","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","description":"Case-insensitive name prefix","type":"string","minLength":1},"description":"Case-insensitive name prefix"},{"name":"includePublic","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","description":"true = also include public library attract screens (e.g. account defaults reference these)","type":"boolean"},"description":"true = also include public library attract screens (e.g. account defaults reference these)"}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting"],"additionalProperties":false}},"hasMore":{"type":"boolean"},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["data","hasMore","nextCursor"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/attract-screens/{id}":{"get":{"operationId":"get_attract_screen","summary":"Get one attract screen (yours or a public one)","description":"An attract screen is a full-screen design the iPad shows to draw guests in: once it has been idle for the event's settings.attractScreenDelay seconds (update_event attractScreenId), and, if chosen, behind the home screen in place of the live camera (update_event_branding). sizePixels is the iPad screen it was designed for, [width, height] in points. Attract screens are designed in the dashboard; through the API one can be made from a single picture (create_attract_screen_from_image) or video (create_attract_screen_from_video), and they can be listed, renamed, duplicated, deleted and assigned to events.","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}},"patch":{"operationId":"rename_attract_screen","summary":"Rename an attract screen (metadata only)","description":"Only the name changes — the design/layers are editable exclusively in the dashboard editor.","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200}},"required":["name"]}}}}},"delete":{"operationId":"delete_attract_screen","summary":"Delete an attract screen (fails if events still use it)","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"deleted":{"type":"boolean","const":true},"id":{"type":"string"}},"required":["deleted","id"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/attract-screens/{id}/duplicate":{"post":{"operationId":"duplicate_attract_screen","summary":"Duplicate an attract screen (the only way to create one via API)","description":"Copies the item and all its image assets. Works on your items and public library items. An account can make 50 duplicates a day (UTC), all duplicate_* operations together; past that, rate_limited with Retry-After until midnight UTC. Booth.Events support can lift this limit for an account that needs more.","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200}},"required":["name"]}}}}}},"/sticker-sets":{"get":{"operationId":"list_sticker_sets","summary":"List your sticker sets","description":"A sticker set is the collection of stickers guests can add to their photos on the iPad (update_event stickerSetId). Sticker sets are made in the dashboard, from a ZIP of images, and cannot be created through the API: make one there and duplicate it, or assign it as it is. Here they can be listed, renamed, duplicated, deleted and assigned to events.","parameters":[{"name":"limit","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","default":25,"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string"}},{"name":"search","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","description":"Case-insensitive name prefix","type":"string","minLength":1},"description":"Case-insensitive name prefix"},{"name":"includePublic","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","description":"true = also include public library sticker sets (e.g. account defaults reference these)","type":"boolean"},"description":"true = also include public library sticker sets (e.g. account defaults reference these)"}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting"],"additionalProperties":false}},"hasMore":{"type":"boolean"},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["data","hasMore","nextCursor"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/sticker-sets/{id}":{"get":{"operationId":"get_sticker_set","summary":"Get one sticker set (yours or a public one)","description":"A sticker set is the collection of stickers guests can add to their photos on the iPad (update_event stickerSetId). Sticker sets are made in the dashboard, from a ZIP of images, and cannot be created through the API: make one there and duplicate it, or assign it as it is. Here they can be listed, renamed, duplicated, deleted and assigned to events.","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}},"patch":{"operationId":"rename_sticker_set","summary":"Rename a sticker set (metadata only)","description":"Only the name changes — the design/layers are editable exclusively in the dashboard editor.","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200}},"required":["name"]}}}}},"delete":{"operationId":"delete_sticker_set","summary":"Delete a sticker set (fails if events still use it)","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"deleted":{"type":"boolean","const":true},"id":{"type":"string"}},"required":["deleted","id"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/sticker-sets/{id}/duplicate":{"post":{"operationId":"duplicate_sticker_set","summary":"Duplicate a sticker set (the only way to create one via API)","description":"Copies the item and all its image assets. Works on your items and public library items. An account can make 50 duplicates a day (UTC), all duplicate_* operations together; past that, rate_limited with Retry-After until midnight UTC. Booth.Events support can lift this limit for an account that needs more.","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200}},"required":["name"]}}}}}},"/attract-screens/from-image":{"post":{"operationId":"create_attract_screen_from_image","summary":"Create an attract screen from one full-screen picture","description":"Makes an attract screen as the dashboard does, with the picture stretched over the whole iPad screen, on top of the live camera: a PNG's transparent parts show the camera through it. Make the picture the shape of the screen in sizePixels, at twice its size for a sharp result (e.g. 2064×2752 for [1032, 1376]): a picture of another shape is stretched. PNG or JPEG, at most 8 MB, 6000 px on a side and 36 megapixels, and upright (a JPEG with a rotation tag is refused). Provide it via uploadPath (create_upload purpose 'attract-image') or a public https sourceUrl. Assign it with update_event attractScreenId (shown after settings.attractScreenDelay idle seconds) or update_event_branding (behind the home screen). It can be changed later in the dashboard's attract-screen editor.","x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120,"description":"The name it is listed under"},"sizePixels":{"description":"The iPad screen it is for, [width, height] in screen points, as the dashboard's attract-screen sizes: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait (the default), [1376, 1032] landscape; [834, 1210] an 11-inch iPad Pro (2024 on), [820, 1180] a 10.9-inch iPad Air, [810, 1080] a 10.2- or 10.9-inch iPad, [744, 1133] an iPad mini. Each side 100–6000. The iPad stretches the screen's pictures to its own screen, so pick the one it is mounted as","minItems":2,"maxItems":2,"type":"array","items":{"type":"integer","minimum":100,"maximum":6000}},"uploadPath":{"description":"storagePath from create_upload (after PUTting the bytes)","type":"string"},"sourceUrl":{"description":"Public https URL fetched server-side (must not redirect) — alternative to uploadPath","type":"string"}},"required":["name"]}}}}}},"/attract-screens/from-video":{"post":{"operationId":"create_attract_screen_from_video","summary":"Create an attract screen from one full-screen video","description":"Makes an attract screen as the dashboard does with a background video: the iPad plays it full screen, filling the screen (cropping a video of another shape), over and over while it is idle. MP4 or MOV (QuickTime), at most 50 MB, as the dashboard takes them; the file is played as it is, not converted, so use one an iPad plays, such as H.264 or HEVC video, shaped like the screen in sizePixels. The iPad downloads it before it opens an event that uses it, so keep it short. The screen's picture in lists (thumbnailUrl) shows a video placeholder, not a frame of the video. Provide it via uploadPath (create_upload purpose 'attract-video') or a public https sourceUrl. Assign it with update_event attractScreenId (shown after settings.attractScreenDelay idle seconds) or update_event_branding (behind the home screen). It can be changed later in the dashboard's attract-screen editor.","x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120,"description":"The name it is listed under"},"sizePixels":{"description":"The iPad screen it is for, [width, height] in screen points, as the dashboard's attract-screen sizes: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait (the default), [1376, 1032] landscape; [834, 1210] an 11-inch iPad Pro (2024 on), [820, 1180] a 10.9-inch iPad Air, [810, 1080] a 10.2- or 10.9-inch iPad, [744, 1133] an iPad mini. Each side 100–6000. The iPad stretches the screen's pictures to its own screen, so pick the one it is mounted as","minItems":2,"maxItems":2,"type":"array","items":{"type":"integer","minimum":100,"maximum":6000}},"uploadPath":{"description":"storagePath from create_upload (after PUTting the bytes)","type":"string"},"sourceUrl":{"description":"Public https URL fetched server-side (must not redirect) — alternative to uploadPath","type":"string"}},"required":["name"]}}}}}},"/uploads":{"post":{"operationId":"create_upload","summary":"Mint a signed URL for uploading a file (step 1 of 2)","description":"Two-step upload: call this, then HTTP PUT the raw bytes to the returned uploadUrl — sending back the returned headers EXACTLY (they are part of the signature) — within 15 minutes. Then pass storagePath to the consuming operation (e.g. create_template_from_pdf, add_ai_prompt_reference_image, add_template_scene). Alternative: every consuming operation also accepts a public https sourceUrl it fetches server-side — use that when you cannot PUT binaries (AI agents over MCP, assets already hosted on your own storage).","x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"uploadUrl":{"type":"string","description":"PUT the raw bytes here (no auth header needed)"},"storagePath":{"type":"string","description":"Pass this as uploadPath to the consuming operation after the PUT succeeds"},"method":{"type":"string","const":"PUT"},"headers":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"},"description":"Send these headers EXACTLY on the PUT — they are part of the signature"},"expiresAt":{"type":"string","description":"ISO-8601 expiry of uploadUrl (~15 minutes)"},"maxSizeBytes":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991}},"required":["uploadUrl","storagePath","method","headers","expiresAt","maxSizeBytes"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"purpose":{"type":"string","enum":["template-pdf","template-zip","template-image","scene","event-branding-image","account-logo","ai-reference-image","ai-choice-image","template-choice-image","template-artwork","ai-preview-image","ai-test-photo","attract-image","attract-video"],"description":"What the file is for — decides allowed content types and the size cap"},"filename":{"type":"string","minLength":1,"maxLength":200,"description":"Original filename (sanitized server-side)"},"contentType":{"type":"string","minLength":1,"description":"MIME type of the bytes you will PUT — must match the purpose"}},"required":["purpose","filename","contentType"]}}}}}},"/templates/import-pdf":{"post":{"operationId":"create_template_from_pdf","summary":"Create a template by importing a PDF design (parsed into layers)","description":"Same pipeline as the dashboard \"Upload design\" PDF flow: text and artwork become image layers, and the photo areas guests get composited into are found automatically — make the design to the rules below; in a PDF, use white boxes. Provide the PDF via uploadPath (create_upload + PUT) or a public https sourceUrl. targetWidthPx sets the render width (default: the PDF page width in points, at least 1000 — native 72dpi sizes are usually too small for print). The rendered page may be at most 9600px on a side and 36 megapixels, which covers jumbo strips such as 2400x9600; a targetWidthPx that takes the page beyond that is refused. Only page 1 imports (warning \"only_first_page_imported\" for multi-page files); text is rasterized (not editable); transparent page backgrounds become white. thumbnailUrl is null only if preview rendering failed — the dashboard renders one lazily on first view. The template takes the shape and size of the design (for a PDF, its first page), and nothing checks it against a print size: an A4 or Letter page makes an A4- or Letter-shaped template, which does not fit photo paper. Check width and height in the result against the paper it will print on — a 4×6 print is 3:2 (1800×1200 landscape or 1200×1800 portrait at 300 dpi), a 2×6 strip is 1:3 (600×1800) — and if they do not match, ask for a design at the right size, delete this template (delete_template) and import again. The call returns when the import is done, which can take a few minutes. An account runs at most 3 imports and other heavy operations at once: send several one after another, and retry a 429 after its Retry-After. 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`.","x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"},"photoAreaCount":{"type":"number","description":"Detected photo areas. AI prompt/portrait assignment (update_template) requires exactly 1. Each is in layers[] (kind photoArea) with its rectangle: check them against the design and put them right with update_template_photo_area / add_template_photo_area / remove_template_photo_area."},"aiCustomPromptIds":{"type":"array","items":{"type":"string"},"description":"Attached AI prompts (see list_ai_prompts)"},"aiPortraitIds":{"type":"array","items":{"type":"string"},"description":"Attached AI portrait styles (see list_ai_portraits)"},"disabledCaptureTypes":{"type":"array","items":{"type":"string"}},"sceneSelectionForced":{"type":"boolean"},"transparentScene":{"type":"boolean","description":"Offers a \"no scene\" option to guests"},"scenes":{"type":"array","items":{"type":"object","properties":{"storagePath":{"type":"string","description":"Key for update_template_scene / remove_template_scene"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"7-day signed URL — re-fetch to refresh"},"width":{"anyOf":[{"type":"number"},{"type":"null"}]},"height":{"anyOf":[{"type":"number"},{"type":"null"}]},"crop":{"anyOf":[{"type":"string","enum":["scene","photo"]},{"type":"null"}]},"compositingMethod":{"anyOf":[{"type":"string","enum":["sourceOver","addition","colorBlend","colorDodgeBlend","differenceBlend","exclusionBlend","lightenBlend","linearDodgeBlend","luminosityBlend","minimumCompositing","overlayBlend","screenBlend","softLightBlend"]},{"type":"null"}]},"uploadedOn":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["storagePath","thumbnailUrl","width","height","crop","compositingMethod","uploadedOn"],"additionalProperties":false},"description":"Green-screen background scenes"},"printable":{"type":"boolean","description":"false = photos with this template are not printed"},"printsShortestSideDoubled":{"type":"boolean","description":"Each print carries the design twice along its short side (2x6 → 2x2x6)"},"imageFilters":{"type":"array","items":{"type":"string"},"description":"The colour filters in the order guests see them; exactly one = forced on every capture; [] = none. May hold an id a newer iPad app added"},"bwCustom":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"highlights":{"type":"number"},"shadows":{"type":"number"},"colourResponse":{"type":"number"}},"required":["exposure","contrast","highlights","shadows","colourResponse"],"additionalProperties":false,"description":"Mono Custom (blackAndWhiteCustom) sliders, as the iPad uses them: the defaults until changed"},"filmStrip":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"grain":{"type":"number"},"warmth":{"type":"number"},"vignette":{"type":"number"},"glow":{"type":"number"},"softness":{"type":"number"}},"required":["exposure","contrast","grain","warmth","vignette","glow","softness"],"additionalProperties":false,"description":"Film Custom (blackAndWhiteFilmStrip) sliders, as the iPad uses them: the defaults until changed"},"glamFilter":{"type":"boolean","description":"The Glam Filter is on"},"glamFilterIntensity":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Its strength, 0 to 1. null = never set: the iPad applies no glam until it is, even with glamFilter on"},"layers":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position in the stack, 0 = bottom"},"kind":{"type":"string","enum":["background","artwork","photoArea","text","field","other"],"description":"background = the base colour; artwork = a picture of the design; photoArea = where a photo goes; text; field = a dynamic element (a guest answer, printed); other = live view or video"},"x":{"type":"number","description":"Left edge, in template pixels (see sizePixels)"},"y":{"type":"number","description":"Top edge, in template pixels"},"width":{"type":"number"},"height":{"type":"number"},"rotation":{"type":"number","description":"Degrees clockwise around the top-left corner"},"opacity":{"type":"number","description":"0 to 1"},"color":{"description":"background: the colour; text and field: the text colour","type":"string"},"name":{"description":"artwork: the picture file","type":"string"},"order":{"description":"photoArea: the order photos are taken in","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"textId":{"description":"text: the key for update_template_text / remove_template_layer","type":"string"},"text":{"type":"string"},"fontFamily":{"type":"string"},"fontWeight":{"type":"string"},"fontStyle":{"type":"string"},"fontSize":{"description":"text: in template pixels; field: the largest size the answer is set in","type":"number"},"textAlign":{"type":"string"},"fieldId":{"description":"field: the dynamic field shown (see fields / placeableFields)","type":"string"},"display":{"description":"field: the answer as words, or as the chosen picture","type":"string","enum":["text","image"]},"preview":{"description":"field, text display of a free-text field: the sample shown until a guest answers","anyOf":[{"type":"string"},{"type":"null"}]},"previewChoiceId":{"description":"field, choice field: the choice the design shows","anyOf":[{"type":"string"},{"type":"null"}]},"allCaps":{"description":"field: the answer is printed in capitals","type":"boolean"},"type":{"description":"other: the stored layer type","type":"string"}},"required":["index","kind","x","y","width","height","rotation","opacity"],"additionalProperties":false},"description":"The design, bottom to top, each layer as a rectangle in template pixels. Photo areas say where guests land; text and field layers are the ones add_template_text / add_template_field make and update_template_text / remove_template_layer change; artwork is a picture of the design (add_template_image / update_template_image). move_template_layer changes what is drawn over what"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}}},"required":["id","type","name","text"],"additionalProperties":false},"description":"The template's own dynamic fields (made by add_template_field). Each is asked of the guest when this template is used, and offered to update_event_survey on events using it"},"placeableFields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}},"owner":{"type":"object","properties":{"kind":{"type":"string","enum":["template","prompt"]},"id":{"type":"string"}},"required":["kind","id"],"additionalProperties":false,"description":"Who the field belongs to: this template, or an AI prompt assigned to it"}},"required":["id","type","name","text","owner"],"additionalProperties":false},"description":"Every field add_template_field can place by fieldId: the own fields, and the fields of the AI prompts assigned to this template (update_template aiCustomPromptIds)"},"warnings":{"type":"array","items":{"type":"string"},"description":"Non-fatal notes: 'only_first_page_imported' (multi-page PDFs), 'no_photo_areas_detected' (guests cannot be composited into this template: the design broke the photo-area rules in this tool's description — fix it and import again, or add the areas with add_template_photo_area)"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting","photoAreaCount","aiCustomPromptIds","aiPortraitIds","disabledCaptureTypes","sceneSelectionForced","transparentScene","scenes","printable","printsShortestSideDoubled","imageFilters","bwCustom","filmStrip","glamFilter","glamFilterIntensity","layers","fields","placeableFields","warnings"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120,"description":"Template name"},"uploadPath":{"description":"storagePath from create_upload (after PUTting the bytes)","type":"string"},"sourceUrl":{"description":"Public https URL fetched server-side (must not redirect) — alternative to uploadPath","type":"string"},"targetWidthPx":{"type":"integer","minimum":100,"maximum":9600},"disabledCaptureTypes":{"type":"array","items":{"type":"string","enum":["photo","boomerang","slowmo","video","gif","aiPhoto","aiCustomPrompt"]}}},"required":["name"]}}}}}},"/templates/import-images":{"post":{"operationId":"create_template_from_images","summary":"Create a template from PNG/JPG layer images or a ZIP of them","description":"Same pipeline as the dashboard \"Upload design\" image/ZIP flow: images stack largest-first (the largest sets the template size), and the photo areas guests get composited into are found automatically in the stacked design — make it to the rules below. Export every image at the full size of the design: each is stretched over the whole design when the photo areas are looked for, but placed at its own size in the template. Provide EITHER images[] (each via uploadPath or sourceUrl) OR one zip (zipUploadPath/zipSourceUrl containing flat .png/.jpg layers, e.g. a Photoshop export). Each image may be at most 9600px on a side and 36 megapixels (jumbo strips such as 2400x9600 fit), and at most 16 MB; all images together at most 150 megapixels. thumbnailUrl is null only if preview rendering failed — the dashboard renders one lazily on first view. The template takes the shape and size of the design (for a PDF, its first page), and nothing checks it against a print size: an A4 or Letter page makes an A4- or Letter-shaped template, which does not fit photo paper. Check width and height in the result against the paper it will print on — a 4×6 print is 3:2 (1800×1200 landscape or 1200×1800 portrait at 300 dpi), a 2×6 strip is 1:3 (600×1800) — and if they do not match, ask for a design at the right size, delete this template (delete_template) and import again. The call returns when the import is done, which can take a few minutes. An account runs at most 3 imports and other heavy operations at once: send several one after another, and retry a 429 after its Retry-After. 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`.","x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"},"photoAreaCount":{"type":"number","description":"Detected photo areas. AI prompt/portrait assignment (update_template) requires exactly 1. Each is in layers[] (kind photoArea) with its rectangle: check them against the design and put them right with update_template_photo_area / add_template_photo_area / remove_template_photo_area."},"aiCustomPromptIds":{"type":"array","items":{"type":"string"},"description":"Attached AI prompts (see list_ai_prompts)"},"aiPortraitIds":{"type":"array","items":{"type":"string"},"description":"Attached AI portrait styles (see list_ai_portraits)"},"disabledCaptureTypes":{"type":"array","items":{"type":"string"}},"sceneSelectionForced":{"type":"boolean"},"transparentScene":{"type":"boolean","description":"Offers a \"no scene\" option to guests"},"scenes":{"type":"array","items":{"type":"object","properties":{"storagePath":{"type":"string","description":"Key for update_template_scene / remove_template_scene"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"7-day signed URL — re-fetch to refresh"},"width":{"anyOf":[{"type":"number"},{"type":"null"}]},"height":{"anyOf":[{"type":"number"},{"type":"null"}]},"crop":{"anyOf":[{"type":"string","enum":["scene","photo"]},{"type":"null"}]},"compositingMethod":{"anyOf":[{"type":"string","enum":["sourceOver","addition","colorBlend","colorDodgeBlend","differenceBlend","exclusionBlend","lightenBlend","linearDodgeBlend","luminosityBlend","minimumCompositing","overlayBlend","screenBlend","softLightBlend"]},{"type":"null"}]},"uploadedOn":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["storagePath","thumbnailUrl","width","height","crop","compositingMethod","uploadedOn"],"additionalProperties":false},"description":"Green-screen background scenes"},"printable":{"type":"boolean","description":"false = photos with this template are not printed"},"printsShortestSideDoubled":{"type":"boolean","description":"Each print carries the design twice along its short side (2x6 → 2x2x6)"},"imageFilters":{"type":"array","items":{"type":"string"},"description":"The colour filters in the order guests see them; exactly one = forced on every capture; [] = none. May hold an id a newer iPad app added"},"bwCustom":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"highlights":{"type":"number"},"shadows":{"type":"number"},"colourResponse":{"type":"number"}},"required":["exposure","contrast","highlights","shadows","colourResponse"],"additionalProperties":false,"description":"Mono Custom (blackAndWhiteCustom) sliders, as the iPad uses them: the defaults until changed"},"filmStrip":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"grain":{"type":"number"},"warmth":{"type":"number"},"vignette":{"type":"number"},"glow":{"type":"number"},"softness":{"type":"number"}},"required":["exposure","contrast","grain","warmth","vignette","glow","softness"],"additionalProperties":false,"description":"Film Custom (blackAndWhiteFilmStrip) sliders, as the iPad uses them: the defaults until changed"},"glamFilter":{"type":"boolean","description":"The Glam Filter is on"},"glamFilterIntensity":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Its strength, 0 to 1. null = never set: the iPad applies no glam until it is, even with glamFilter on"},"layers":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position in the stack, 0 = bottom"},"kind":{"type":"string","enum":["background","artwork","photoArea","text","field","other"],"description":"background = the base colour; artwork = a picture of the design; photoArea = where a photo goes; text; field = a dynamic element (a guest answer, printed); other = live view or video"},"x":{"type":"number","description":"Left edge, in template pixels (see sizePixels)"},"y":{"type":"number","description":"Top edge, in template pixels"},"width":{"type":"number"},"height":{"type":"number"},"rotation":{"type":"number","description":"Degrees clockwise around the top-left corner"},"opacity":{"type":"number","description":"0 to 1"},"color":{"description":"background: the colour; text and field: the text colour","type":"string"},"name":{"description":"artwork: the picture file","type":"string"},"order":{"description":"photoArea: the order photos are taken in","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"textId":{"description":"text: the key for update_template_text / remove_template_layer","type":"string"},"text":{"type":"string"},"fontFamily":{"type":"string"},"fontWeight":{"type":"string"},"fontStyle":{"type":"string"},"fontSize":{"description":"text: in template pixels; field: the largest size the answer is set in","type":"number"},"textAlign":{"type":"string"},"fieldId":{"description":"field: the dynamic field shown (see fields / placeableFields)","type":"string"},"display":{"description":"field: the answer as words, or as the chosen picture","type":"string","enum":["text","image"]},"preview":{"description":"field, text display of a free-text field: the sample shown until a guest answers","anyOf":[{"type":"string"},{"type":"null"}]},"previewChoiceId":{"description":"field, choice field: the choice the design shows","anyOf":[{"type":"string"},{"type":"null"}]},"allCaps":{"description":"field: the answer is printed in capitals","type":"boolean"},"type":{"description":"other: the stored layer type","type":"string"}},"required":["index","kind","x","y","width","height","rotation","opacity"],"additionalProperties":false},"description":"The design, bottom to top, each layer as a rectangle in template pixels. Photo areas say where guests land; text and field layers are the ones add_template_text / add_template_field make and update_template_text / remove_template_layer change; artwork is a picture of the design (add_template_image / update_template_image). move_template_layer changes what is drawn over what"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}}},"required":["id","type","name","text"],"additionalProperties":false},"description":"The template's own dynamic fields (made by add_template_field). Each is asked of the guest when this template is used, and offered to update_event_survey on events using it"},"placeableFields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}},"owner":{"type":"object","properties":{"kind":{"type":"string","enum":["template","prompt"]},"id":{"type":"string"}},"required":["kind","id"],"additionalProperties":false,"description":"Who the field belongs to: this template, or an AI prompt assigned to it"}},"required":["id","type","name","text","owner"],"additionalProperties":false},"description":"Every field add_template_field can place by fieldId: the own fields, and the fields of the AI prompts assigned to this template (update_template aiCustomPromptIds)"},"warnings":{"type":"array","items":{"type":"string"},"description":"Non-fatal notes: 'only_first_page_imported' (multi-page PDFs), 'no_photo_areas_detected' (guests cannot be composited into this template: the design broke the photo-area rules in this tool's description — fix it and import again, or add the areas with add_template_photo_area)"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting","photoAreaCount","aiCustomPromptIds","aiPortraitIds","disabledCaptureTypes","sceneSelectionForced","transparentScene","scenes","printable","printsShortestSideDoubled","imageFilters","bwCustom","filmStrip","glamFilter","glamFilterIntensity","layers","fields","placeableFields","warnings"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":120,"description":"Template name"},"images":{"minItems":1,"maxItems":40,"type":"array","items":{"type":"object","properties":{"uploadPath":{"description":"storagePath from create_upload (after PUTting the bytes)","type":"string"},"sourceUrl":{"description":"Public https URL fetched server-side (must not redirect) — alternative to uploadPath","type":"string"}}}},"zipUploadPath":{"description":"storagePath from create_upload with purpose \"template-zip\" (after PUTting the bytes)","type":"string"},"zipSourceUrl":{"description":"Public https URL of a .zip of flat .png/.jpg layers, fetched server-side (must not redirect)","type":"string"},"disabledCaptureTypes":{"type":"array","items":{"type":"string","enum":["photo","boomerang","slowmo","video","gif","aiPhoto","aiCustomPrompt"]}}},"required":["name"]}}}}}},"/templates/{id}/text":{"post":{"operationId":"add_template_text","summary":"Add a line of text to a template (e.g. the couple’s names, the date)","description":"Adds a text layer on top of the design (or at toIndex in the stack), as the dashboard editor does: any Google Font (list_fonts), weight, italic, size, colour and alignment. Position by the top-left corner in template pixels, or leave x out to centre it. The text is measured in its font, so the result says how wide it came out (layers[].width); the thumbnail shows it. Through MCP the thumbnail comes with the result as an image: look at it and adjust with update_template_text. An event uses the template itself, not a copy: a change to a template shows on every event that uses it from then on. To personalise a design for one client (their names, date or logo), copy it first (duplicate_template) and change the copy.","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"The template (yours, not a public one)"}}],"x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"},"photoAreaCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Media-layer count; AI prompts/portraits attach only when this is exactly 1"},"aiCustomPromptIds":{"type":"array","items":{"type":"string"},"description":"Attached AI prompts (see list_ai_prompts)"},"aiPortraitIds":{"type":"array","items":{"type":"string"},"description":"Attached AI portrait styles (see list_ai_portraits)"},"disabledCaptureTypes":{"type":"array","items":{"type":"string"}},"sceneSelectionForced":{"type":"boolean"},"transparentScene":{"type":"boolean","description":"Offers a \"no scene\" option to guests"},"scenes":{"type":"array","items":{"type":"object","properties":{"storagePath":{"type":"string","description":"Key for update_template_scene / remove_template_scene"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"7-day signed URL — re-fetch to refresh"},"width":{"anyOf":[{"type":"number"},{"type":"null"}]},"height":{"anyOf":[{"type":"number"},{"type":"null"}]},"crop":{"anyOf":[{"type":"string","enum":["scene","photo"]},{"type":"null"}]},"compositingMethod":{"anyOf":[{"type":"string","enum":["sourceOver","addition","colorBlend","colorDodgeBlend","differenceBlend","exclusionBlend","lightenBlend","linearDodgeBlend","luminosityBlend","minimumCompositing","overlayBlend","screenBlend","softLightBlend"]},{"type":"null"}]},"uploadedOn":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["storagePath","thumbnailUrl","width","height","crop","compositingMethod","uploadedOn"],"additionalProperties":false},"description":"Green-screen background scenes"},"printable":{"type":"boolean","description":"false = photos with this template are not printed"},"printsShortestSideDoubled":{"type":"boolean","description":"Each print carries the design twice along its short side (2x6 → 2x2x6)"},"imageFilters":{"type":"array","items":{"type":"string"},"description":"The colour filters in the order guests see them; exactly one = forced on every capture; [] = none. May hold an id a newer iPad app added"},"bwCustom":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"highlights":{"type":"number"},"shadows":{"type":"number"},"colourResponse":{"type":"number"}},"required":["exposure","contrast","highlights","shadows","colourResponse"],"additionalProperties":false,"description":"Mono Custom (blackAndWhiteCustom) sliders, as the iPad uses them: the defaults until changed"},"filmStrip":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"grain":{"type":"number"},"warmth":{"type":"number"},"vignette":{"type":"number"},"glow":{"type":"number"},"softness":{"type":"number"}},"required":["exposure","contrast","grain","warmth","vignette","glow","softness"],"additionalProperties":false,"description":"Film Custom (blackAndWhiteFilmStrip) sliders, as the iPad uses them: the defaults until changed"},"glamFilter":{"type":"boolean","description":"The Glam Filter is on"},"glamFilterIntensity":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Its strength, 0 to 1. null = never set: the iPad applies no glam until it is, even with glamFilter on"},"layers":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position in the stack, 0 = bottom"},"kind":{"type":"string","enum":["background","artwork","photoArea","text","field","other"],"description":"background = the base colour; artwork = a picture of the design; photoArea = where a photo goes; text; field = a dynamic element (a guest answer, printed); other = live view or video"},"x":{"type":"number","description":"Left edge, in template pixels (see sizePixels)"},"y":{"type":"number","description":"Top edge, in template pixels"},"width":{"type":"number"},"height":{"type":"number"},"rotation":{"type":"number","description":"Degrees clockwise around the top-left corner"},"opacity":{"type":"number","description":"0 to 1"},"color":{"description":"background: the colour; text and field: the text colour","type":"string"},"name":{"description":"artwork: the picture file","type":"string"},"order":{"description":"photoArea: the order photos are taken in","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"textId":{"description":"text: the key for update_template_text / remove_template_layer","type":"string"},"text":{"type":"string"},"fontFamily":{"type":"string"},"fontWeight":{"type":"string"},"fontStyle":{"type":"string"},"fontSize":{"description":"text: in template pixels; field: the largest size the answer is set in","type":"number"},"textAlign":{"type":"string"},"fieldId":{"description":"field: the dynamic field shown (see fields / placeableFields)","type":"string"},"display":{"description":"field: the answer as words, or as the chosen picture","type":"string","enum":["text","image"]},"preview":{"description":"field, text display of a free-text field: the sample shown until a guest answers","anyOf":[{"type":"string"},{"type":"null"}]},"previewChoiceId":{"description":"field, choice field: the choice the design shows","anyOf":[{"type":"string"},{"type":"null"}]},"allCaps":{"description":"field: the answer is printed in capitals","type":"boolean"},"type":{"description":"other: the stored layer type","type":"string"}},"required":["index","kind","x","y","width","height","rotation","opacity"],"additionalProperties":false},"description":"The design, bottom to top, each layer as a rectangle in template pixels. Photo areas say where guests land; text and field layers are the ones add_template_text / add_template_field make and update_template_text / remove_template_layer change; artwork is a picture of the design (add_template_image / update_template_image). move_template_layer changes what is drawn over what"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}}},"required":["id","type","name","text"],"additionalProperties":false},"description":"The template's own dynamic fields (made by add_template_field). Each is asked of the guest when this template is used, and offered to update_event_survey on events using it"},"placeableFields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}},"owner":{"type":"object","properties":{"kind":{"type":"string","enum":["template","prompt"]},"id":{"type":"string"}},"required":["kind","id"],"additionalProperties":false,"description":"Who the field belongs to: this template, or an AI prompt assigned to it"}},"required":["id","type","name","text","owner"],"additionalProperties":false},"description":"Every field add_template_field can place by fieldId: the own fields, and the fields of the AI prompts assigned to this template (update_template aiCustomPromptIds)"},"textId":{"type":"string","description":"The new layer, for update_template_text / remove_template_layer"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting","photoAreaCount","aiCustomPromptIds","aiPortraitIds","disabledCaptureTypes","sceneSelectionForced","transparentScene","scenes","printable","printsShortestSideDoubled","imageFilters","bwCustom","filmStrip","glamFilter","glamFilterIntensity","layers","fields","placeableFields","textId"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"text":{"description":"The text. A line break starts a new line; lines never wrap","type":"string","maxLength":1000},"x":{"description":"Left edge, in template pixels (see sizePixels). Left out: centred horizontally","type":"number"},"y":{"type":"number","description":"Top edge, in template pixels"},"rotation":{"description":"Degrees clockwise, around the top-left corner. Default 0","type":"number","minimum":-360,"maximum":360},"opacity":{"description":"0 to 1. Default 1","type":"number","minimum":0,"maximum":1},"fontFamily":{"type":"string","minLength":1,"maxLength":100,"description":"A Google Fonts family from list_fonts, e.g. \"Open Sans\". Default Roboto"},"fontWeight":{"type":"string","enum":["100","200","300","400","500","600","700","800","900","normal","bold"],"description":"One of the family's weights from list_fonts: 'normal' (regular), '100'…'900', or 'bold' (700). Default: the family's regular weight"},"fontStyle":{"type":"string","enum":["normal","italic"],"description":"Default normal"},"color":{"description":"The text colour, #rrggbb. Default #000000","type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"textAlign":{"description":"For a text with several lines: how the lines line up. Default left","type":"string","enum":["left","center","right"]},"fontSize":{"description":"In template pixels (a 1800px-wide print needs far more than a screen). Default 40","type":"integer","minimum":1,"maximum":9999},"toIndex":{"description":"Where in the stack the new layer goes: the index it takes (layers[].index, 0 = bottom); the layers from there up move one higher. The background colour stays at index 0, so 1 is just above it. Default: on top of every other layer","type":"integer","minimum":0,"maximum":9007199254740991}},"required":["text","y"]}}}}}},"/templates/{id}/text/{textId}":{"patch":{"operationId":"update_template_text","summary":"Change a text layer: its text, font, size, colour, position","description":"Changes only what is sent; the rest stays. A new text or typography is measured again; a text that was centred stays centred unless x is sent. Works on any text layer of the template, the dashboard’s included.","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"The template (yours, not a public one)"}},{"name":"textId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"The layer, from get_template (layers[].textId)"}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"},"photoAreaCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Media-layer count; AI prompts/portraits attach only when this is exactly 1"},"aiCustomPromptIds":{"type":"array","items":{"type":"string"},"description":"Attached AI prompts (see list_ai_prompts)"},"aiPortraitIds":{"type":"array","items":{"type":"string"},"description":"Attached AI portrait styles (see list_ai_portraits)"},"disabledCaptureTypes":{"type":"array","items":{"type":"string"}},"sceneSelectionForced":{"type":"boolean"},"transparentScene":{"type":"boolean","description":"Offers a \"no scene\" option to guests"},"scenes":{"type":"array","items":{"type":"object","properties":{"storagePath":{"type":"string","description":"Key for update_template_scene / remove_template_scene"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"7-day signed URL — re-fetch to refresh"},"width":{"anyOf":[{"type":"number"},{"type":"null"}]},"height":{"anyOf":[{"type":"number"},{"type":"null"}]},"crop":{"anyOf":[{"type":"string","enum":["scene","photo"]},{"type":"null"}]},"compositingMethod":{"anyOf":[{"type":"string","enum":["sourceOver","addition","colorBlend","colorDodgeBlend","differenceBlend","exclusionBlend","lightenBlend","linearDodgeBlend","luminosityBlend","minimumCompositing","overlayBlend","screenBlend","softLightBlend"]},{"type":"null"}]},"uploadedOn":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["storagePath","thumbnailUrl","width","height","crop","compositingMethod","uploadedOn"],"additionalProperties":false},"description":"Green-screen background scenes"},"printable":{"type":"boolean","description":"false = photos with this template are not printed"},"printsShortestSideDoubled":{"type":"boolean","description":"Each print carries the design twice along its short side (2x6 → 2x2x6)"},"imageFilters":{"type":"array","items":{"type":"string"},"description":"The colour filters in the order guests see them; exactly one = forced on every capture; [] = none. May hold an id a newer iPad app added"},"bwCustom":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"highlights":{"type":"number"},"shadows":{"type":"number"},"colourResponse":{"type":"number"}},"required":["exposure","contrast","highlights","shadows","colourResponse"],"additionalProperties":false,"description":"Mono Custom (blackAndWhiteCustom) sliders, as the iPad uses them: the defaults until changed"},"filmStrip":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"grain":{"type":"number"},"warmth":{"type":"number"},"vignette":{"type":"number"},"glow":{"type":"number"},"softness":{"type":"number"}},"required":["exposure","contrast","grain","warmth","vignette","glow","softness"],"additionalProperties":false,"description":"Film Custom (blackAndWhiteFilmStrip) sliders, as the iPad uses them: the defaults until changed"},"glamFilter":{"type":"boolean","description":"The Glam Filter is on"},"glamFilterIntensity":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Its strength, 0 to 1. null = never set: the iPad applies no glam until it is, even with glamFilter on"},"layers":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position in the stack, 0 = bottom"},"kind":{"type":"string","enum":["background","artwork","photoArea","text","field","other"],"description":"background = the base colour; artwork = a picture of the design; photoArea = where a photo goes; text; field = a dynamic element (a guest answer, printed); other = live view or video"},"x":{"type":"number","description":"Left edge, in template pixels (see sizePixels)"},"y":{"type":"number","description":"Top edge, in template pixels"},"width":{"type":"number"},"height":{"type":"number"},"rotation":{"type":"number","description":"Degrees clockwise around the top-left corner"},"opacity":{"type":"number","description":"0 to 1"},"color":{"description":"background: the colour; text and field: the text colour","type":"string"},"name":{"description":"artwork: the picture file","type":"string"},"order":{"description":"photoArea: the order photos are taken in","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"textId":{"description":"text: the key for update_template_text / remove_template_layer","type":"string"},"text":{"type":"string"},"fontFamily":{"type":"string"},"fontWeight":{"type":"string"},"fontStyle":{"type":"string"},"fontSize":{"description":"text: in template pixels; field: the largest size the answer is set in","type":"number"},"textAlign":{"type":"string"},"fieldId":{"description":"field: the dynamic field shown (see fields / placeableFields)","type":"string"},"display":{"description":"field: the answer as words, or as the chosen picture","type":"string","enum":["text","image"]},"preview":{"description":"field, text display of a free-text field: the sample shown until a guest answers","anyOf":[{"type":"string"},{"type":"null"}]},"previewChoiceId":{"description":"field, choice field: the choice the design shows","anyOf":[{"type":"string"},{"type":"null"}]},"allCaps":{"description":"field: the answer is printed in capitals","type":"boolean"},"type":{"description":"other: the stored layer type","type":"string"}},"required":["index","kind","x","y","width","height","rotation","opacity"],"additionalProperties":false},"description":"The design, bottom to top, each layer as a rectangle in template pixels. Photo areas say where guests land; text and field layers are the ones add_template_text / add_template_field make and update_template_text / remove_template_layer change; artwork is a picture of the design (add_template_image / update_template_image). move_template_layer changes what is drawn over what"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}}},"required":["id","type","name","text"],"additionalProperties":false},"description":"The template's own dynamic fields (made by add_template_field). Each is asked of the guest when this template is used, and offered to update_event_survey on events using it"},"placeableFields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}},"owner":{"type":"object","properties":{"kind":{"type":"string","enum":["template","prompt"]},"id":{"type":"string"}},"required":["kind","id"],"additionalProperties":false,"description":"Who the field belongs to: this template, or an AI prompt assigned to it"}},"required":["id","type","name","text","owner"],"additionalProperties":false},"description":"Every field add_template_field can place by fieldId: the own fields, and the fields of the AI prompts assigned to this template (update_template aiCustomPromptIds)"},"textId":{"type":"string","description":"The new layer, for update_template_text / remove_template_layer"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting","photoAreaCount","aiCustomPromptIds","aiPortraitIds","disabledCaptureTypes","sceneSelectionForced","transparentScene","scenes","printable","printsShortestSideDoubled","imageFilters","bwCustom","filmStrip","glamFilter","glamFilterIntensity","layers","fields","placeableFields","textId"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"text":{"description":"The text. A line break starts a new line; lines never wrap","type":"string","maxLength":1000},"x":{"description":"Left edge, in template pixels (see sizePixels). Left out: centred horizontally","type":"number"},"y":{"description":"Top edge, in template pixels","type":"number"},"rotation":{"description":"Degrees clockwise, around the top-left corner. Default 0","type":"number","minimum":-360,"maximum":360},"opacity":{"description":"0 to 1. Default 1","type":"number","minimum":0,"maximum":1},"fontFamily":{"type":"string","minLength":1,"maxLength":100,"description":"A Google Fonts family from list_fonts, e.g. \"Open Sans\". Default Roboto"},"fontWeight":{"type":"string","enum":["100","200","300","400","500","600","700","800","900","normal","bold"],"description":"One of the family's weights from list_fonts: 'normal' (regular), '100'…'900', or 'bold' (700). Default: the family's regular weight"},"fontStyle":{"type":"string","enum":["normal","italic"],"description":"Default normal"},"color":{"description":"The text colour, #rrggbb. Default #000000","type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"textAlign":{"description":"For a text with several lines: how the lines line up. Default left","type":"string","enum":["left","center","right"]},"fontSize":{"description":"In template pixels","type":"integer","minimum":1,"maximum":9999}}}}}}}},"/templates/{id}/fields":{"post":{"operationId":"add_template_field","summary":"Print a guest’s answer on a template: a new question, or a field of an assigned AI prompt","description":"Places a dynamic field on the design, as the dashboard’s \"Dynamic element\" does. With `field`, the template gets a question of its own (asked of every guest who uses it, and offered to update_event_survey); with `fieldId`, an existing field is shown: one of the template’s own, or one of an AI prompt assigned with update_template (placeableFields in get_template). A free-text answer is printed as one line that shrinks to fit the box; a choice is printed as its words, or (image_multi_select) as the picture chosen. Until a guest answers, the design shows the preview. It goes on top of the design, or at toIndex in the stack. Through MCP the thumbnail comes with the result as an image.","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"The template (yours, not a public one)"}}],"x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"},"photoAreaCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Media-layer count; AI prompts/portraits attach only when this is exactly 1"},"aiCustomPromptIds":{"type":"array","items":{"type":"string"},"description":"Attached AI prompts (see list_ai_prompts)"},"aiPortraitIds":{"type":"array","items":{"type":"string"},"description":"Attached AI portrait styles (see list_ai_portraits)"},"disabledCaptureTypes":{"type":"array","items":{"type":"string"}},"sceneSelectionForced":{"type":"boolean"},"transparentScene":{"type":"boolean","description":"Offers a \"no scene\" option to guests"},"scenes":{"type":"array","items":{"type":"object","properties":{"storagePath":{"type":"string","description":"Key for update_template_scene / remove_template_scene"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"7-day signed URL — re-fetch to refresh"},"width":{"anyOf":[{"type":"number"},{"type":"null"}]},"height":{"anyOf":[{"type":"number"},{"type":"null"}]},"crop":{"anyOf":[{"type":"string","enum":["scene","photo"]},{"type":"null"}]},"compositingMethod":{"anyOf":[{"type":"string","enum":["sourceOver","addition","colorBlend","colorDodgeBlend","differenceBlend","exclusionBlend","lightenBlend","linearDodgeBlend","luminosityBlend","minimumCompositing","overlayBlend","screenBlend","softLightBlend"]},{"type":"null"}]},"uploadedOn":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["storagePath","thumbnailUrl","width","height","crop","compositingMethod","uploadedOn"],"additionalProperties":false},"description":"Green-screen background scenes"},"printable":{"type":"boolean","description":"false = photos with this template are not printed"},"printsShortestSideDoubled":{"type":"boolean","description":"Each print carries the design twice along its short side (2x6 → 2x2x6)"},"imageFilters":{"type":"array","items":{"type":"string"},"description":"The colour filters in the order guests see them; exactly one = forced on every capture; [] = none. May hold an id a newer iPad app added"},"bwCustom":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"highlights":{"type":"number"},"shadows":{"type":"number"},"colourResponse":{"type":"number"}},"required":["exposure","contrast","highlights","shadows","colourResponse"],"additionalProperties":false,"description":"Mono Custom (blackAndWhiteCustom) sliders, as the iPad uses them: the defaults until changed"},"filmStrip":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"grain":{"type":"number"},"warmth":{"type":"number"},"vignette":{"type":"number"},"glow":{"type":"number"},"softness":{"type":"number"}},"required":["exposure","contrast","grain","warmth","vignette","glow","softness"],"additionalProperties":false,"description":"Film Custom (blackAndWhiteFilmStrip) sliders, as the iPad uses them: the defaults until changed"},"glamFilter":{"type":"boolean","description":"The Glam Filter is on"},"glamFilterIntensity":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Its strength, 0 to 1. null = never set: the iPad applies no glam until it is, even with glamFilter on"},"layers":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position in the stack, 0 = bottom"},"kind":{"type":"string","enum":["background","artwork","photoArea","text","field","other"],"description":"background = the base colour; artwork = a picture of the design; photoArea = where a photo goes; text; field = a dynamic element (a guest answer, printed); other = live view or video"},"x":{"type":"number","description":"Left edge, in template pixels (see sizePixels)"},"y":{"type":"number","description":"Top edge, in template pixels"},"width":{"type":"number"},"height":{"type":"number"},"rotation":{"type":"number","description":"Degrees clockwise around the top-left corner"},"opacity":{"type":"number","description":"0 to 1"},"color":{"description":"background: the colour; text and field: the text colour","type":"string"},"name":{"description":"artwork: the picture file","type":"string"},"order":{"description":"photoArea: the order photos are taken in","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"textId":{"description":"text: the key for update_template_text / remove_template_layer","type":"string"},"text":{"type":"string"},"fontFamily":{"type":"string"},"fontWeight":{"type":"string"},"fontStyle":{"type":"string"},"fontSize":{"description":"text: in template pixels; field: the largest size the answer is set in","type":"number"},"textAlign":{"type":"string"},"fieldId":{"description":"field: the dynamic field shown (see fields / placeableFields)","type":"string"},"display":{"description":"field: the answer as words, or as the chosen picture","type":"string","enum":["text","image"]},"preview":{"description":"field, text display of a free-text field: the sample shown until a guest answers","anyOf":[{"type":"string"},{"type":"null"}]},"previewChoiceId":{"description":"field, choice field: the choice the design shows","anyOf":[{"type":"string"},{"type":"null"}]},"allCaps":{"description":"field: the answer is printed in capitals","type":"boolean"},"type":{"description":"other: the stored layer type","type":"string"}},"required":["index","kind","x","y","width","height","rotation","opacity"],"additionalProperties":false},"description":"The design, bottom to top, each layer as a rectangle in template pixels. Photo areas say where guests land; text and field layers are the ones add_template_text / add_template_field make and update_template_text / remove_template_layer change; artwork is a picture of the design (add_template_image / update_template_image). move_template_layer changes what is drawn over what"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}}},"required":["id","type","name","text"],"additionalProperties":false},"description":"The template's own dynamic fields (made by add_template_field). Each is asked of the guest when this template is used, and offered to update_event_survey on events using it"},"placeableFields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}},"owner":{"type":"object","properties":{"kind":{"type":"string","enum":["template","prompt"]},"id":{"type":"string"}},"required":["kind","id"],"additionalProperties":false,"description":"Who the field belongs to: this template, or an AI prompt assigned to it"}},"required":["id","type","name","text","owner"],"additionalProperties":false},"description":"Every field add_template_field can place by fieldId: the own fields, and the fields of the AI prompts assigned to this template (update_template aiCustomPromptIds)"},"fieldId":{"type":"string","description":"The field shown (new or existing), for remove_template_layer"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting","photoAreaCount","aiCustomPromptIds","aiPortraitIds","disabledCaptureTypes","sceneSelectionForced","transparentScene","scenes","printable","printsShortestSideDoubled","imageFilters","bwCustom","filmStrip","glamFilter","glamFilterIntensity","layers","fields","placeableFields","fieldId"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"field":{"oneOf":[{"type":"object","properties":{"type":{"type":"string","const":"string"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type","type":"string","maxLength":200},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"The question the guest reads"}},"required":["type","text"],"additionalProperties":false,"description":"A line of free text the guest types"},{"type":"object","properties":{"type":{"type":"string","const":"full_name"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type","type":"string","maxLength":200},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"The question the guest reads"}},"required":["type","text"],"additionalProperties":false,"description":"The guest's name"},{"type":"object","properties":{"type":{"type":"string","const":"multi_select"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type","type":"string","maxLength":200},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"The question the guest reads"},"choices":{"minItems":2,"maxItems":50,"type":"array","items":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":200,"description":"What the guest reads"}},"required":["text"],"additionalProperties":false}}},"required":["type","text","choices"],"additionalProperties":false,"description":"A choice from a list of words"},{"type":"object","properties":{"type":{"type":"string","const":"image_multi_select"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type","type":"string","maxLength":200},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"The question the guest reads"},"choices":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"text":{"type":"string","minLength":1,"maxLength":200,"description":"What the guest reads"},"uploadPath":{"description":"storagePath from create_upload (after PUTting the bytes)","type":"string"},"sourceUrl":{"description":"Public https URL fetched server-side (must not redirect) — alternative to uploadPath","type":"string"}},"required":["text"],"additionalProperties":false,"description":"One picture guests can pick: PNG or JPEG, up to 8 MB, fitted to 800px on its long edge. For uploadPath use create_upload with purpose 'template-choice-image'"}}},"required":["type","text","choices"],"additionalProperties":false,"description":"A choice between pictures"}],"description":"A NEW field of this template, asked of every guest who uses it. The same shape as an AI prompt field (create_ai_prompt)"},"fieldId":{"description":"An EXISTING field: one of the template (fields) or of an AI prompt assigned to it (placeableFields, from get_template)","type":"string","minLength":1,"maxLength":64},"display":{"description":"How the answer is printed: as words, or as the picture the guest chose (image_multi_select only). Default: image for image_multi_select, text otherwise","type":"string","enum":["text","image"]},"preview":{"description":"Free-text field: the sample shown in the design until a guest answers, e.g. a name. Required for a new field; default for an existing one: the field's question","type":"string","minLength":1,"maxLength":200},"previewChoiceId":{"description":"Existing choice field: the choice the design shows. Default: the first choice","type":"string","minLength":1,"maxLength":64},"x":{"description":"Left edge, in template pixels (see sizePixels). Left out: centred horizontally","type":"number"},"y":{"type":"number","description":"Top edge, in template pixels"},"rotation":{"description":"Degrees clockwise, around the top-left corner. Default 0","type":"number","minimum":-360,"maximum":360},"opacity":{"description":"0 to 1. Default 1","type":"number","minimum":0,"maximum":1},"width":{"description":"The box the answer fits in, in template pixels; a long answer is shrunk to fit. Default 40% of the template's width","type":"number","exclusiveMinimum":0},"height":{"description":"Picture display only: the box's height. Default: from the shape of the picture shown. (Text display is one line, as tall as maxFontSize)","type":"number","exclusiveMinimum":0},"maxFontSize":{"description":"Text display: the largest size the answer is set in, in template pixels. Default 72","type":"integer","minimum":1,"maximum":9999},"fontFamily":{"type":"string","minLength":1,"maxLength":100,"description":"A Google Fonts family from list_fonts, e.g. \"Open Sans\". Default Roboto"},"fontWeight":{"type":"string","enum":["100","200","300","400","500","600","700","800","900","normal","bold"],"description":"One of the family's weights from list_fonts: 'normal' (regular), '100'…'900', or 'bold' (700). Default: the family's regular weight"},"fontStyle":{"type":"string","enum":["normal","italic"],"description":"Default normal"},"color":{"description":"The text colour, #rrggbb. Default #000000","type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"textAlign":{"description":"For a text with several lines: how the lines line up. Default left","type":"string","enum":["left","center","right"]},"allCaps":{"description":"Text display: print the answer in capitals. Default false","type":"boolean"},"toIndex":{"description":"Where in the stack the new layer goes: the index it takes (layers[].index, 0 = bottom); the layers from there up move one higher. The background colour stays at index 0, so 1 is just above it. Default: on top of every other layer","type":"integer","minimum":0,"maximum":9007199254740991}},"required":["y"]}}}}}},"/templates/{id}/fields/{fieldId}":{"patch":{"operationId":"update_template_field","summary":"Change a placed dynamic field: its preview, box, typography","description":"Changes only what is sent on the layer that shows the field; the rest stays. The preview is what the design shows until a guest answers: `preview` for a free-text field, `previewChoiceId` for a choice field. A picture layer (display image) takes the box and the choice only. The field itself (its question, its choices) is not changed here.","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"The template (yours, not a public one)"}},{"name":"fieldId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"The field shown, from get_template (layers[].fieldId)"}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"},"photoAreaCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Media-layer count; AI prompts/portraits attach only when this is exactly 1"},"aiCustomPromptIds":{"type":"array","items":{"type":"string"},"description":"Attached AI prompts (see list_ai_prompts)"},"aiPortraitIds":{"type":"array","items":{"type":"string"},"description":"Attached AI portrait styles (see list_ai_portraits)"},"disabledCaptureTypes":{"type":"array","items":{"type":"string"}},"sceneSelectionForced":{"type":"boolean"},"transparentScene":{"type":"boolean","description":"Offers a \"no scene\" option to guests"},"scenes":{"type":"array","items":{"type":"object","properties":{"storagePath":{"type":"string","description":"Key for update_template_scene / remove_template_scene"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"7-day signed URL — re-fetch to refresh"},"width":{"anyOf":[{"type":"number"},{"type":"null"}]},"height":{"anyOf":[{"type":"number"},{"type":"null"}]},"crop":{"anyOf":[{"type":"string","enum":["scene","photo"]},{"type":"null"}]},"compositingMethod":{"anyOf":[{"type":"string","enum":["sourceOver","addition","colorBlend","colorDodgeBlend","differenceBlend","exclusionBlend","lightenBlend","linearDodgeBlend","luminosityBlend","minimumCompositing","overlayBlend","screenBlend","softLightBlend"]},{"type":"null"}]},"uploadedOn":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["storagePath","thumbnailUrl","width","height","crop","compositingMethod","uploadedOn"],"additionalProperties":false},"description":"Green-screen background scenes"},"printable":{"type":"boolean","description":"false = photos with this template are not printed"},"printsShortestSideDoubled":{"type":"boolean","description":"Each print carries the design twice along its short side (2x6 → 2x2x6)"},"imageFilters":{"type":"array","items":{"type":"string"},"description":"The colour filters in the order guests see them; exactly one = forced on every capture; [] = none. May hold an id a newer iPad app added"},"bwCustom":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"highlights":{"type":"number"},"shadows":{"type":"number"},"colourResponse":{"type":"number"}},"required":["exposure","contrast","highlights","shadows","colourResponse"],"additionalProperties":false,"description":"Mono Custom (blackAndWhiteCustom) sliders, as the iPad uses them: the defaults until changed"},"filmStrip":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"grain":{"type":"number"},"warmth":{"type":"number"},"vignette":{"type":"number"},"glow":{"type":"number"},"softness":{"type":"number"}},"required":["exposure","contrast","grain","warmth","vignette","glow","softness"],"additionalProperties":false,"description":"Film Custom (blackAndWhiteFilmStrip) sliders, as the iPad uses them: the defaults until changed"},"glamFilter":{"type":"boolean","description":"The Glam Filter is on"},"glamFilterIntensity":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Its strength, 0 to 1. null = never set: the iPad applies no glam until it is, even with glamFilter on"},"layers":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position in the stack, 0 = bottom"},"kind":{"type":"string","enum":["background","artwork","photoArea","text","field","other"],"description":"background = the base colour; artwork = a picture of the design; photoArea = where a photo goes; text; field = a dynamic element (a guest answer, printed); other = live view or video"},"x":{"type":"number","description":"Left edge, in template pixels (see sizePixels)"},"y":{"type":"number","description":"Top edge, in template pixels"},"width":{"type":"number"},"height":{"type":"number"},"rotation":{"type":"number","description":"Degrees clockwise around the top-left corner"},"opacity":{"type":"number","description":"0 to 1"},"color":{"description":"background: the colour; text and field: the text colour","type":"string"},"name":{"description":"artwork: the picture file","type":"string"},"order":{"description":"photoArea: the order photos are taken in","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"textId":{"description":"text: the key for update_template_text / remove_template_layer","type":"string"},"text":{"type":"string"},"fontFamily":{"type":"string"},"fontWeight":{"type":"string"},"fontStyle":{"type":"string"},"fontSize":{"description":"text: in template pixels; field: the largest size the answer is set in","type":"number"},"textAlign":{"type":"string"},"fieldId":{"description":"field: the dynamic field shown (see fields / placeableFields)","type":"string"},"display":{"description":"field: the answer as words, or as the chosen picture","type":"string","enum":["text","image"]},"preview":{"description":"field, text display of a free-text field: the sample shown until a guest answers","anyOf":[{"type":"string"},{"type":"null"}]},"previewChoiceId":{"description":"field, choice field: the choice the design shows","anyOf":[{"type":"string"},{"type":"null"}]},"allCaps":{"description":"field: the answer is printed in capitals","type":"boolean"},"type":{"description":"other: the stored layer type","type":"string"}},"required":["index","kind","x","y","width","height","rotation","opacity"],"additionalProperties":false},"description":"The design, bottom to top, each layer as a rectangle in template pixels. Photo areas say where guests land; text and field layers are the ones add_template_text / add_template_field make and update_template_text / remove_template_layer change; artwork is a picture of the design (add_template_image / update_template_image). move_template_layer changes what is drawn over what"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}}},"required":["id","type","name","text"],"additionalProperties":false},"description":"The template's own dynamic fields (made by add_template_field). Each is asked of the guest when this template is used, and offered to update_event_survey on events using it"},"placeableFields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}},"owner":{"type":"object","properties":{"kind":{"type":"string","enum":["template","prompt"]},"id":{"type":"string"}},"required":["kind","id"],"additionalProperties":false,"description":"Who the field belongs to: this template, or an AI prompt assigned to it"}},"required":["id","type","name","text","owner"],"additionalProperties":false},"description":"Every field add_template_field can place by fieldId: the own fields, and the fields of the AI prompts assigned to this template (update_template aiCustomPromptIds)"},"fieldId":{"type":"string","description":"The field shown (new or existing), for remove_template_layer"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting","photoAreaCount","aiCustomPromptIds","aiPortraitIds","disabledCaptureTypes","sceneSelectionForced","transparentScene","scenes","printable","printsShortestSideDoubled","imageFilters","bwCustom","filmStrip","glamFilter","glamFilterIntensity","layers","fields","placeableFields","fieldId"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"preview":{"description":"Free-text field: the sample the design shows until a guest answers","type":"string","minLength":1,"maxLength":200},"previewChoiceId":{"description":"Choice field: the choice the design shows","type":"string","minLength":1,"maxLength":64},"x":{"description":"Left edge, in template pixels (see sizePixels). Left out: centred horizontally","type":"number"},"y":{"description":"Top edge, in template pixels","type":"number"},"rotation":{"description":"Degrees clockwise, around the top-left corner. Default 0","type":"number","minimum":-360,"maximum":360},"opacity":{"description":"0 to 1. Default 1","type":"number","minimum":0,"maximum":1},"width":{"description":"The box the answer fits in, in template pixels","type":"number","exclusiveMinimum":0},"height":{"description":"Picture display only: the box's height","type":"number","exclusiveMinimum":0},"maxFontSize":{"description":"Text display: the largest size the answer is set in, in template pixels","type":"integer","minimum":1,"maximum":9999},"fontFamily":{"type":"string","minLength":1,"maxLength":100,"description":"A Google Fonts family from list_fonts, e.g. \"Open Sans\". Default Roboto"},"fontWeight":{"type":"string","enum":["100","200","300","400","500","600","700","800","900","normal","bold"],"description":"One of the family's weights from list_fonts: 'normal' (regular), '100'…'900', or 'bold' (700). Default: the family's regular weight"},"fontStyle":{"type":"string","enum":["normal","italic"],"description":"Default normal"},"color":{"description":"The text colour, #rrggbb. Default #000000","type":"string","pattern":"^#[0-9a-fA-F]{6}$"},"textAlign":{"description":"For a text with several lines: how the lines line up. Default left","type":"string","enum":["left","center","right"]},"allCaps":{"description":"Text display: print the answer in capitals","type":"boolean"}}}}}}}},"/templates/{id}/layers":{"delete":{"operationId":"remove_template_layer","summary":"Take a text layer, a dynamic field or a picture off a template","description":"By textId: that text layer. By fieldId: every layer showing that field on this template; when it was the last layer of one of the template’s own fields, the field itself is deleted too (refused with `conflict` while an event’s survey still asks it). By index: an artwork layer (a picture of the design, e.g. one placed with add_template_image); the layer goes and its picture file stays, as in the dashboard, since copies of the template may show the same file. The indexes of the layers above it shift down by one. Photo areas have their own tool (remove_template_photo_area); the background colour stays.","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"The template (yours, not a public one)"}},{"name":"textId","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","description":"A text layer, from get_template","type":"string","minLength":1},"description":"A text layer, from get_template"},{"name":"fieldId","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","description":"A dynamic field: every layer showing it on this template goes. The last layer of one of the template's own fields deletes the field too","type":"string","minLength":1},"description":"A dynamic field: every layer showing it on this template goes. The last layer of one of the template's own fields deletes the field too"},{"name":"index","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","description":"An artwork layer: layers[].index of kind artwork, from get_template. The layer goes; its picture file stays","type":"integer","minimum":0,"maximum":9007199254740991},"description":"An artwork layer: layers[].index of kind artwork, from get_template. The layer goes; its picture file stays"}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"},"photoAreaCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Media-layer count; AI prompts/portraits attach only when this is exactly 1"},"aiCustomPromptIds":{"type":"array","items":{"type":"string"},"description":"Attached AI prompts (see list_ai_prompts)"},"aiPortraitIds":{"type":"array","items":{"type":"string"},"description":"Attached AI portrait styles (see list_ai_portraits)"},"disabledCaptureTypes":{"type":"array","items":{"type":"string"}},"sceneSelectionForced":{"type":"boolean"},"transparentScene":{"type":"boolean","description":"Offers a \"no scene\" option to guests"},"scenes":{"type":"array","items":{"type":"object","properties":{"storagePath":{"type":"string","description":"Key for update_template_scene / remove_template_scene"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"7-day signed URL — re-fetch to refresh"},"width":{"anyOf":[{"type":"number"},{"type":"null"}]},"height":{"anyOf":[{"type":"number"},{"type":"null"}]},"crop":{"anyOf":[{"type":"string","enum":["scene","photo"]},{"type":"null"}]},"compositingMethod":{"anyOf":[{"type":"string","enum":["sourceOver","addition","colorBlend","colorDodgeBlend","differenceBlend","exclusionBlend","lightenBlend","linearDodgeBlend","luminosityBlend","minimumCompositing","overlayBlend","screenBlend","softLightBlend"]},{"type":"null"}]},"uploadedOn":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["storagePath","thumbnailUrl","width","height","crop","compositingMethod","uploadedOn"],"additionalProperties":false},"description":"Green-screen background scenes"},"printable":{"type":"boolean","description":"false = photos with this template are not printed"},"printsShortestSideDoubled":{"type":"boolean","description":"Each print carries the design twice along its short side (2x6 → 2x2x6)"},"imageFilters":{"type":"array","items":{"type":"string"},"description":"The colour filters in the order guests see them; exactly one = forced on every capture; [] = none. May hold an id a newer iPad app added"},"bwCustom":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"highlights":{"type":"number"},"shadows":{"type":"number"},"colourResponse":{"type":"number"}},"required":["exposure","contrast","highlights","shadows","colourResponse"],"additionalProperties":false,"description":"Mono Custom (blackAndWhiteCustom) sliders, as the iPad uses them: the defaults until changed"},"filmStrip":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"grain":{"type":"number"},"warmth":{"type":"number"},"vignette":{"type":"number"},"glow":{"type":"number"},"softness":{"type":"number"}},"required":["exposure","contrast","grain","warmth","vignette","glow","softness"],"additionalProperties":false,"description":"Film Custom (blackAndWhiteFilmStrip) sliders, as the iPad uses them: the defaults until changed"},"glamFilter":{"type":"boolean","description":"The Glam Filter is on"},"glamFilterIntensity":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Its strength, 0 to 1. null = never set: the iPad applies no glam until it is, even with glamFilter on"},"layers":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position in the stack, 0 = bottom"},"kind":{"type":"string","enum":["background","artwork","photoArea","text","field","other"],"description":"background = the base colour; artwork = a picture of the design; photoArea = where a photo goes; text; field = a dynamic element (a guest answer, printed); other = live view or video"},"x":{"type":"number","description":"Left edge, in template pixels (see sizePixels)"},"y":{"type":"number","description":"Top edge, in template pixels"},"width":{"type":"number"},"height":{"type":"number"},"rotation":{"type":"number","description":"Degrees clockwise around the top-left corner"},"opacity":{"type":"number","description":"0 to 1"},"color":{"description":"background: the colour; text and field: the text colour","type":"string"},"name":{"description":"artwork: the picture file","type":"string"},"order":{"description":"photoArea: the order photos are taken in","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"textId":{"description":"text: the key for update_template_text / remove_template_layer","type":"string"},"text":{"type":"string"},"fontFamily":{"type":"string"},"fontWeight":{"type":"string"},"fontStyle":{"type":"string"},"fontSize":{"description":"text: in template pixels; field: the largest size the answer is set in","type":"number"},"textAlign":{"type":"string"},"fieldId":{"description":"field: the dynamic field shown (see fields / placeableFields)","type":"string"},"display":{"description":"field: the answer as words, or as the chosen picture","type":"string","enum":["text","image"]},"preview":{"description":"field, text display of a free-text field: the sample shown until a guest answers","anyOf":[{"type":"string"},{"type":"null"}]},"previewChoiceId":{"description":"field, choice field: the choice the design shows","anyOf":[{"type":"string"},{"type":"null"}]},"allCaps":{"description":"field: the answer is printed in capitals","type":"boolean"},"type":{"description":"other: the stored layer type","type":"string"}},"required":["index","kind","x","y","width","height","rotation","opacity"],"additionalProperties":false},"description":"The design, bottom to top, each layer as a rectangle in template pixels. Photo areas say where guests land; text and field layers are the ones add_template_text / add_template_field make and update_template_text / remove_template_layer change; artwork is a picture of the design (add_template_image / update_template_image). move_template_layer changes what is drawn over what"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}}},"required":["id","type","name","text"],"additionalProperties":false},"description":"The template's own dynamic fields (made by add_template_field). Each is asked of the guest when this template is used, and offered to update_event_survey on events using it"},"placeableFields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}},"owner":{"type":"object","properties":{"kind":{"type":"string","enum":["template","prompt"]},"id":{"type":"string"}},"required":["kind","id"],"additionalProperties":false,"description":"Who the field belongs to: this template, or an AI prompt assigned to it"}},"required":["id","type","name","text","owner"],"additionalProperties":false},"description":"Every field add_template_field can place by fieldId: the own fields, and the fields of the AI prompts assigned to this template (update_template aiCustomPromptIds)"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting","photoAreaCount","aiCustomPromptIds","aiPortraitIds","disabledCaptureTypes","sceneSelectionForced","transparentScene","scenes","printable","printsShortestSideDoubled","imageFilters","bwCustom","filmStrip","glamFilter","glamFilterIntensity","layers","fields","placeableFields"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/templates/{id}/photo-areas":{"post":{"operationId":"add_template_photo_area","summary":"Add a photo area to a template (where a guest photo goes)","description":"For a design whose import detected no photo area (warning \"no_photo_areas_detected\"), or one more. Position by the top-left corner in template pixels, or leave x out to centre it. The order says which photo of the session it shows. Layers are drawn bottom to top, and a new one goes on top: on a frame design (artwork with a transparent hole for the photo) the photo area must sit BELOW the frame’s artwork, so the guest photo shows through the hole instead of covering the frame. Send toIndex 1 (just above the background colour) to put it there, or move it afterwards with move_template_layer. Refused while AI prompts or portraits are assigned (they need exactly one photo area).","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"The template (yours, not a public one)"}}],"x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"},"photoAreaCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Media-layer count; AI prompts/portraits attach only when this is exactly 1"},"aiCustomPromptIds":{"type":"array","items":{"type":"string"},"description":"Attached AI prompts (see list_ai_prompts)"},"aiPortraitIds":{"type":"array","items":{"type":"string"},"description":"Attached AI portrait styles (see list_ai_portraits)"},"disabledCaptureTypes":{"type":"array","items":{"type":"string"}},"sceneSelectionForced":{"type":"boolean"},"transparentScene":{"type":"boolean","description":"Offers a \"no scene\" option to guests"},"scenes":{"type":"array","items":{"type":"object","properties":{"storagePath":{"type":"string","description":"Key for update_template_scene / remove_template_scene"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"7-day signed URL — re-fetch to refresh"},"width":{"anyOf":[{"type":"number"},{"type":"null"}]},"height":{"anyOf":[{"type":"number"},{"type":"null"}]},"crop":{"anyOf":[{"type":"string","enum":["scene","photo"]},{"type":"null"}]},"compositingMethod":{"anyOf":[{"type":"string","enum":["sourceOver","addition","colorBlend","colorDodgeBlend","differenceBlend","exclusionBlend","lightenBlend","linearDodgeBlend","luminosityBlend","minimumCompositing","overlayBlend","screenBlend","softLightBlend"]},{"type":"null"}]},"uploadedOn":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["storagePath","thumbnailUrl","width","height","crop","compositingMethod","uploadedOn"],"additionalProperties":false},"description":"Green-screen background scenes"},"printable":{"type":"boolean","description":"false = photos with this template are not printed"},"printsShortestSideDoubled":{"type":"boolean","description":"Each print carries the design twice along its short side (2x6 → 2x2x6)"},"imageFilters":{"type":"array","items":{"type":"string"},"description":"The colour filters in the order guests see them; exactly one = forced on every capture; [] = none. May hold an id a newer iPad app added"},"bwCustom":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"highlights":{"type":"number"},"shadows":{"type":"number"},"colourResponse":{"type":"number"}},"required":["exposure","contrast","highlights","shadows","colourResponse"],"additionalProperties":false,"description":"Mono Custom (blackAndWhiteCustom) sliders, as the iPad uses them: the defaults until changed"},"filmStrip":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"grain":{"type":"number"},"warmth":{"type":"number"},"vignette":{"type":"number"},"glow":{"type":"number"},"softness":{"type":"number"}},"required":["exposure","contrast","grain","warmth","vignette","glow","softness"],"additionalProperties":false,"description":"Film Custom (blackAndWhiteFilmStrip) sliders, as the iPad uses them: the defaults until changed"},"glamFilter":{"type":"boolean","description":"The Glam Filter is on"},"glamFilterIntensity":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Its strength, 0 to 1. null = never set: the iPad applies no glam until it is, even with glamFilter on"},"layers":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position in the stack, 0 = bottom"},"kind":{"type":"string","enum":["background","artwork","photoArea","text","field","other"],"description":"background = the base colour; artwork = a picture of the design; photoArea = where a photo goes; text; field = a dynamic element (a guest answer, printed); other = live view or video"},"x":{"type":"number","description":"Left edge, in template pixels (see sizePixels)"},"y":{"type":"number","description":"Top edge, in template pixels"},"width":{"type":"number"},"height":{"type":"number"},"rotation":{"type":"number","description":"Degrees clockwise around the top-left corner"},"opacity":{"type":"number","description":"0 to 1"},"color":{"description":"background: the colour; text and field: the text colour","type":"string"},"name":{"description":"artwork: the picture file","type":"string"},"order":{"description":"photoArea: the order photos are taken in","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"textId":{"description":"text: the key for update_template_text / remove_template_layer","type":"string"},"text":{"type":"string"},"fontFamily":{"type":"string"},"fontWeight":{"type":"string"},"fontStyle":{"type":"string"},"fontSize":{"description":"text: in template pixels; field: the largest size the answer is set in","type":"number"},"textAlign":{"type":"string"},"fieldId":{"description":"field: the dynamic field shown (see fields / placeableFields)","type":"string"},"display":{"description":"field: the answer as words, or as the chosen picture","type":"string","enum":["text","image"]},"preview":{"description":"field, text display of a free-text field: the sample shown until a guest answers","anyOf":[{"type":"string"},{"type":"null"}]},"previewChoiceId":{"description":"field, choice field: the choice the design shows","anyOf":[{"type":"string"},{"type":"null"}]},"allCaps":{"description":"field: the answer is printed in capitals","type":"boolean"},"type":{"description":"other: the stored layer type","type":"string"}},"required":["index","kind","x","y","width","height","rotation","opacity"],"additionalProperties":false},"description":"The design, bottom to top, each layer as a rectangle in template pixels. Photo areas say where guests land; text and field layers are the ones add_template_text / add_template_field make and update_template_text / remove_template_layer change; artwork is a picture of the design (add_template_image / update_template_image). move_template_layer changes what is drawn over what"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}}},"required":["id","type","name","text"],"additionalProperties":false},"description":"The template's own dynamic fields (made by add_template_field). Each is asked of the guest when this template is used, and offered to update_event_survey on events using it"},"placeableFields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}},"owner":{"type":"object","properties":{"kind":{"type":"string","enum":["template","prompt"]},"id":{"type":"string"}},"required":["kind","id"],"additionalProperties":false,"description":"Who the field belongs to: this template, or an AI prompt assigned to it"}},"required":["id","type","name","text","owner"],"additionalProperties":false},"description":"Every field add_template_field can place by fieldId: the own fields, and the fields of the AI prompts assigned to this template (update_template aiCustomPromptIds)"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting","photoAreaCount","aiCustomPromptIds","aiPortraitIds","disabledCaptureTypes","sceneSelectionForced","transparentScene","scenes","printable","printsShortestSideDoubled","imageFilters","bwCustom","filmStrip","glamFilter","glamFilterIntensity","layers","fields","placeableFields"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"x":{"description":"Left edge, in template pixels (see sizePixels). Left out: centred horizontally","type":"number"},"y":{"type":"number","description":"Top edge, in template pixels"},"rotation":{"description":"Degrees clockwise, around the top-left corner. Default 0","type":"number","minimum":-360,"maximum":360},"opacity":{"description":"0 to 1. Default 1","type":"number","minimum":0,"maximum":1},"width":{"description":"In template pixels. Default 300","type":"number","exclusiveMinimum":0},"height":{"description":"In template pixels. Default 300","type":"number","exclusiveMinimum":0},"order":{"description":"Default: one more than the highest on the template","type":"integer","minimum":1,"maximum":99},"toIndex":{"description":"Where in the stack the photo area goes: the index it takes (layers[].index, 0 = bottom); the layers from there up move one higher. Under a frame's artwork, the guest photo shows through the frame's transparent hole: 1 is just above the background colour, below every artwork. Default: on top of every other layer, which covers the artwork under it","type":"integer","minimum":0,"maximum":9007199254740991}},"required":["y"]}}}}}},"/templates/{id}/photo-areas/{index}":{"patch":{"operationId":"update_template_photo_area","summary":"Move, resize, turn or renumber a photo area","description":"For when the import put a photo area where the design did not mean one, or sized it wrong: send the corner, the size, the rotation or the order you want; the rest stays. Index from get_template (layers[].index of a photoArea).","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"The template (yours, not a public one)"}},{"name":"index","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","description":"The layer, from get_template: layers[].index of a photoArea","type":"integer","minimum":0,"maximum":9007199254740991}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"},"photoAreaCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Media-layer count; AI prompts/portraits attach only when this is exactly 1"},"aiCustomPromptIds":{"type":"array","items":{"type":"string"},"description":"Attached AI prompts (see list_ai_prompts)"},"aiPortraitIds":{"type":"array","items":{"type":"string"},"description":"Attached AI portrait styles (see list_ai_portraits)"},"disabledCaptureTypes":{"type":"array","items":{"type":"string"}},"sceneSelectionForced":{"type":"boolean"},"transparentScene":{"type":"boolean","description":"Offers a \"no scene\" option to guests"},"scenes":{"type":"array","items":{"type":"object","properties":{"storagePath":{"type":"string","description":"Key for update_template_scene / remove_template_scene"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"7-day signed URL — re-fetch to refresh"},"width":{"anyOf":[{"type":"number"},{"type":"null"}]},"height":{"anyOf":[{"type":"number"},{"type":"null"}]},"crop":{"anyOf":[{"type":"string","enum":["scene","photo"]},{"type":"null"}]},"compositingMethod":{"anyOf":[{"type":"string","enum":["sourceOver","addition","colorBlend","colorDodgeBlend","differenceBlend","exclusionBlend","lightenBlend","linearDodgeBlend","luminosityBlend","minimumCompositing","overlayBlend","screenBlend","softLightBlend"]},{"type":"null"}]},"uploadedOn":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["storagePath","thumbnailUrl","width","height","crop","compositingMethod","uploadedOn"],"additionalProperties":false},"description":"Green-screen background scenes"},"printable":{"type":"boolean","description":"false = photos with this template are not printed"},"printsShortestSideDoubled":{"type":"boolean","description":"Each print carries the design twice along its short side (2x6 → 2x2x6)"},"imageFilters":{"type":"array","items":{"type":"string"},"description":"The colour filters in the order guests see them; exactly one = forced on every capture; [] = none. May hold an id a newer iPad app added"},"bwCustom":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"highlights":{"type":"number"},"shadows":{"type":"number"},"colourResponse":{"type":"number"}},"required":["exposure","contrast","highlights","shadows","colourResponse"],"additionalProperties":false,"description":"Mono Custom (blackAndWhiteCustom) sliders, as the iPad uses them: the defaults until changed"},"filmStrip":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"grain":{"type":"number"},"warmth":{"type":"number"},"vignette":{"type":"number"},"glow":{"type":"number"},"softness":{"type":"number"}},"required":["exposure","contrast","grain","warmth","vignette","glow","softness"],"additionalProperties":false,"description":"Film Custom (blackAndWhiteFilmStrip) sliders, as the iPad uses them: the defaults until changed"},"glamFilter":{"type":"boolean","description":"The Glam Filter is on"},"glamFilterIntensity":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Its strength, 0 to 1. null = never set: the iPad applies no glam until it is, even with glamFilter on"},"layers":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position in the stack, 0 = bottom"},"kind":{"type":"string","enum":["background","artwork","photoArea","text","field","other"],"description":"background = the base colour; artwork = a picture of the design; photoArea = where a photo goes; text; field = a dynamic element (a guest answer, printed); other = live view or video"},"x":{"type":"number","description":"Left edge, in template pixels (see sizePixels)"},"y":{"type":"number","description":"Top edge, in template pixels"},"width":{"type":"number"},"height":{"type":"number"},"rotation":{"type":"number","description":"Degrees clockwise around the top-left corner"},"opacity":{"type":"number","description":"0 to 1"},"color":{"description":"background: the colour; text and field: the text colour","type":"string"},"name":{"description":"artwork: the picture file","type":"string"},"order":{"description":"photoArea: the order photos are taken in","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"textId":{"description":"text: the key for update_template_text / remove_template_layer","type":"string"},"text":{"type":"string"},"fontFamily":{"type":"string"},"fontWeight":{"type":"string"},"fontStyle":{"type":"string"},"fontSize":{"description":"text: in template pixels; field: the largest size the answer is set in","type":"number"},"textAlign":{"type":"string"},"fieldId":{"description":"field: the dynamic field shown (see fields / placeableFields)","type":"string"},"display":{"description":"field: the answer as words, or as the chosen picture","type":"string","enum":["text","image"]},"preview":{"description":"field, text display of a free-text field: the sample shown until a guest answers","anyOf":[{"type":"string"},{"type":"null"}]},"previewChoiceId":{"description":"field, choice field: the choice the design shows","anyOf":[{"type":"string"},{"type":"null"}]},"allCaps":{"description":"field: the answer is printed in capitals","type":"boolean"},"type":{"description":"other: the stored layer type","type":"string"}},"required":["index","kind","x","y","width","height","rotation","opacity"],"additionalProperties":false},"description":"The design, bottom to top, each layer as a rectangle in template pixels. Photo areas say where guests land; text and field layers are the ones add_template_text / add_template_field make and update_template_text / remove_template_layer change; artwork is a picture of the design (add_template_image / update_template_image). move_template_layer changes what is drawn over what"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}}},"required":["id","type","name","text"],"additionalProperties":false},"description":"The template's own dynamic fields (made by add_template_field). Each is asked of the guest when this template is used, and offered to update_event_survey on events using it"},"placeableFields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}},"owner":{"type":"object","properties":{"kind":{"type":"string","enum":["template","prompt"]},"id":{"type":"string"}},"required":["kind","id"],"additionalProperties":false,"description":"Who the field belongs to: this template, or an AI prompt assigned to it"}},"required":["id","type","name","text","owner"],"additionalProperties":false},"description":"Every field add_template_field can place by fieldId: the own fields, and the fields of the AI prompts assigned to this template (update_template aiCustomPromptIds)"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting","photoAreaCount","aiCustomPromptIds","aiPortraitIds","disabledCaptureTypes","sceneSelectionForced","transparentScene","scenes","printable","printsShortestSideDoubled","imageFilters","bwCustom","filmStrip","glamFilter","glamFilterIntensity","layers","fields","placeableFields"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"x":{"description":"Left edge, in template pixels","type":"number"},"y":{"description":"Top edge, in template pixels","type":"number"},"width":{"type":"number","exclusiveMinimum":0},"height":{"type":"number","exclusiveMinimum":0},"rotation":{"description":"Degrees clockwise, around the top-left corner","type":"number","minimum":-360,"maximum":360},"order":{"type":"integer","minimum":1,"maximum":99,"description":"Which photo of the session goes here (1 = the first). Two areas with the same number show the same photo, as a strip does"}}}}}}},"delete":{"operationId":"remove_template_photo_area","summary":"Remove a photo area from a template","description":"For a photo area the import detected that the design did not mean (a white box in the artwork). Refused while AI prompts or portraits are assigned and this is the only one. Indexes of the layers above it shift down by one.","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"The template (yours, not a public one)"}},{"name":"index","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","description":"The layer, from get_template: layers[].index of a photoArea","type":"integer","minimum":0,"maximum":9007199254740991}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"},"photoAreaCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Media-layer count; AI prompts/portraits attach only when this is exactly 1"},"aiCustomPromptIds":{"type":"array","items":{"type":"string"},"description":"Attached AI prompts (see list_ai_prompts)"},"aiPortraitIds":{"type":"array","items":{"type":"string"},"description":"Attached AI portrait styles (see list_ai_portraits)"},"disabledCaptureTypes":{"type":"array","items":{"type":"string"}},"sceneSelectionForced":{"type":"boolean"},"transparentScene":{"type":"boolean","description":"Offers a \"no scene\" option to guests"},"scenes":{"type":"array","items":{"type":"object","properties":{"storagePath":{"type":"string","description":"Key for update_template_scene / remove_template_scene"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"7-day signed URL — re-fetch to refresh"},"width":{"anyOf":[{"type":"number"},{"type":"null"}]},"height":{"anyOf":[{"type":"number"},{"type":"null"}]},"crop":{"anyOf":[{"type":"string","enum":["scene","photo"]},{"type":"null"}]},"compositingMethod":{"anyOf":[{"type":"string","enum":["sourceOver","addition","colorBlend","colorDodgeBlend","differenceBlend","exclusionBlend","lightenBlend","linearDodgeBlend","luminosityBlend","minimumCompositing","overlayBlend","screenBlend","softLightBlend"]},{"type":"null"}]},"uploadedOn":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["storagePath","thumbnailUrl","width","height","crop","compositingMethod","uploadedOn"],"additionalProperties":false},"description":"Green-screen background scenes"},"printable":{"type":"boolean","description":"false = photos with this template are not printed"},"printsShortestSideDoubled":{"type":"boolean","description":"Each print carries the design twice along its short side (2x6 → 2x2x6)"},"imageFilters":{"type":"array","items":{"type":"string"},"description":"The colour filters in the order guests see them; exactly one = forced on every capture; [] = none. May hold an id a newer iPad app added"},"bwCustom":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"highlights":{"type":"number"},"shadows":{"type":"number"},"colourResponse":{"type":"number"}},"required":["exposure","contrast","highlights","shadows","colourResponse"],"additionalProperties":false,"description":"Mono Custom (blackAndWhiteCustom) sliders, as the iPad uses them: the defaults until changed"},"filmStrip":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"grain":{"type":"number"},"warmth":{"type":"number"},"vignette":{"type":"number"},"glow":{"type":"number"},"softness":{"type":"number"}},"required":["exposure","contrast","grain","warmth","vignette","glow","softness"],"additionalProperties":false,"description":"Film Custom (blackAndWhiteFilmStrip) sliders, as the iPad uses them: the defaults until changed"},"glamFilter":{"type":"boolean","description":"The Glam Filter is on"},"glamFilterIntensity":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Its strength, 0 to 1. null = never set: the iPad applies no glam until it is, even with glamFilter on"},"layers":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position in the stack, 0 = bottom"},"kind":{"type":"string","enum":["background","artwork","photoArea","text","field","other"],"description":"background = the base colour; artwork = a picture of the design; photoArea = where a photo goes; text; field = a dynamic element (a guest answer, printed); other = live view or video"},"x":{"type":"number","description":"Left edge, in template pixels (see sizePixels)"},"y":{"type":"number","description":"Top edge, in template pixels"},"width":{"type":"number"},"height":{"type":"number"},"rotation":{"type":"number","description":"Degrees clockwise around the top-left corner"},"opacity":{"type":"number","description":"0 to 1"},"color":{"description":"background: the colour; text and field: the text colour","type":"string"},"name":{"description":"artwork: the picture file","type":"string"},"order":{"description":"photoArea: the order photos are taken in","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"textId":{"description":"text: the key for update_template_text / remove_template_layer","type":"string"},"text":{"type":"string"},"fontFamily":{"type":"string"},"fontWeight":{"type":"string"},"fontStyle":{"type":"string"},"fontSize":{"description":"text: in template pixels; field: the largest size the answer is set in","type":"number"},"textAlign":{"type":"string"},"fieldId":{"description":"field: the dynamic field shown (see fields / placeableFields)","type":"string"},"display":{"description":"field: the answer as words, or as the chosen picture","type":"string","enum":["text","image"]},"preview":{"description":"field, text display of a free-text field: the sample shown until a guest answers","anyOf":[{"type":"string"},{"type":"null"}]},"previewChoiceId":{"description":"field, choice field: the choice the design shows","anyOf":[{"type":"string"},{"type":"null"}]},"allCaps":{"description":"field: the answer is printed in capitals","type":"boolean"},"type":{"description":"other: the stored layer type","type":"string"}},"required":["index","kind","x","y","width","height","rotation","opacity"],"additionalProperties":false},"description":"The design, bottom to top, each layer as a rectangle in template pixels. Photo areas say where guests land; text and field layers are the ones add_template_text / add_template_field make and update_template_text / remove_template_layer change; artwork is a picture of the design (add_template_image / update_template_image). move_template_layer changes what is drawn over what"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}}},"required":["id","type","name","text"],"additionalProperties":false},"description":"The template's own dynamic fields (made by add_template_field). Each is asked of the guest when this template is used, and offered to update_event_survey on events using it"},"placeableFields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}},"owner":{"type":"object","properties":{"kind":{"type":"string","enum":["template","prompt"]},"id":{"type":"string"}},"required":["kind","id"],"additionalProperties":false,"description":"Who the field belongs to: this template, or an AI prompt assigned to it"}},"required":["id","type","name","text","owner"],"additionalProperties":false},"description":"Every field add_template_field can place by fieldId: the own fields, and the fields of the AI prompts assigned to this template (update_template aiCustomPromptIds)"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting","photoAreaCount","aiCustomPromptIds","aiPortraitIds","disabledCaptureTypes","sceneSelectionForced","transparentScene","scenes","printable","printsShortestSideDoubled","imageFilters","bwCustom","filmStrip","glamFilter","glamFilterIntensity","layers","fields","placeableFields"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/templates/{id}/images":{"post":{"operationId":"add_template_image","summary":"Place a picture on a template (e.g. the client’s logo)","description":"Adds an artwork layer, as the dashboard editor's image upload does: a PNG (its transparency kept) or a JPEG, stored with the template and drawn stretched to its box, so keep the box the picture's shape unless you mean to stretch it. Typical use: the client's logo on a library design. Size and place it in template pixels (sizePixels): send width or height and the other follows the picture's shape; position by the top-left corner, or leave x out to centre it. It goes on top of the design, or at toIndex in the stack (move_template_layer moves it later). The picture may be at most 6000 px on a side (on a longer template, as long as the template, up to 9600 px), 36 megapixels and 8 MB (16 MB on a template longer than 6000 px), and upright (a JPEG with a rotation tag is refused). Provide it via uploadPath (create_upload purpose 'template-artwork') or a public https sourceUrl. update_template_image moves or resizes it; remove_template_layer (index) takes it off. Through MCP the thumbnail comes with the result as an image.","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"The template (yours, not a public one)"}}],"x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"},"photoAreaCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Media-layer count; AI prompts/portraits attach only when this is exactly 1"},"aiCustomPromptIds":{"type":"array","items":{"type":"string"},"description":"Attached AI prompts (see list_ai_prompts)"},"aiPortraitIds":{"type":"array","items":{"type":"string"},"description":"Attached AI portrait styles (see list_ai_portraits)"},"disabledCaptureTypes":{"type":"array","items":{"type":"string"}},"sceneSelectionForced":{"type":"boolean"},"transparentScene":{"type":"boolean","description":"Offers a \"no scene\" option to guests"},"scenes":{"type":"array","items":{"type":"object","properties":{"storagePath":{"type":"string","description":"Key for update_template_scene / remove_template_scene"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"7-day signed URL — re-fetch to refresh"},"width":{"anyOf":[{"type":"number"},{"type":"null"}]},"height":{"anyOf":[{"type":"number"},{"type":"null"}]},"crop":{"anyOf":[{"type":"string","enum":["scene","photo"]},{"type":"null"}]},"compositingMethod":{"anyOf":[{"type":"string","enum":["sourceOver","addition","colorBlend","colorDodgeBlend","differenceBlend","exclusionBlend","lightenBlend","linearDodgeBlend","luminosityBlend","minimumCompositing","overlayBlend","screenBlend","softLightBlend"]},{"type":"null"}]},"uploadedOn":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["storagePath","thumbnailUrl","width","height","crop","compositingMethod","uploadedOn"],"additionalProperties":false},"description":"Green-screen background scenes"},"printable":{"type":"boolean","description":"false = photos with this template are not printed"},"printsShortestSideDoubled":{"type":"boolean","description":"Each print carries the design twice along its short side (2x6 → 2x2x6)"},"imageFilters":{"type":"array","items":{"type":"string"},"description":"The colour filters in the order guests see them; exactly one = forced on every capture; [] = none. May hold an id a newer iPad app added"},"bwCustom":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"highlights":{"type":"number"},"shadows":{"type":"number"},"colourResponse":{"type":"number"}},"required":["exposure","contrast","highlights","shadows","colourResponse"],"additionalProperties":false,"description":"Mono Custom (blackAndWhiteCustom) sliders, as the iPad uses them: the defaults until changed"},"filmStrip":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"grain":{"type":"number"},"warmth":{"type":"number"},"vignette":{"type":"number"},"glow":{"type":"number"},"softness":{"type":"number"}},"required":["exposure","contrast","grain","warmth","vignette","glow","softness"],"additionalProperties":false,"description":"Film Custom (blackAndWhiteFilmStrip) sliders, as the iPad uses them: the defaults until changed"},"glamFilter":{"type":"boolean","description":"The Glam Filter is on"},"glamFilterIntensity":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Its strength, 0 to 1. null = never set: the iPad applies no glam until it is, even with glamFilter on"},"layers":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position in the stack, 0 = bottom"},"kind":{"type":"string","enum":["background","artwork","photoArea","text","field","other"],"description":"background = the base colour; artwork = a picture of the design; photoArea = where a photo goes; text; field = a dynamic element (a guest answer, printed); other = live view or video"},"x":{"type":"number","description":"Left edge, in template pixels (see sizePixels)"},"y":{"type":"number","description":"Top edge, in template pixels"},"width":{"type":"number"},"height":{"type":"number"},"rotation":{"type":"number","description":"Degrees clockwise around the top-left corner"},"opacity":{"type":"number","description":"0 to 1"},"color":{"description":"background: the colour; text and field: the text colour","type":"string"},"name":{"description":"artwork: the picture file","type":"string"},"order":{"description":"photoArea: the order photos are taken in","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"textId":{"description":"text: the key for update_template_text / remove_template_layer","type":"string"},"text":{"type":"string"},"fontFamily":{"type":"string"},"fontWeight":{"type":"string"},"fontStyle":{"type":"string"},"fontSize":{"description":"text: in template pixels; field: the largest size the answer is set in","type":"number"},"textAlign":{"type":"string"},"fieldId":{"description":"field: the dynamic field shown (see fields / placeableFields)","type":"string"},"display":{"description":"field: the answer as words, or as the chosen picture","type":"string","enum":["text","image"]},"preview":{"description":"field, text display of a free-text field: the sample shown until a guest answers","anyOf":[{"type":"string"},{"type":"null"}]},"previewChoiceId":{"description":"field, choice field: the choice the design shows","anyOf":[{"type":"string"},{"type":"null"}]},"allCaps":{"description":"field: the answer is printed in capitals","type":"boolean"},"type":{"description":"other: the stored layer type","type":"string"}},"required":["index","kind","x","y","width","height","rotation","opacity"],"additionalProperties":false},"description":"The design, bottom to top, each layer as a rectangle in template pixels. Photo areas say where guests land; text and field layers are the ones add_template_text / add_template_field make and update_template_text / remove_template_layer change; artwork is a picture of the design (add_template_image / update_template_image). move_template_layer changes what is drawn over what"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}}},"required":["id","type","name","text"],"additionalProperties":false},"description":"The template's own dynamic fields (made by add_template_field). Each is asked of the guest when this template is used, and offered to update_event_survey on events using it"},"placeableFields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}},"owner":{"type":"object","properties":{"kind":{"type":"string","enum":["template","prompt"]},"id":{"type":"string"}},"required":["kind","id"],"additionalProperties":false,"description":"Who the field belongs to: this template, or an AI prompt assigned to it"}},"required":["id","type","name","text","owner"],"additionalProperties":false},"description":"Every field add_template_field can place by fieldId: the own fields, and the fields of the AI prompts assigned to this template (update_template aiCustomPromptIds)"},"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The artwork layer (layers[].index), for update_template_image, move_template_layer and remove_template_layer"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting","photoAreaCount","aiCustomPromptIds","aiPortraitIds","disabledCaptureTypes","sceneSelectionForced","transparentScene","scenes","printable","printsShortestSideDoubled","imageFilters","bwCustom","filmStrip","glamFilter","glamFilterIntensity","layers","fields","placeableFields","index"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"uploadPath":{"description":"storagePath from create_upload with purpose 'template-artwork' (after PUTting the bytes)","type":"string"},"sourceUrl":{"description":"Public https URL fetched server-side (must not redirect) — alternative to uploadPath","type":"string"},"x":{"description":"Left edge, in template pixels (see sizePixels). Left out: centred horizontally","type":"number"},"y":{"type":"number","description":"Top edge, in template pixels"},"rotation":{"description":"Degrees clockwise, around the top-left corner. Default 0","type":"number","minimum":-360,"maximum":360},"opacity":{"description":"0 to 1. Default 1","type":"number","minimum":0,"maximum":1},"width":{"description":"In template pixels. Sent alone, the height follows the picture's shape. Default: the picture's own size in pixels, shrunk to fit the template","type":"number","exclusiveMinimum":0},"height":{"description":"In template pixels. Sent alone, the width follows the picture’s shape; send both to stretch the picture to that box","type":"number","exclusiveMinimum":0},"toIndex":{"description":"Where in the stack the new layer goes: the index it takes (layers[].index, 0 = bottom); the layers from there up move one higher. The background colour stays at index 0, so 1 is just above it. Default: on top of every other layer","type":"integer","minimum":0,"maximum":9007199254740991}},"required":["y"]}}}}}},"/templates/{id}/images/{index}":{"patch":{"operationId":"update_template_image","summary":"Move, resize, turn or fade a picture on a template","description":"Changes only what is sent on an artwork layer (layers[].index of kind artwork, from get_template): one placed with add_template_image or one of the design’s own. The rest stays. Width or height sent alone keeps the shape of the box. The picture itself does not change; to show another, take this one off (remove_template_layer) and add the new one. Position and size in template pixels, as get_template gives them.","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"The template (yours, not a public one)"}},{"name":"index","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","description":"The layer, from get_template: layers[].index of an artwork layer","type":"integer","minimum":0,"maximum":9007199254740991}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"},"photoAreaCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Media-layer count; AI prompts/portraits attach only when this is exactly 1"},"aiCustomPromptIds":{"type":"array","items":{"type":"string"},"description":"Attached AI prompts (see list_ai_prompts)"},"aiPortraitIds":{"type":"array","items":{"type":"string"},"description":"Attached AI portrait styles (see list_ai_portraits)"},"disabledCaptureTypes":{"type":"array","items":{"type":"string"}},"sceneSelectionForced":{"type":"boolean"},"transparentScene":{"type":"boolean","description":"Offers a \"no scene\" option to guests"},"scenes":{"type":"array","items":{"type":"object","properties":{"storagePath":{"type":"string","description":"Key for update_template_scene / remove_template_scene"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"7-day signed URL — re-fetch to refresh"},"width":{"anyOf":[{"type":"number"},{"type":"null"}]},"height":{"anyOf":[{"type":"number"},{"type":"null"}]},"crop":{"anyOf":[{"type":"string","enum":["scene","photo"]},{"type":"null"}]},"compositingMethod":{"anyOf":[{"type":"string","enum":["sourceOver","addition","colorBlend","colorDodgeBlend","differenceBlend","exclusionBlend","lightenBlend","linearDodgeBlend","luminosityBlend","minimumCompositing","overlayBlend","screenBlend","softLightBlend"]},{"type":"null"}]},"uploadedOn":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["storagePath","thumbnailUrl","width","height","crop","compositingMethod","uploadedOn"],"additionalProperties":false},"description":"Green-screen background scenes"},"printable":{"type":"boolean","description":"false = photos with this template are not printed"},"printsShortestSideDoubled":{"type":"boolean","description":"Each print carries the design twice along its short side (2x6 → 2x2x6)"},"imageFilters":{"type":"array","items":{"type":"string"},"description":"The colour filters in the order guests see them; exactly one = forced on every capture; [] = none. May hold an id a newer iPad app added"},"bwCustom":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"highlights":{"type":"number"},"shadows":{"type":"number"},"colourResponse":{"type":"number"}},"required":["exposure","contrast","highlights","shadows","colourResponse"],"additionalProperties":false,"description":"Mono Custom (blackAndWhiteCustom) sliders, as the iPad uses them: the defaults until changed"},"filmStrip":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"grain":{"type":"number"},"warmth":{"type":"number"},"vignette":{"type":"number"},"glow":{"type":"number"},"softness":{"type":"number"}},"required":["exposure","contrast","grain","warmth","vignette","glow","softness"],"additionalProperties":false,"description":"Film Custom (blackAndWhiteFilmStrip) sliders, as the iPad uses them: the defaults until changed"},"glamFilter":{"type":"boolean","description":"The Glam Filter is on"},"glamFilterIntensity":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Its strength, 0 to 1. null = never set: the iPad applies no glam until it is, even with glamFilter on"},"layers":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position in the stack, 0 = bottom"},"kind":{"type":"string","enum":["background","artwork","photoArea","text","field","other"],"description":"background = the base colour; artwork = a picture of the design; photoArea = where a photo goes; text; field = a dynamic element (a guest answer, printed); other = live view or video"},"x":{"type":"number","description":"Left edge, in template pixels (see sizePixels)"},"y":{"type":"number","description":"Top edge, in template pixels"},"width":{"type":"number"},"height":{"type":"number"},"rotation":{"type":"number","description":"Degrees clockwise around the top-left corner"},"opacity":{"type":"number","description":"0 to 1"},"color":{"description":"background: the colour; text and field: the text colour","type":"string"},"name":{"description":"artwork: the picture file","type":"string"},"order":{"description":"photoArea: the order photos are taken in","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"textId":{"description":"text: the key for update_template_text / remove_template_layer","type":"string"},"text":{"type":"string"},"fontFamily":{"type":"string"},"fontWeight":{"type":"string"},"fontStyle":{"type":"string"},"fontSize":{"description":"text: in template pixels; field: the largest size the answer is set in","type":"number"},"textAlign":{"type":"string"},"fieldId":{"description":"field: the dynamic field shown (see fields / placeableFields)","type":"string"},"display":{"description":"field: the answer as words, or as the chosen picture","type":"string","enum":["text","image"]},"preview":{"description":"field, text display of a free-text field: the sample shown until a guest answers","anyOf":[{"type":"string"},{"type":"null"}]},"previewChoiceId":{"description":"field, choice field: the choice the design shows","anyOf":[{"type":"string"},{"type":"null"}]},"allCaps":{"description":"field: the answer is printed in capitals","type":"boolean"},"type":{"description":"other: the stored layer type","type":"string"}},"required":["index","kind","x","y","width","height","rotation","opacity"],"additionalProperties":false},"description":"The design, bottom to top, each layer as a rectangle in template pixels. Photo areas say where guests land; text and field layers are the ones add_template_text / add_template_field make and update_template_text / remove_template_layer change; artwork is a picture of the design (add_template_image / update_template_image). move_template_layer changes what is drawn over what"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}}},"required":["id","type","name","text"],"additionalProperties":false},"description":"The template's own dynamic fields (made by add_template_field). Each is asked of the guest when this template is used, and offered to update_event_survey on events using it"},"placeableFields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}},"owner":{"type":"object","properties":{"kind":{"type":"string","enum":["template","prompt"]},"id":{"type":"string"}},"required":["kind","id"],"additionalProperties":false,"description":"Who the field belongs to: this template, or an AI prompt assigned to it"}},"required":["id","type","name","text","owner"],"additionalProperties":false},"description":"Every field add_template_field can place by fieldId: the own fields, and the fields of the AI prompts assigned to this template (update_template aiCustomPromptIds)"},"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"The artwork layer (layers[].index), for update_template_image, move_template_layer and remove_template_layer"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting","photoAreaCount","aiCustomPromptIds","aiPortraitIds","disabledCaptureTypes","sceneSelectionForced","transparentScene","scenes","printable","printsShortestSideDoubled","imageFilters","bwCustom","filmStrip","glamFilter","glamFilterIntensity","layers","fields","placeableFields","index"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"x":{"description":"Left edge, in template pixels","type":"number"},"y":{"description":"Top edge, in template pixels","type":"number"},"width":{"description":"In template pixels. Sent alone, the height follows: the box keeps its shape","type":"number","exclusiveMinimum":0},"height":{"description":"In template pixels. Sent alone, the width follows: the box keeps its shape","type":"number","exclusiveMinimum":0},"rotation":{"description":"Degrees clockwise, around the top-left corner","type":"number","minimum":-360,"maximum":360},"opacity":{"description":"0 to 1","type":"number","minimum":0,"maximum":1}}}}}}}},"/templates/{id}/layers/{index}":{"patch":{"operationId":"move_template_layer","summary":"Move a layer up or down the stack (what is drawn over what)","description":"Layers are drawn bottom to top, in layers[].index order, as the dashboard editor's Layers list stacks them: any layer moves, the background colour at index 0 excepted (it stays at the bottom). For a frame design — artwork with a transparent hole — the photo area must sit BELOW the frame's artwork, so the guest photo shows through the hole: move the photo area to the back (or to just below the artwork). A photo area on top covers the frame. Moving a photo area changes what is drawn over it, not which photo it shows (its order). The other layers' indexes shift; the result lists them, and `index` is where the moved layer is now. Through MCP the thumbnail comes with the result as an image.","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"The template (yours, not a public one)"}},{"name":"index","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","description":"The layer to move: layers[].index from get_template","type":"integer","minimum":0,"maximum":9007199254740991}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"},"photoAreaCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Media-layer count; AI prompts/portraits attach only when this is exactly 1"},"aiCustomPromptIds":{"type":"array","items":{"type":"string"},"description":"Attached AI prompts (see list_ai_prompts)"},"aiPortraitIds":{"type":"array","items":{"type":"string"},"description":"Attached AI portrait styles (see list_ai_portraits)"},"disabledCaptureTypes":{"type":"array","items":{"type":"string"}},"sceneSelectionForced":{"type":"boolean"},"transparentScene":{"type":"boolean","description":"Offers a \"no scene\" option to guests"},"scenes":{"type":"array","items":{"type":"object","properties":{"storagePath":{"type":"string","description":"Key for update_template_scene / remove_template_scene"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"7-day signed URL — re-fetch to refresh"},"width":{"anyOf":[{"type":"number"},{"type":"null"}]},"height":{"anyOf":[{"type":"number"},{"type":"null"}]},"crop":{"anyOf":[{"type":"string","enum":["scene","photo"]},{"type":"null"}]},"compositingMethod":{"anyOf":[{"type":"string","enum":["sourceOver","addition","colorBlend","colorDodgeBlend","differenceBlend","exclusionBlend","lightenBlend","linearDodgeBlend","luminosityBlend","minimumCompositing","overlayBlend","screenBlend","softLightBlend"]},{"type":"null"}]},"uploadedOn":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["storagePath","thumbnailUrl","width","height","crop","compositingMethod","uploadedOn"],"additionalProperties":false},"description":"Green-screen background scenes"},"printable":{"type":"boolean","description":"false = photos with this template are not printed"},"printsShortestSideDoubled":{"type":"boolean","description":"Each print carries the design twice along its short side (2x6 → 2x2x6)"},"imageFilters":{"type":"array","items":{"type":"string"},"description":"The colour filters in the order guests see them; exactly one = forced on every capture; [] = none. May hold an id a newer iPad app added"},"bwCustom":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"highlights":{"type":"number"},"shadows":{"type":"number"},"colourResponse":{"type":"number"}},"required":["exposure","contrast","highlights","shadows","colourResponse"],"additionalProperties":false,"description":"Mono Custom (blackAndWhiteCustom) sliders, as the iPad uses them: the defaults until changed"},"filmStrip":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"grain":{"type":"number"},"warmth":{"type":"number"},"vignette":{"type":"number"},"glow":{"type":"number"},"softness":{"type":"number"}},"required":["exposure","contrast","grain","warmth","vignette","glow","softness"],"additionalProperties":false,"description":"Film Custom (blackAndWhiteFilmStrip) sliders, as the iPad uses them: the defaults until changed"},"glamFilter":{"type":"boolean","description":"The Glam Filter is on"},"glamFilterIntensity":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Its strength, 0 to 1. null = never set: the iPad applies no glam until it is, even with glamFilter on"},"layers":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position in the stack, 0 = bottom"},"kind":{"type":"string","enum":["background","artwork","photoArea","text","field","other"],"description":"background = the base colour; artwork = a picture of the design; photoArea = where a photo goes; text; field = a dynamic element (a guest answer, printed); other = live view or video"},"x":{"type":"number","description":"Left edge, in template pixels (see sizePixels)"},"y":{"type":"number","description":"Top edge, in template pixels"},"width":{"type":"number"},"height":{"type":"number"},"rotation":{"type":"number","description":"Degrees clockwise around the top-left corner"},"opacity":{"type":"number","description":"0 to 1"},"color":{"description":"background: the colour; text and field: the text colour","type":"string"},"name":{"description":"artwork: the picture file","type":"string"},"order":{"description":"photoArea: the order photos are taken in","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"textId":{"description":"text: the key for update_template_text / remove_template_layer","type":"string"},"text":{"type":"string"},"fontFamily":{"type":"string"},"fontWeight":{"type":"string"},"fontStyle":{"type":"string"},"fontSize":{"description":"text: in template pixels; field: the largest size the answer is set in","type":"number"},"textAlign":{"type":"string"},"fieldId":{"description":"field: the dynamic field shown (see fields / placeableFields)","type":"string"},"display":{"description":"field: the answer as words, or as the chosen picture","type":"string","enum":["text","image"]},"preview":{"description":"field, text display of a free-text field: the sample shown until a guest answers","anyOf":[{"type":"string"},{"type":"null"}]},"previewChoiceId":{"description":"field, choice field: the choice the design shows","anyOf":[{"type":"string"},{"type":"null"}]},"allCaps":{"description":"field: the answer is printed in capitals","type":"boolean"},"type":{"description":"other: the stored layer type","type":"string"}},"required":["index","kind","x","y","width","height","rotation","opacity"],"additionalProperties":false},"description":"The design, bottom to top, each layer as a rectangle in template pixels. Photo areas say where guests land; text and field layers are the ones add_template_text / add_template_field make and update_template_text / remove_template_layer change; artwork is a picture of the design (add_template_image / update_template_image). move_template_layer changes what is drawn over what"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}}},"required":["id","type","name","text"],"additionalProperties":false},"description":"The template's own dynamic fields (made by add_template_field). Each is asked of the guest when this template is used, and offered to update_event_survey on events using it"},"placeableFields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}},"owner":{"type":"object","properties":{"kind":{"type":"string","enum":["template","prompt"]},"id":{"type":"string"}},"required":["kind","id"],"additionalProperties":false,"description":"Who the field belongs to: this template, or an AI prompt assigned to it"}},"required":["id","type","name","text","owner"],"additionalProperties":false},"description":"Every field add_template_field can place by fieldId: the own fields, and the fields of the AI prompts assigned to this template (update_template aiCustomPromptIds)"},"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Where the moved layer is now (layers[].index)"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting","photoAreaCount","aiCustomPromptIds","aiPortraitIds","disabledCaptureTypes","sceneSelectionForced","transparentScene","scenes","printable","printsShortestSideDoubled","imageFilters","bwCustom","filmStrip","glamFilter","glamFilterIntensity","layers","fields","placeableFields","index"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"toIndex":{"description":"The index the layer takes (0 = bottom). The layers between move by one to make room","type":"integer","minimum":0,"maximum":9007199254740991},"to":{"description":"front = on top of every other layer; back = the bottom, just above the background colour","type":"string","enum":["front","back"]}}}}}}}},"/templates/{id}/preview":{"post":{"operationId":"render_template_preview","summary":"Render (or re-render) the picture of a template","description":"Every change made here renders the thumbnail itself; this is for a template whose thumbnailUrl is null (the dashboard renders one only when someone opens its list there) or that was last changed in the dashboard. Up to 800 px on the long edge; photo areas are drawn as numbered boxes, dynamic fields show their previews. It is a working picture, not a mockup: no guest photos are put in the photo areas, so to show a client how their prints will look, place sample photos over the boxes yourself. Through MCP the picture comes with the result as an image.","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"The template (yours, not a public one)"}}],"x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"},"photoAreaCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Media-layer count; AI prompts/portraits attach only when this is exactly 1"},"aiCustomPromptIds":{"type":"array","items":{"type":"string"},"description":"Attached AI prompts (see list_ai_prompts)"},"aiPortraitIds":{"type":"array","items":{"type":"string"},"description":"Attached AI portrait styles (see list_ai_portraits)"},"disabledCaptureTypes":{"type":"array","items":{"type":"string"}},"sceneSelectionForced":{"type":"boolean"},"transparentScene":{"type":"boolean","description":"Offers a \"no scene\" option to guests"},"scenes":{"type":"array","items":{"type":"object","properties":{"storagePath":{"type":"string","description":"Key for update_template_scene / remove_template_scene"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"7-day signed URL — re-fetch to refresh"},"width":{"anyOf":[{"type":"number"},{"type":"null"}]},"height":{"anyOf":[{"type":"number"},{"type":"null"}]},"crop":{"anyOf":[{"type":"string","enum":["scene","photo"]},{"type":"null"}]},"compositingMethod":{"anyOf":[{"type":"string","enum":["sourceOver","addition","colorBlend","colorDodgeBlend","differenceBlend","exclusionBlend","lightenBlend","linearDodgeBlend","luminosityBlend","minimumCompositing","overlayBlend","screenBlend","softLightBlend"]},{"type":"null"}]},"uploadedOn":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["storagePath","thumbnailUrl","width","height","crop","compositingMethod","uploadedOn"],"additionalProperties":false},"description":"Green-screen background scenes"},"printable":{"type":"boolean","description":"false = photos with this template are not printed"},"printsShortestSideDoubled":{"type":"boolean","description":"Each print carries the design twice along its short side (2x6 → 2x2x6)"},"imageFilters":{"type":"array","items":{"type":"string"},"description":"The colour filters in the order guests see them; exactly one = forced on every capture; [] = none. May hold an id a newer iPad app added"},"bwCustom":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"highlights":{"type":"number"},"shadows":{"type":"number"},"colourResponse":{"type":"number"}},"required":["exposure","contrast","highlights","shadows","colourResponse"],"additionalProperties":false,"description":"Mono Custom (blackAndWhiteCustom) sliders, as the iPad uses them: the defaults until changed"},"filmStrip":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"grain":{"type":"number"},"warmth":{"type":"number"},"vignette":{"type":"number"},"glow":{"type":"number"},"softness":{"type":"number"}},"required":["exposure","contrast","grain","warmth","vignette","glow","softness"],"additionalProperties":false,"description":"Film Custom (blackAndWhiteFilmStrip) sliders, as the iPad uses them: the defaults until changed"},"glamFilter":{"type":"boolean","description":"The Glam Filter is on"},"glamFilterIntensity":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Its strength, 0 to 1. null = never set: the iPad applies no glam until it is, even with glamFilter on"},"layers":{"type":"array","items":{"type":"object","properties":{"index":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Position in the stack, 0 = bottom"},"kind":{"type":"string","enum":["background","artwork","photoArea","text","field","other"],"description":"background = the base colour; artwork = a picture of the design; photoArea = where a photo goes; text; field = a dynamic element (a guest answer, printed); other = live view or video"},"x":{"type":"number","description":"Left edge, in template pixels (see sizePixels)"},"y":{"type":"number","description":"Top edge, in template pixels"},"width":{"type":"number"},"height":{"type":"number"},"rotation":{"type":"number","description":"Degrees clockwise around the top-left corner"},"opacity":{"type":"number","description":"0 to 1"},"color":{"description":"background: the colour; text and field: the text colour","type":"string"},"name":{"description":"artwork: the picture file","type":"string"},"order":{"description":"photoArea: the order photos are taken in","type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"textId":{"description":"text: the key for update_template_text / remove_template_layer","type":"string"},"text":{"type":"string"},"fontFamily":{"type":"string"},"fontWeight":{"type":"string"},"fontStyle":{"type":"string"},"fontSize":{"description":"text: in template pixels; field: the largest size the answer is set in","type":"number"},"textAlign":{"type":"string"},"fieldId":{"description":"field: the dynamic field shown (see fields / placeableFields)","type":"string"},"display":{"description":"field: the answer as words, or as the chosen picture","type":"string","enum":["text","image"]},"preview":{"description":"field, text display of a free-text field: the sample shown until a guest answers","anyOf":[{"type":"string"},{"type":"null"}]},"previewChoiceId":{"description":"field, choice field: the choice the design shows","anyOf":[{"type":"string"},{"type":"null"}]},"allCaps":{"description":"field: the answer is printed in capitals","type":"boolean"},"type":{"description":"other: the stored layer type","type":"string"}},"required":["index","kind","x","y","width","height","rotation","opacity"],"additionalProperties":false},"description":"The design, bottom to top, each layer as a rectangle in template pixels. Photo areas say where guests land; text and field layers are the ones add_template_text / add_template_field make and update_template_text / remove_template_layer change; artwork is a picture of the design (add_template_image / update_template_image). move_template_layer changes what is drawn over what"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}}},"required":["id","type","name","text"],"additionalProperties":false},"description":"The template's own dynamic fields (made by add_template_field). Each is asked of the guest when this template is used, and offered to update_event_survey on events using it"},"placeableFields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}},"owner":{"type":"object","properties":{"kind":{"type":"string","enum":["template","prompt"]},"id":{"type":"string"}},"required":["kind","id"],"additionalProperties":false,"description":"Who the field belongs to: this template, or an AI prompt assigned to it"}},"required":["id","type","name","text","owner"],"additionalProperties":false},"description":"Every field add_template_field can place by fieldId: the own fields, and the fields of the AI prompts assigned to this template (update_template aiCustomPromptIds)"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting","photoAreaCount","aiCustomPromptIds","aiPortraitIds","disabledCaptureTypes","sceneSelectionForced","transparentScene","scenes","printable","printsShortestSideDoubled","imageFilters","bwCustom","filmStrip","glamFilter","glamFilterIntensity","layers","fields","placeableFields"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/fonts":{"get":{"operationId":"list_fonts","summary":"The fonts template text can be set in (Google Fonts)","description":"Without `search`: the nine families the dashboard offers first (Open Sans, Montserrat, Oswald, Raleway, Poppins, Lato, Nunito, Pacifico, Roboto). With `search`: every Google Fonts family whose name contains it, most popular first — the same catalogue the dashboard editor picks from. Use `family` as fontFamily and one of `weights` (or `italicWeights` with fontStyle \"italic\") as fontWeight in add_template_text, update_template_text and add_template_field.","parameters":[{"name":"search","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","description":"Part of a family name, e.g. \"sans\", \"script\" (matches \"Dancing Script\")","type":"string","minLength":1,"maxLength":60},"description":"Part of a family name, e.g. \"sans\", \"script\" (matches \"Dancing Script\")"},{"name":"limit","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","default":25,"type":"integer","minimum":1,"maximum":100}}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"family":{"type":"string","description":"Send this, exactly, as fontFamily"},"category":{"type":"string","description":"serif, sans-serif, display, handwriting or monospace, as Google Fonts files it"},"weights":{"type":"array","items":{"type":"string"},"description":"The weights of the upright face: 'normal' (regular) and/or '100'…'900'"},"italicWeights":{"type":"array","items":{"type":"string"},"description":"The weights of the italic face; empty when the family has no italic"}},"required":["family","category","weights","italicWeights"],"additionalProperties":false}},"hasMore":{"type":"boolean","description":"true = more families match; narrow the search or raise limit"}},"required":["data","hasMore"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/templates/{id}/scenes":{"post":{"operationId":"add_template_scene","summary":"Add a green-screen background scene to a template","description":"Guests captured in front of a green screen get composited onto the scene. Provide the image via uploadPath (create_upload, purpose \"scene\") or a public https sourceUrl. A 400px thumbnail is rendered automatically. A template holds at most 20 scenes; one more is refused with validation_failed.","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"Template id — use list_templates to find one"}}],"x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"},"photoAreaCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Media-layer count; AI prompts/portraits attach only when this is exactly 1"},"aiCustomPromptIds":{"type":"array","items":{"type":"string"},"description":"Attached AI prompts (see list_ai_prompts)"},"aiPortraitIds":{"type":"array","items":{"type":"string"},"description":"Attached AI portrait styles (see list_ai_portraits)"},"disabledCaptureTypes":{"type":"array","items":{"type":"string"}},"sceneSelectionForced":{"type":"boolean"},"transparentScene":{"type":"boolean","description":"Offers a \"no scene\" option to guests"},"scenes":{"type":"array","items":{"type":"object","properties":{"storagePath":{"type":"string","description":"Key for update_template_scene / remove_template_scene"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"7-day signed URL — re-fetch to refresh"},"width":{"anyOf":[{"type":"number"},{"type":"null"}]},"height":{"anyOf":[{"type":"number"},{"type":"null"}]},"crop":{"anyOf":[{"type":"string","enum":["scene","photo"]},{"type":"null"}]},"compositingMethod":{"anyOf":[{"type":"string","enum":["sourceOver","addition","colorBlend","colorDodgeBlend","differenceBlend","exclusionBlend","lightenBlend","linearDodgeBlend","luminosityBlend","minimumCompositing","overlayBlend","screenBlend","softLightBlend"]},{"type":"null"}]},"uploadedOn":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["storagePath","thumbnailUrl","width","height","crop","compositingMethod","uploadedOn"],"additionalProperties":false},"description":"Green-screen background scenes"},"printable":{"type":"boolean","description":"false = photos with this template are not printed"},"printsShortestSideDoubled":{"type":"boolean","description":"Each print carries the design twice along its short side (2x6 → 2x2x6)"},"imageFilters":{"type":"array","items":{"type":"string"},"description":"The colour filters in the order guests see them; exactly one = forced on every capture; [] = none. May hold an id a newer iPad app added"},"bwCustom":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"highlights":{"type":"number"},"shadows":{"type":"number"},"colourResponse":{"type":"number"}},"required":["exposure","contrast","highlights","shadows","colourResponse"],"additionalProperties":false,"description":"Mono Custom (blackAndWhiteCustom) sliders, as the iPad uses them: the defaults until changed"},"filmStrip":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"grain":{"type":"number"},"warmth":{"type":"number"},"vignette":{"type":"number"},"glow":{"type":"number"},"softness":{"type":"number"}},"required":["exposure","contrast","grain","warmth","vignette","glow","softness"],"additionalProperties":false,"description":"Film Custom (blackAndWhiteFilmStrip) sliders, as the iPad uses them: the defaults until changed"},"glamFilter":{"type":"boolean","description":"The Glam Filter is on"},"glamFilterIntensity":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Its strength, 0 to 1. null = never set: the iPad applies no glam until it is, even with glamFilter on"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting","photoAreaCount","aiCustomPromptIds","aiPortraitIds","disabledCaptureTypes","sceneSelectionForced","transparentScene","scenes","printable","printsShortestSideDoubled","imageFilters","bwCustom","filmStrip","glamFilter","glamFilterIntensity"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"uploadPath":{"description":"storagePath from create_upload (after PUTting the bytes)","type":"string"},"sourceUrl":{"description":"Public https URL fetched server-side (must not redirect) — alternative to uploadPath","type":"string"},"crop":{"type":"string","enum":["scene","photo"]},"compositingMethod":{"type":"string","enum":["sourceOver","addition","colorBlend","colorDodgeBlend","differenceBlend","exclusionBlend","lightenBlend","linearDodgeBlend","luminosityBlend","minimumCompositing","overlayBlend","screenBlend","softLightBlend"]}}}}}}},"patch":{"operationId":"update_template_scene","summary":"Update a scene's crop or compositing method","description":"Scenes are keyed by storagePath (from the template DTO scenes[]). compositingMethod: null resets to the default (sourceOver).","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"Template id — use list_templates to find one"}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"},"photoAreaCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Media-layer count; AI prompts/portraits attach only when this is exactly 1"},"aiCustomPromptIds":{"type":"array","items":{"type":"string"},"description":"Attached AI prompts (see list_ai_prompts)"},"aiPortraitIds":{"type":"array","items":{"type":"string"},"description":"Attached AI portrait styles (see list_ai_portraits)"},"disabledCaptureTypes":{"type":"array","items":{"type":"string"}},"sceneSelectionForced":{"type":"boolean"},"transparentScene":{"type":"boolean","description":"Offers a \"no scene\" option to guests"},"scenes":{"type":"array","items":{"type":"object","properties":{"storagePath":{"type":"string","description":"Key for update_template_scene / remove_template_scene"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"7-day signed URL — re-fetch to refresh"},"width":{"anyOf":[{"type":"number"},{"type":"null"}]},"height":{"anyOf":[{"type":"number"},{"type":"null"}]},"crop":{"anyOf":[{"type":"string","enum":["scene","photo"]},{"type":"null"}]},"compositingMethod":{"anyOf":[{"type":"string","enum":["sourceOver","addition","colorBlend","colorDodgeBlend","differenceBlend","exclusionBlend","lightenBlend","linearDodgeBlend","luminosityBlend","minimumCompositing","overlayBlend","screenBlend","softLightBlend"]},{"type":"null"}]},"uploadedOn":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["storagePath","thumbnailUrl","width","height","crop","compositingMethod","uploadedOn"],"additionalProperties":false},"description":"Green-screen background scenes"},"printable":{"type":"boolean","description":"false = photos with this template are not printed"},"printsShortestSideDoubled":{"type":"boolean","description":"Each print carries the design twice along its short side (2x6 → 2x2x6)"},"imageFilters":{"type":"array","items":{"type":"string"},"description":"The colour filters in the order guests see them; exactly one = forced on every capture; [] = none. May hold an id a newer iPad app added"},"bwCustom":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"highlights":{"type":"number"},"shadows":{"type":"number"},"colourResponse":{"type":"number"}},"required":["exposure","contrast","highlights","shadows","colourResponse"],"additionalProperties":false,"description":"Mono Custom (blackAndWhiteCustom) sliders, as the iPad uses them: the defaults until changed"},"filmStrip":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"grain":{"type":"number"},"warmth":{"type":"number"},"vignette":{"type":"number"},"glow":{"type":"number"},"softness":{"type":"number"}},"required":["exposure","contrast","grain","warmth","vignette","glow","softness"],"additionalProperties":false,"description":"Film Custom (blackAndWhiteFilmStrip) sliders, as the iPad uses them: the defaults until changed"},"glamFilter":{"type":"boolean","description":"The Glam Filter is on"},"glamFilterIntensity":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Its strength, 0 to 1. null = never set: the iPad applies no glam until it is, even with glamFilter on"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting","photoAreaCount","aiCustomPromptIds","aiPortraitIds","disabledCaptureTypes","sceneSelectionForced","transparentScene","scenes","printable","printsShortestSideDoubled","imageFilters","bwCustom","filmStrip","glamFilter","glamFilterIntensity"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"storagePath":{"type":"string","minLength":1,"description":"Scene key from the template's scenes[] (get_template) — not a file path you choose"},"crop":{"type":"string","enum":["scene","photo"]},"compositingMethod":{"anyOf":[{"type":"string","enum":["sourceOver","addition","colorBlend","colorDodgeBlend","differenceBlend","exclusionBlend","lightenBlend","linearDodgeBlend","luminosityBlend","minimumCompositing","overlayBlend","screenBlend","softLightBlend"]},{"type":"null"}]}},"required":["storagePath"]}}}}},"delete":{"operationId":"remove_template_scene","summary":"Remove a scene from a template (deletes its image files)","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"Template id — use list_templates to find one"}},{"name":"storagePath","in":"query","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"From the template DTO scenes[].storagePath"},"description":"From the template DTO scenes[].storagePath"}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean","description":"Public library items are duplicable but not editable"},"sizePixels":{"anyOf":[{"type":"array","items":{"type":"number"}},{"type":"null"}],"description":"The size it was designed at, [width, height]. For an attract screen this is the iPad screen it is meant for, in screen points: e.g. [1032, 1376] for a 13-inch iPad Pro mounted portrait, [1376, 1032] landscape. Pick one that matches how the iPad is mounted"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A picture of the design, up to 800 px on its long edge: a stable URL when available (never expires), otherwise a 7-day signed URL — refresh by re-listing. null = not rendered yet (a template: render_template_preview renders it; every change made through this API renders it too)"},"isImporting":{"type":"boolean","description":"true = still importing; sizePixels/layers/thumbnail are placeholders, poll until false"},"photoAreaCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Media-layer count; AI prompts/portraits attach only when this is exactly 1"},"aiCustomPromptIds":{"type":"array","items":{"type":"string"},"description":"Attached AI prompts (see list_ai_prompts)"},"aiPortraitIds":{"type":"array","items":{"type":"string"},"description":"Attached AI portrait styles (see list_ai_portraits)"},"disabledCaptureTypes":{"type":"array","items":{"type":"string"}},"sceneSelectionForced":{"type":"boolean"},"transparentScene":{"type":"boolean","description":"Offers a \"no scene\" option to guests"},"scenes":{"type":"array","items":{"type":"object","properties":{"storagePath":{"type":"string","description":"Key for update_template_scene / remove_template_scene"},"thumbnailUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"7-day signed URL — re-fetch to refresh"},"width":{"anyOf":[{"type":"number"},{"type":"null"}]},"height":{"anyOf":[{"type":"number"},{"type":"null"}]},"crop":{"anyOf":[{"type":"string","enum":["scene","photo"]},{"type":"null"}]},"compositingMethod":{"anyOf":[{"type":"string","enum":["sourceOver","addition","colorBlend","colorDodgeBlend","differenceBlend","exclusionBlend","lightenBlend","linearDodgeBlend","luminosityBlend","minimumCompositing","overlayBlend","screenBlend","softLightBlend"]},{"type":"null"}]},"uploadedOn":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["storagePath","thumbnailUrl","width","height","crop","compositingMethod","uploadedOn"],"additionalProperties":false},"description":"Green-screen background scenes"},"printable":{"type":"boolean","description":"false = photos with this template are not printed"},"printsShortestSideDoubled":{"type":"boolean","description":"Each print carries the design twice along its short side (2x6 → 2x2x6)"},"imageFilters":{"type":"array","items":{"type":"string"},"description":"The colour filters in the order guests see them; exactly one = forced on every capture; [] = none. May hold an id a newer iPad app added"},"bwCustom":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"highlights":{"type":"number"},"shadows":{"type":"number"},"colourResponse":{"type":"number"}},"required":["exposure","contrast","highlights","shadows","colourResponse"],"additionalProperties":false,"description":"Mono Custom (blackAndWhiteCustom) sliders, as the iPad uses them: the defaults until changed"},"filmStrip":{"type":"object","properties":{"exposure":{"type":"number"},"contrast":{"type":"number"},"grain":{"type":"number"},"warmth":{"type":"number"},"vignette":{"type":"number"},"glow":{"type":"number"},"softness":{"type":"number"}},"required":["exposure","contrast","grain","warmth","vignette","glow","softness"],"additionalProperties":false,"description":"Film Custom (blackAndWhiteFilmStrip) sliders, as the iPad uses them: the defaults until changed"},"glamFilter":{"type":"boolean","description":"The Glam Filter is on"},"glamFilterIntensity":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Its strength, 0 to 1. null = never set: the iPad applies no glam until it is, even with glamFilter on"}},"required":["id","name","createdDate","modifiedDate","isPublic","sizePixels","thumbnailUrl","isImporting","photoAreaCount","aiCustomPromptIds","aiPortraitIds","disabledCaptureTypes","sceneSelectionForced","transparentScene","scenes","printable","printsShortestSideDoubled","imageFilters","bwCustom","filmStrip","glamFilter","glamFilterIntensity"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/ai-prompts":{"get":{"operationId":"list_ai_prompts","summary":"List AI photo prompts (the catalog guests can pick from)","description":"Own prompts plus the public/default catalog, each with a browser-fetchable previewImageUrl (7-day signed — re-list to refresh; never store long-term). Sibling of list_ai_portraits.","parameters":[{"name":"limit","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","default":25,"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string"}},{"name":"includePublic","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","description":"false = only prompts private to this account","type":"boolean"},"description":"false = only prompts private to this account"}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean"},"previewImageUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The picture guests pick the prompt by, at the booth (7-day signed URL — refresh by re-listing). null = none yet, and the iPad does NOT offer the prompt to guests until one is set: test_ai_prompt with useAsPreview, or set_ai_prompt_preview_image"},"model":{"anyOf":[{"type":"string"},{"type":"null"}]},"disabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"rerollsAllowed":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Rerolls guests get; null = the iPad device default"},"serverProcessDesired":{"type":"boolean","description":"true = guests do not wait: the result is made on the server and sent to the email address or phone number the guest gives (the iPad asks for one), and re-rolls do not apply. false = guests wait at the iPad and see the result there. In Capture Station mode the setting does not apply. The dashboard switches this on when it picks the slowest model (Nano Banana Pro 4K); this API changes it only when you send it"},"promptText":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A plain prompt: its text (first variant). null when the prompt has a body of pieces (hasPromptDoc) — read and write `body` instead."},"hasPromptDoc":{"type":"boolean","description":"true = the prompt has a body of pieces (`body`), published from the dashboard prompt editor or through this API; false = a plain prompt (`promptText`)"},"body":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"text"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false,"description":"Prompt text, used as written"},{"type":"object","properties":{"kind":{"type":"string","const":"fieldRef"},"fieldId":{"type":"string"},"usageId":{"type":"string","description":"The id of this one use of the field"},"role":{"type":"string","enum":["image","text"],"description":"'image' = the use of an image choice that sends the picked picture to the image model (its first use); 'text' = the answer as words"},"phrasing":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"},"description":"For a choice field: the words each choice puts into the prompt, by choice id"}},"required":["kind","fieldId","usageId","role","phrasing"],"additionalProperties":false,"description":"The guest's answer to a field, placed in the prompt"}]},"description":"The prompt as an ordered list of pieces: text, and the guest's answers to `fields`. For a plain prompt, its text as one piece"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}}},"required":["id","type","name","text"],"additionalProperties":false},"description":"The questions a guest answers before a generation, in the order they are asked"},"versionNumber":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"How many times the prompt has been published; null = never"},"publishedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When it was last published; null = never"},"referenceImages":{"type":"array","items":{"type":"object","properties":{"storagePath":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass to remove_ai_prompt_reference_image. null on PUBLIC catalog prompts: the path is namespaced by the account that created them, so returning it would disclose another account's uid and our storage layout — and they cannot be edited anyway."},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Browser-fetchable (7-day signed URL — re-fetch to refresh); null when the picture could not be linked"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A short phrase naming what the image shows (e.g. \"a red baseball cap\"), written automatically when the image is added — the dashboard assistant refers to the image by it. null = not described."}},"required":["storagePath","url","description"],"additionalProperties":false},"description":"Style reference images sent with every generation"},"hasReferenceImages":{"type":"boolean"}},"required":["id","name","createdDate","modifiedDate","isPublic","previewImageUrl","model","disabled","rerollsAllowed","serverProcessDesired","promptText","hasPromptDoc","body","fields","versionNumber","publishedAt","referenceImages","hasReferenceImages"],"additionalProperties":false}},"hasMore":{"type":"boolean"},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["data","hasMore","nextCursor"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}},"post":{"operationId":"create_ai_prompt","summary":"Create an AI photo prompt","description":"A prompt sent to the image model with each guest photo. Either plain — `promptText` — or with questions the guest answers first: `fields` (free text, a choice from a list, a choice between pictures) and `body`, the prompt as pieces of text with the answers in place. Give a new field a `key` and point the body at it with `fieldKey`; the response returns the real ids. Creating costs nothing — generation consumes AI credits (see get_ai_credits). Attach style reference images with add_ai_prompt_reference_image. Then test_ai_prompt with useAsPreview: it shows what the prompt makes and gives the prompt the preview image guests pick it by, without which the iPad does not offer it. Choice pictures sent without imageDescription are described automatically. A picture is described once: the same image sent again reuses its description. An account gets 200 new descriptions a day (UTC); past that, pictures are stored without one and the response carries warnings: ['imageDescriptionSkipped'].","x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean"},"previewImageUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The picture guests pick the prompt by, at the booth (7-day signed URL — refresh by re-listing). null = none yet, and the iPad does NOT offer the prompt to guests until one is set: test_ai_prompt with useAsPreview, or set_ai_prompt_preview_image"},"model":{"anyOf":[{"type":"string"},{"type":"null"}]},"disabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"rerollsAllowed":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Rerolls guests get; null = the iPad device default"},"serverProcessDesired":{"type":"boolean","description":"true = guests do not wait: the result is made on the server and sent to the email address or phone number the guest gives (the iPad asks for one), and re-rolls do not apply. false = guests wait at the iPad and see the result there. In Capture Station mode the setting does not apply. The dashboard switches this on when it picks the slowest model (Nano Banana Pro 4K); this API changes it only when you send it"},"promptText":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A plain prompt: its text (first variant). null when the prompt has a body of pieces (hasPromptDoc) — read and write `body` instead."},"hasPromptDoc":{"type":"boolean","description":"true = the prompt has a body of pieces (`body`), published from the dashboard prompt editor or through this API; false = a plain prompt (`promptText`)"},"body":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"text"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false,"description":"Prompt text, used as written"},{"type":"object","properties":{"kind":{"type":"string","const":"fieldRef"},"fieldId":{"type":"string"},"usageId":{"type":"string","description":"The id of this one use of the field"},"role":{"type":"string","enum":["image","text"],"description":"'image' = the use of an image choice that sends the picked picture to the image model (its first use); 'text' = the answer as words"},"phrasing":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"},"description":"For a choice field: the words each choice puts into the prompt, by choice id"}},"required":["kind","fieldId","usageId","role","phrasing"],"additionalProperties":false,"description":"The guest's answer to a field, placed in the prompt"}]},"description":"The prompt as an ordered list of pieces: text, and the guest's answers to `fields`. For a plain prompt, its text as one piece"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}}},"required":["id","type","name","text"],"additionalProperties":false},"description":"The questions a guest answers before a generation, in the order they are asked"},"versionNumber":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"How many times the prompt has been published; null = never"},"publishedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When it was last published; null = never"},"referenceImages":{"type":"array","items":{"type":"object","properties":{"storagePath":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass to remove_ai_prompt_reference_image. null on PUBLIC catalog prompts: the path is namespaced by the account that created them, so returning it would disclose another account's uid and our storage layout — and they cannot be edited anyway."},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Browser-fetchable (7-day signed URL — re-fetch to refresh); null when the picture could not be linked"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A short phrase naming what the image shows (e.g. \"a red baseball cap\"), written automatically when the image is added — the dashboard assistant refers to the image by it. null = not described."}},"required":["storagePath","url","description"],"additionalProperties":false},"description":"Style reference images sent with every generation"},"hasReferenceImages":{"type":"boolean"},"warnings":{"description":"Non-blocking. 'imageDescriptionSkipped': a new picture was stored without its automatic description, because the account has used its 200 descriptions for today (UTC days). The picture works the same; a choice picture can be given one with imageDescription.","type":"array","items":{"type":"string","const":"imageDescriptionSkipped"}}},"required":["id","name","createdDate","modifiedDate","isPublic","previewImageUrl","model","disabled","rerollsAllowed","serverProcessDesired","promptText","hasPromptDoc","body","fields","versionNumber","publishedAt","referenceImages","hasReferenceImages"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"promptText":{"description":"A plain prompt: sent to the image model verbatim. Or send `body`","type":"string","minLength":1,"maxLength":5000},"body":{"minItems":1,"maxItems":200,"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"text"},"value":{"type":"string","maxLength":50000}},"required":["kind","value"],"additionalProperties":false,"description":"Prompt text, used as written. A line break starts a new paragraph"},{"type":"object","properties":{"kind":{"type":"string","const":"fieldRef"},"fieldId":{"description":"The id of one of the prompt fields","type":"string","minLength":1,"maxLength":64},"fieldKey":{"description":"Or the `key` of a field in this same call","type":"string","pattern":"^[A-Za-z0-9_-]{1,40}$"},"usageId":{"description":"Send back the one you read. Left out, the use keeps its id by position","type":"string","minLength":1,"maxLength":64},"role":{"description":"Returned on read. Set for you: the first use of an image choice is 'image'","type":"string","enum":["image","text"]},"phrasing":{"description":"For a choice field: the words each choice puts into the prompt, by choice id (or the `key` of a choice in this call). A choice left out puts in its own text. Entries for choices the field does not have are ignored","type":"object","propertyNames":{"type":"string","minLength":1,"maxLength":64},"additionalProperties":{"type":"string","maxLength":10000}}},"required":["kind"],"additionalProperties":false,"description":"The guest's answer to a field, placed in the prompt"}]},"description":"The prompt as an ordered list of pieces: text, and answers to fields. REPLACES the body"},"fields":{"maxItems":20,"type":"array","items":{"anyOf":[{"oneOf":[{"type":"object","properties":{"id":{"description":"The field's id, to keep or change it. Leave out to create a field","type":"string","minLength":1,"maxLength":64},"key":{"description":"A name of your own for this field, so `body` can point at it in the same call (`fieldKey`). Not stored","type":"string","pattern":"^[A-Za-z0-9_-]{1,40}$"},"type":{"type":"string","const":"string"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (custom, full name, choice)","type":"string","maxLength":200},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"The question the guest reads"}},"required":["type","text"],"additionalProperties":false,"description":"A line of free text the guest types"},{"type":"object","properties":{"id":{"description":"The field's id, to keep or change it. Leave out to create a field","type":"string","minLength":1,"maxLength":64},"key":{"description":"A name of your own for this field, so `body` can point at it in the same call (`fieldKey`). Not stored","type":"string","pattern":"^[A-Za-z0-9_-]{1,40}$"},"type":{"type":"string","const":"full_name"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (custom, full name, choice)","type":"string","maxLength":200},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"The question the guest reads"}},"required":["type","text"],"additionalProperties":false,"description":"The guest's name"},{"type":"object","properties":{"id":{"description":"The field's id, to keep or change it. Leave out to create a field","type":"string","minLength":1,"maxLength":64},"key":{"description":"A name of your own for this field, so `body` can point at it in the same call (`fieldKey`). Not stored","type":"string","pattern":"^[A-Za-z0-9_-]{1,40}$"},"type":{"type":"string","const":"multi_select"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (custom, full name, choice)","type":"string","maxLength":200},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"The question the guest reads"},"choices":{"minItems":2,"maxItems":50,"type":"array","items":{"type":"object","properties":{"id":{"description":"The choice's id, to keep or change it. Leave out to add a choice","type":"string","minLength":1,"maxLength":64},"key":{"description":"A name of your own for this choice, so `phrasing` can point at it in the same call. Not stored","type":"string","pattern":"^[A-Za-z0-9_-]{1,40}$"},"text":{"type":"string","minLength":1,"maxLength":200,"description":"What the guest reads"}},"required":["text"],"additionalProperties":false}}},"required":["type","text","choices"],"additionalProperties":false,"description":"A choice from a list of words. What each choice puts into the prompt is `phrasing`"},{"type":"object","properties":{"id":{"description":"The field's id, to keep or change it. Leave out to create a field","type":"string","minLength":1,"maxLength":64},"key":{"description":"A name of your own for this field, so `body` can point at it in the same call (`fieldKey`). Not stored","type":"string","pattern":"^[A-Za-z0-9_-]{1,40}$"},"type":{"type":"string","const":"image_multi_select"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (custom, full name, choice)","type":"string","maxLength":200},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"The question the guest reads"},"choices":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"id":{"description":"The choice's id, to keep or change it. Leave out to add a choice","type":"string","minLength":1,"maxLength":64},"key":{"description":"A name of your own for this choice, so `phrasing` can point at it in the same call. Not stored","type":"string","pattern":"^[A-Za-z0-9_-]{1,40}$"},"text":{"type":"string","minLength":1,"maxLength":200,"description":"What the guest reads"},"uploadPath":{"description":"storagePath from create_upload (after PUTting the bytes)","type":"string"},"sourceUrl":{"description":"Public https URL fetched server-side (must not redirect) — alternative to uploadPath","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows, e.g. \"a red baseball cap\". Written automatically for a new picture when left out","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"imageUrl":{"description":"Returned on read. Ignored here","type":"string"}},"required":["text"],"additionalProperties":false,"description":"One picture guests can pick. A new choice needs its picture (uploadPath or sourceUrl); a kept one needs it only to replace the picture. PNG or JPEG, up to 8 MB; fitted to 800px on its long edge. For uploadPath use create_upload with purpose 'ai-choice-image'"}}},"required":["type","text","choices"],"additionalProperties":false,"description":"A choice between pictures. The picked picture is sent to the image model"}]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64,"description":"The field's id"},"key":{"description":"A name of your own for this field, so `body` can point at it in the same call (`fieldKey`). Not stored","type":"string","pattern":"^[A-Za-z0-9_-]{1,40}$"},"type":{"type":"string","minLength":1,"maxLength":64},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (custom, full name, choice)","type":"string","maxLength":200},"text":{"description":"The question the guest reads","type":"string","maxLength":1000}},"required":["id","type"],"additionalProperties":false,"description":"A field of any other type, as get_ai_prompt returns it. Send it back with its id to keep it; its name and text can be changed, the rest of it stays as it is. It cannot be created here"}]},"description":"The prompt's fields, complete and in the order guests are asked. REPLACES the list: a field with its `id` is kept or changed (its type cannot change; a name left out stays as it is), one without is created, one left out is DELETED — refused while a template shows it or an event's survey asks it"},"model":{"description":"Image model — list_ai_models gives each one's credits per image, speed and reference-image cap; default nano-banana-2-2k","type":"string","enum":["nano-banana","nano-banana-pro-2k","nano-banana-pro-4k","nano-banana-2-2k","nano-banana-2-4k","nano-banana-2-lite","openai-images-2.5-flare-medium","openai-images-2.5-flare-high","openai-images-2.5-sunburst-medium","openai-images-2.5-sunburst-high"]},"rerollsAllowed":{"description":"Rerolls guests get; omit for the iPad device default","type":"integer","minimum":0,"maximum":20},"serverProcessDesired":{"description":"true = guests do not wait: the result is made on the server and sent to the email address or phone number the guest gives (the iPad asks for one), and re-rolls do not apply. false = guests wait at the iPad and see the result there. In Capture Station mode the setting does not apply. The dashboard switches this on when it picks the slowest model (Nano Banana Pro 4K); this API changes it only when you send it. Default false","type":"boolean"}},"required":["name"]}}}}}},"/ai-prompts/{id}":{"get":{"operationId":"get_ai_prompt","summary":"Get one AI prompt (yours or a public one)","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean"},"previewImageUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The picture guests pick the prompt by, at the booth (7-day signed URL — refresh by re-listing). null = none yet, and the iPad does NOT offer the prompt to guests until one is set: test_ai_prompt with useAsPreview, or set_ai_prompt_preview_image"},"model":{"anyOf":[{"type":"string"},{"type":"null"}]},"disabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"rerollsAllowed":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Rerolls guests get; null = the iPad device default"},"serverProcessDesired":{"type":"boolean","description":"true = guests do not wait: the result is made on the server and sent to the email address or phone number the guest gives (the iPad asks for one), and re-rolls do not apply. false = guests wait at the iPad and see the result there. In Capture Station mode the setting does not apply. The dashboard switches this on when it picks the slowest model (Nano Banana Pro 4K); this API changes it only when you send it"},"promptText":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A plain prompt: its text (first variant). null when the prompt has a body of pieces (hasPromptDoc) — read and write `body` instead."},"hasPromptDoc":{"type":"boolean","description":"true = the prompt has a body of pieces (`body`), published from the dashboard prompt editor or through this API; false = a plain prompt (`promptText`)"},"body":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"text"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false,"description":"Prompt text, used as written"},{"type":"object","properties":{"kind":{"type":"string","const":"fieldRef"},"fieldId":{"type":"string"},"usageId":{"type":"string","description":"The id of this one use of the field"},"role":{"type":"string","enum":["image","text"],"description":"'image' = the use of an image choice that sends the picked picture to the image model (its first use); 'text' = the answer as words"},"phrasing":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"},"description":"For a choice field: the words each choice puts into the prompt, by choice id"}},"required":["kind","fieldId","usageId","role","phrasing"],"additionalProperties":false,"description":"The guest's answer to a field, placed in the prompt"}]},"description":"The prompt as an ordered list of pieces: text, and the guest's answers to `fields`. For a plain prompt, its text as one piece"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}}},"required":["id","type","name","text"],"additionalProperties":false},"description":"The questions a guest answers before a generation, in the order they are asked"},"versionNumber":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"How many times the prompt has been published; null = never"},"publishedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When it was last published; null = never"},"referenceImages":{"type":"array","items":{"type":"object","properties":{"storagePath":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass to remove_ai_prompt_reference_image. null on PUBLIC catalog prompts: the path is namespaced by the account that created them, so returning it would disclose another account's uid and our storage layout — and they cannot be edited anyway."},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Browser-fetchable (7-day signed URL — re-fetch to refresh); null when the picture could not be linked"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A short phrase naming what the image shows (e.g. \"a red baseball cap\"), written automatically when the image is added — the dashboard assistant refers to the image by it. null = not described."}},"required":["storagePath","url","description"],"additionalProperties":false},"description":"Style reference images sent with every generation"},"hasReferenceImages":{"type":"boolean"}},"required":["id","name","createdDate","modifiedDate","isPublic","previewImageUrl","model","disabled","rerollsAllowed","serverProcessDesired","promptText","hasPromptDoc","body","fields","versionNumber","publishedAt","referenceImages","hasReferenceImages"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}},"patch":{"operationId":"update_ai_prompt","summary":"Update an AI prompt (name, text or body and fields, model, enabled state, rerolls)","description":"A plain prompt takes `promptText`. A prompt with a body (hasPromptDoc) takes `body` and `fields`, each REPLACING that part and each optional: one left out stays as it is. In `fields`, send a field with its `id` to keep or change it, without one to create it (give it a `key` so `body` can use it via `fieldKey`); a field left out is DELETED, which is refused while a template shows it or an event's survey asks it. Renaming a choice updates its wording in the body unless that wording was set on purpose. Sending `body` to a plain prompt turns it into a prompt with a body. A change to body or fields is PUBLISHED: guests get it at once, and the dashboard's version history gains an entry; a call that changes nothing publishes nothing. Send back the ids you read (get_ai_prompt), or fields and choices are created again. Refused with `conflict` if someone published the prompt while the call was being worked on: read it again and retry. rerollsAllowed: null restores the iPad device default. New choice pictures sent without imageDescription are described automatically. A picture is described once: the same image sent again reuses its description. An account gets 200 new descriptions a day (UTC); past that, pictures are stored without one and the response carries warnings: ['imageDescriptionSkipped'].","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean"},"previewImageUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The picture guests pick the prompt by, at the booth (7-day signed URL — refresh by re-listing). null = none yet, and the iPad does NOT offer the prompt to guests until one is set: test_ai_prompt with useAsPreview, or set_ai_prompt_preview_image"},"model":{"anyOf":[{"type":"string"},{"type":"null"}]},"disabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"rerollsAllowed":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Rerolls guests get; null = the iPad device default"},"serverProcessDesired":{"type":"boolean","description":"true = guests do not wait: the result is made on the server and sent to the email address or phone number the guest gives (the iPad asks for one), and re-rolls do not apply. false = guests wait at the iPad and see the result there. In Capture Station mode the setting does not apply. The dashboard switches this on when it picks the slowest model (Nano Banana Pro 4K); this API changes it only when you send it"},"promptText":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A plain prompt: its text (first variant). null when the prompt has a body of pieces (hasPromptDoc) — read and write `body` instead."},"hasPromptDoc":{"type":"boolean","description":"true = the prompt has a body of pieces (`body`), published from the dashboard prompt editor or through this API; false = a plain prompt (`promptText`)"},"body":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"text"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false,"description":"Prompt text, used as written"},{"type":"object","properties":{"kind":{"type":"string","const":"fieldRef"},"fieldId":{"type":"string"},"usageId":{"type":"string","description":"The id of this one use of the field"},"role":{"type":"string","enum":["image","text"],"description":"'image' = the use of an image choice that sends the picked picture to the image model (its first use); 'text' = the answer as words"},"phrasing":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"},"description":"For a choice field: the words each choice puts into the prompt, by choice id"}},"required":["kind","fieldId","usageId","role","phrasing"],"additionalProperties":false,"description":"The guest's answer to a field, placed in the prompt"}]},"description":"The prompt as an ordered list of pieces: text, and the guest's answers to `fields`. For a plain prompt, its text as one piece"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}}},"required":["id","type","name","text"],"additionalProperties":false},"description":"The questions a guest answers before a generation, in the order they are asked"},"versionNumber":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"How many times the prompt has been published; null = never"},"publishedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When it was last published; null = never"},"referenceImages":{"type":"array","items":{"type":"object","properties":{"storagePath":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass to remove_ai_prompt_reference_image. null on PUBLIC catalog prompts: the path is namespaced by the account that created them, so returning it would disclose another account's uid and our storage layout — and they cannot be edited anyway."},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Browser-fetchable (7-day signed URL — re-fetch to refresh); null when the picture could not be linked"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A short phrase naming what the image shows (e.g. \"a red baseball cap\"), written automatically when the image is added — the dashboard assistant refers to the image by it. null = not described."}},"required":["storagePath","url","description"],"additionalProperties":false},"description":"Style reference images sent with every generation"},"hasReferenceImages":{"type":"boolean"},"warnings":{"description":"Non-blocking. 'imageDescriptionSkipped': a new picture was stored without its automatic description, because the account has used its 200 descriptions for today (UTC days). The picture works the same; a choice picture can be given one with imageDescription.","type":"array","items":{"type":"string","const":"imageDescriptionSkipped"}}},"required":["id","name","createdDate","modifiedDate","isPublic","previewImageUrl","model","disabled","rerollsAllowed","serverProcessDesired","promptText","hasPromptDoc","body","fields","versionNumber","publishedAt","referenceImages","hasReferenceImages"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"promptText":{"description":"The text of a plain prompt. Not for a prompt with a body","type":"string","minLength":1,"maxLength":5000},"body":{"minItems":1,"maxItems":200,"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"text"},"value":{"type":"string","maxLength":50000}},"required":["kind","value"],"additionalProperties":false,"description":"Prompt text, used as written. A line break starts a new paragraph"},{"type":"object","properties":{"kind":{"type":"string","const":"fieldRef"},"fieldId":{"description":"The id of one of the prompt fields","type":"string","minLength":1,"maxLength":64},"fieldKey":{"description":"Or the `key` of a field in this same call","type":"string","pattern":"^[A-Za-z0-9_-]{1,40}$"},"usageId":{"description":"Send back the one you read. Left out, the use keeps its id by position","type":"string","minLength":1,"maxLength":64},"role":{"description":"Returned on read. Set for you: the first use of an image choice is 'image'","type":"string","enum":["image","text"]},"phrasing":{"description":"For a choice field: the words each choice puts into the prompt, by choice id (or the `key` of a choice in this call). A choice left out puts in its own text. Entries for choices the field does not have are ignored","type":"object","propertyNames":{"type":"string","minLength":1,"maxLength":64},"additionalProperties":{"type":"string","maxLength":10000}}},"required":["kind"],"additionalProperties":false,"description":"The guest's answer to a field, placed in the prompt"}]},"description":"The prompt as an ordered list of pieces: text, and answers to fields. REPLACES the body"},"fields":{"maxItems":20,"type":"array","items":{"anyOf":[{"oneOf":[{"type":"object","properties":{"id":{"description":"The field's id, to keep or change it. Leave out to create a field","type":"string","minLength":1,"maxLength":64},"key":{"description":"A name of your own for this field, so `body` can point at it in the same call (`fieldKey`). Not stored","type":"string","pattern":"^[A-Za-z0-9_-]{1,40}$"},"type":{"type":"string","const":"string"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (custom, full name, choice)","type":"string","maxLength":200},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"The question the guest reads"}},"required":["type","text"],"additionalProperties":false,"description":"A line of free text the guest types"},{"type":"object","properties":{"id":{"description":"The field's id, to keep or change it. Leave out to create a field","type":"string","minLength":1,"maxLength":64},"key":{"description":"A name of your own for this field, so `body` can point at it in the same call (`fieldKey`). Not stored","type":"string","pattern":"^[A-Za-z0-9_-]{1,40}$"},"type":{"type":"string","const":"full_name"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (custom, full name, choice)","type":"string","maxLength":200},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"The question the guest reads"}},"required":["type","text"],"additionalProperties":false,"description":"The guest's name"},{"type":"object","properties":{"id":{"description":"The field's id, to keep or change it. Leave out to create a field","type":"string","minLength":1,"maxLength":64},"key":{"description":"A name of your own for this field, so `body` can point at it in the same call (`fieldKey`). Not stored","type":"string","pattern":"^[A-Za-z0-9_-]{1,40}$"},"type":{"type":"string","const":"multi_select"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (custom, full name, choice)","type":"string","maxLength":200},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"The question the guest reads"},"choices":{"minItems":2,"maxItems":50,"type":"array","items":{"type":"object","properties":{"id":{"description":"The choice's id, to keep or change it. Leave out to add a choice","type":"string","minLength":1,"maxLength":64},"key":{"description":"A name of your own for this choice, so `phrasing` can point at it in the same call. Not stored","type":"string","pattern":"^[A-Za-z0-9_-]{1,40}$"},"text":{"type":"string","minLength":1,"maxLength":200,"description":"What the guest reads"}},"required":["text"],"additionalProperties":false}}},"required":["type","text","choices"],"additionalProperties":false,"description":"A choice from a list of words. What each choice puts into the prompt is `phrasing`"},{"type":"object","properties":{"id":{"description":"The field's id, to keep or change it. Leave out to create a field","type":"string","minLength":1,"maxLength":64},"key":{"description":"A name of your own for this field, so `body` can point at it in the same call (`fieldKey`). Not stored","type":"string","pattern":"^[A-Za-z0-9_-]{1,40}$"},"type":{"type":"string","const":"image_multi_select"},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (custom, full name, choice)","type":"string","maxLength":200},"text":{"type":"string","minLength":1,"maxLength":1000,"description":"The question the guest reads"},"choices":{"minItems":1,"maxItems":50,"type":"array","items":{"type":"object","properties":{"id":{"description":"The choice's id, to keep or change it. Leave out to add a choice","type":"string","minLength":1,"maxLength":64},"key":{"description":"A name of your own for this choice, so `phrasing` can point at it in the same call. Not stored","type":"string","pattern":"^[A-Za-z0-9_-]{1,40}$"},"text":{"type":"string","minLength":1,"maxLength":200,"description":"What the guest reads"},"uploadPath":{"description":"storagePath from create_upload (after PUTting the bytes)","type":"string"},"sourceUrl":{"description":"Public https URL fetched server-side (must not redirect) — alternative to uploadPath","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows, e.g. \"a red baseball cap\". Written automatically for a new picture when left out","anyOf":[{"type":"string","maxLength":200},{"type":"null"}]},"imageUrl":{"description":"Returned on read. Ignored here","type":"string"}},"required":["text"],"additionalProperties":false,"description":"One picture guests can pick. A new choice needs its picture (uploadPath or sourceUrl); a kept one needs it only to replace the picture. PNG or JPEG, up to 8 MB; fitted to 800px on its long edge. For uploadPath use create_upload with purpose 'ai-choice-image'"}}},"required":["type","text","choices"],"additionalProperties":false,"description":"A choice between pictures. The picked picture is sent to the image model"}]},{"type":"object","properties":{"id":{"type":"string","minLength":1,"maxLength":64,"description":"The field's id"},"key":{"description":"A name of your own for this field, so `body` can point at it in the same call (`fieldKey`). Not stored","type":"string","pattern":"^[A-Za-z0-9_-]{1,40}$"},"type":{"type":"string","minLength":1,"maxLength":64},"name":{"description":"The data label: what the answer is called in exports. Default: a label for the type (custom, full name, choice)","type":"string","maxLength":200},"text":{"description":"The question the guest reads","type":"string","maxLength":1000}},"required":["id","type"],"additionalProperties":false,"description":"A field of any other type, as get_ai_prompt returns it. Send it back with its id to keep it; its name and text can be changed, the rest of it stays as it is. It cannot be created here"}]},"description":"The prompt's fields, complete and in the order guests are asked. REPLACES the list: a field with its `id` is kept or changed (its type cannot change; a name left out stays as it is), one without is created, one left out is DELETED — refused while a template shows it or an event's survey asks it"},"model":{"description":"See list_ai_models","type":"string","enum":["nano-banana","nano-banana-pro-2k","nano-banana-pro-4k","nano-banana-2-2k","nano-banana-2-4k","nano-banana-2-lite","openai-images-2.5-flare-medium","openai-images-2.5-flare-high","openai-images-2.5-sunburst-medium","openai-images-2.5-sunburst-high"]},"disabled":{"type":"boolean"},"rerollsAllowed":{"anyOf":[{"type":"integer","minimum":0,"maximum":20},{"type":"null"}]},"serverProcessDesired":{"description":"true = guests do not wait: the result is made on the server and sent to the email address or phone number the guest gives (the iPad asks for one), and re-rolls do not apply. false = guests wait at the iPad and see the result there. In Capture Station mode the setting does not apply. The dashboard switches this on when it picks the slowest model (Nano Banana Pro 4K); this API changes it only when you send it","type":"boolean"}}}}}}},"delete":{"operationId":"delete_ai_prompt","summary":"Delete an AI prompt (removes it from any templates using it)","description":"Refused with `conflict` while one of the prompt's fields is still shown on a template of yours or asked in an event's survey, naming where: deleting the prompt deletes its fields.","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"deleted":{"type":"boolean","const":true},"id":{"type":"string"}},"required":["deleted","id"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/ai-prompts/{id}/reference-images":{"post":{"operationId":"add_ai_prompt_reference_image","summary":"Attach a style reference image to an AI prompt","description":"Provide the image via uploadPath (create_upload + PUT) or a public https sourceUrl. Stored as uploaded — generation downscales to 1536px as needed. Each model caps how many reference images are used (nano-banana: 2, newer models: 13). A short description of the image is written automatically (referenceImages[].description) — it costs no AI credits, and when it cannot be written the image is added without one. A picture is described once: the same image sent again reuses its description. An account gets 200 new descriptions a day (UTC); past that, pictures are stored without one and the response carries warnings: ['imageDescriptionSkipped'].","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean"},"previewImageUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The picture guests pick the prompt by, at the booth (7-day signed URL — refresh by re-listing). null = none yet, and the iPad does NOT offer the prompt to guests until one is set: test_ai_prompt with useAsPreview, or set_ai_prompt_preview_image"},"model":{"anyOf":[{"type":"string"},{"type":"null"}]},"disabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"rerollsAllowed":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Rerolls guests get; null = the iPad device default"},"serverProcessDesired":{"type":"boolean","description":"true = guests do not wait: the result is made on the server and sent to the email address or phone number the guest gives (the iPad asks for one), and re-rolls do not apply. false = guests wait at the iPad and see the result there. In Capture Station mode the setting does not apply. The dashboard switches this on when it picks the slowest model (Nano Banana Pro 4K); this API changes it only when you send it"},"promptText":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A plain prompt: its text (first variant). null when the prompt has a body of pieces (hasPromptDoc) — read and write `body` instead."},"hasPromptDoc":{"type":"boolean","description":"true = the prompt has a body of pieces (`body`), published from the dashboard prompt editor or through this API; false = a plain prompt (`promptText`)"},"body":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"text"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false,"description":"Prompt text, used as written"},{"type":"object","properties":{"kind":{"type":"string","const":"fieldRef"},"fieldId":{"type":"string"},"usageId":{"type":"string","description":"The id of this one use of the field"},"role":{"type":"string","enum":["image","text"],"description":"'image' = the use of an image choice that sends the picked picture to the image model (its first use); 'text' = the answer as words"},"phrasing":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"},"description":"For a choice field: the words each choice puts into the prompt, by choice id"}},"required":["kind","fieldId","usageId","role","phrasing"],"additionalProperties":false,"description":"The guest's answer to a field, placed in the prompt"}]},"description":"The prompt as an ordered list of pieces: text, and the guest's answers to `fields`. For a plain prompt, its text as one piece"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}}},"required":["id","type","name","text"],"additionalProperties":false},"description":"The questions a guest answers before a generation, in the order they are asked"},"versionNumber":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"How many times the prompt has been published; null = never"},"publishedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When it was last published; null = never"},"referenceImages":{"type":"array","items":{"type":"object","properties":{"storagePath":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass to remove_ai_prompt_reference_image. null on PUBLIC catalog prompts: the path is namespaced by the account that created them, so returning it would disclose another account's uid and our storage layout — and they cannot be edited anyway."},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Browser-fetchable (7-day signed URL — re-fetch to refresh); null when the picture could not be linked"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A short phrase naming what the image shows (e.g. \"a red baseball cap\"), written automatically when the image is added — the dashboard assistant refers to the image by it. null = not described."}},"required":["storagePath","url","description"],"additionalProperties":false},"description":"Style reference images sent with every generation"},"hasReferenceImages":{"type":"boolean"},"warnings":{"description":"Non-blocking. 'imageDescriptionSkipped': a new picture was stored without its automatic description, because the account has used its 200 descriptions for today (UTC days). The picture works the same; a choice picture can be given one with imageDescription.","type":"array","items":{"type":"string","const":"imageDescriptionSkipped"}}},"required":["id","name","createdDate","modifiedDate","isPublic","previewImageUrl","model","disabled","rerollsAllowed","serverProcessDesired","promptText","hasPromptDoc","body","fields","versionNumber","publishedAt","referenceImages","hasReferenceImages"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"uploadPath":{"description":"storagePath from create_upload (after PUTting the bytes)","type":"string"},"sourceUrl":{"description":"Public https URL fetched server-side (must not redirect) — alternative to uploadPath","type":"string"}}}}}}},"delete":{"operationId":"remove_ai_prompt_reference_image","summary":"Remove a reference image from an AI prompt","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}},{"name":"storagePath","in":"query","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"From the prompt DTO referenceImages[].storagePath"},"description":"From the prompt DTO referenceImages[].storagePath"}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean"},"previewImageUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The picture guests pick the prompt by, at the booth (7-day signed URL — refresh by re-listing). null = none yet, and the iPad does NOT offer the prompt to guests until one is set: test_ai_prompt with useAsPreview, or set_ai_prompt_preview_image"},"model":{"anyOf":[{"type":"string"},{"type":"null"}]},"disabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"rerollsAllowed":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Rerolls guests get; null = the iPad device default"},"serverProcessDesired":{"type":"boolean","description":"true = guests do not wait: the result is made on the server and sent to the email address or phone number the guest gives (the iPad asks for one), and re-rolls do not apply. false = guests wait at the iPad and see the result there. In Capture Station mode the setting does not apply. The dashboard switches this on when it picks the slowest model (Nano Banana Pro 4K); this API changes it only when you send it"},"promptText":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A plain prompt: its text (first variant). null when the prompt has a body of pieces (hasPromptDoc) — read and write `body` instead."},"hasPromptDoc":{"type":"boolean","description":"true = the prompt has a body of pieces (`body`), published from the dashboard prompt editor or through this API; false = a plain prompt (`promptText`)"},"body":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"text"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false,"description":"Prompt text, used as written"},{"type":"object","properties":{"kind":{"type":"string","const":"fieldRef"},"fieldId":{"type":"string"},"usageId":{"type":"string","description":"The id of this one use of the field"},"role":{"type":"string","enum":["image","text"],"description":"'image' = the use of an image choice that sends the picked picture to the image model (its first use); 'text' = the answer as words"},"phrasing":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"},"description":"For a choice field: the words each choice puts into the prompt, by choice id"}},"required":["kind","fieldId","usageId","role","phrasing"],"additionalProperties":false,"description":"The guest's answer to a field, placed in the prompt"}]},"description":"The prompt as an ordered list of pieces: text, and the guest's answers to `fields`. For a plain prompt, its text as one piece"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}}},"required":["id","type","name","text"],"additionalProperties":false},"description":"The questions a guest answers before a generation, in the order they are asked"},"versionNumber":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"How many times the prompt has been published; null = never"},"publishedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When it was last published; null = never"},"referenceImages":{"type":"array","items":{"type":"object","properties":{"storagePath":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass to remove_ai_prompt_reference_image. null on PUBLIC catalog prompts: the path is namespaced by the account that created them, so returning it would disclose another account's uid and our storage layout — and they cannot be edited anyway."},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Browser-fetchable (7-day signed URL — re-fetch to refresh); null when the picture could not be linked"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A short phrase naming what the image shows (e.g. \"a red baseball cap\"), written automatically when the image is added — the dashboard assistant refers to the image by it. null = not described."}},"required":["storagePath","url","description"],"additionalProperties":false},"description":"Style reference images sent with every generation"},"hasReferenceImages":{"type":"boolean"}},"required":["id","name","createdDate","modifiedDate","isPublic","previewImageUrl","model","disabled","rerollsAllowed","serverProcessDesired","promptText","hasPromptDoc","body","fields","versionNumber","publishedAt","referenceImages","hasReferenceImages"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/ai-prompts/{id}/preview-image":{"put":{"operationId":"set_ai_prompt_preview_image","summary":"Set an AI prompt's preview thumbnail","description":"The image operator webapps show END customers when they pick a style (previewImageUrl). Provide via uploadPath or sourceUrl.","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean"},"previewImageUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The picture guests pick the prompt by, at the booth (7-day signed URL — refresh by re-listing). null = none yet, and the iPad does NOT offer the prompt to guests until one is set: test_ai_prompt with useAsPreview, or set_ai_prompt_preview_image"},"model":{"anyOf":[{"type":"string"},{"type":"null"}]},"disabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"rerollsAllowed":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Rerolls guests get; null = the iPad device default"},"serverProcessDesired":{"type":"boolean","description":"true = guests do not wait: the result is made on the server and sent to the email address or phone number the guest gives (the iPad asks for one), and re-rolls do not apply. false = guests wait at the iPad and see the result there. In Capture Station mode the setting does not apply. The dashboard switches this on when it picks the slowest model (Nano Banana Pro 4K); this API changes it only when you send it"},"promptText":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A plain prompt: its text (first variant). null when the prompt has a body of pieces (hasPromptDoc) — read and write `body` instead."},"hasPromptDoc":{"type":"boolean","description":"true = the prompt has a body of pieces (`body`), published from the dashboard prompt editor or through this API; false = a plain prompt (`promptText`)"},"body":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"text"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false,"description":"Prompt text, used as written"},{"type":"object","properties":{"kind":{"type":"string","const":"fieldRef"},"fieldId":{"type":"string"},"usageId":{"type":"string","description":"The id of this one use of the field"},"role":{"type":"string","enum":["image","text"],"description":"'image' = the use of an image choice that sends the picked picture to the image model (its first use); 'text' = the answer as words"},"phrasing":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"},"description":"For a choice field: the words each choice puts into the prompt, by choice id"}},"required":["kind","fieldId","usageId","role","phrasing"],"additionalProperties":false,"description":"The guest's answer to a field, placed in the prompt"}]},"description":"The prompt as an ordered list of pieces: text, and the guest's answers to `fields`. For a plain prompt, its text as one piece"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}}},"required":["id","type","name","text"],"additionalProperties":false},"description":"The questions a guest answers before a generation, in the order they are asked"},"versionNumber":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"How many times the prompt has been published; null = never"},"publishedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When it was last published; null = never"},"referenceImages":{"type":"array","items":{"type":"object","properties":{"storagePath":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass to remove_ai_prompt_reference_image. null on PUBLIC catalog prompts: the path is namespaced by the account that created them, so returning it would disclose another account's uid and our storage layout — and they cannot be edited anyway."},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Browser-fetchable (7-day signed URL — re-fetch to refresh); null when the picture could not be linked"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A short phrase naming what the image shows (e.g. \"a red baseball cap\"), written automatically when the image is added — the dashboard assistant refers to the image by it. null = not described."}},"required":["storagePath","url","description"],"additionalProperties":false},"description":"Style reference images sent with every generation"},"hasReferenceImages":{"type":"boolean"}},"required":["id","name","createdDate","modifiedDate","isPublic","previewImageUrl","model","disabled","rerollsAllowed","serverProcessDesired","promptText","hasPromptDoc","body","fields","versionNumber","publishedAt","referenceImages","hasReferenceImages"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"uploadPath":{"description":"storagePath from create_upload (after PUTting the bytes)","type":"string"},"sourceUrl":{"description":"Public https URL fetched server-side (must not redirect) — alternative to uploadPath","type":"string"}}}}}}}},"/ai-prompts/{id}/duplicate":{"post":{"operationId":"duplicate_ai_prompt","summary":"Duplicate an AI prompt (e.g. to customize a public one)","description":"Copies the prompt text or body, settings, dynamic fields (as new fields the copy owns) and reference images. Works on your prompts and public ones. An account can make 50 duplicates a day (UTC), all duplicate_* operations together; past that, rate_limited with Retry-After until midnight UTC. Booth.Events support can lift this limit for an account that needs more.","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"isPublic":{"type":"boolean"},"previewImageUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The picture guests pick the prompt by, at the booth (7-day signed URL — refresh by re-listing). null = none yet, and the iPad does NOT offer the prompt to guests until one is set: test_ai_prompt with useAsPreview, or set_ai_prompt_preview_image"},"model":{"anyOf":[{"type":"string"},{"type":"null"}]},"disabled":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"rerollsAllowed":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Rerolls guests get; null = the iPad device default"},"serverProcessDesired":{"type":"boolean","description":"true = guests do not wait: the result is made on the server and sent to the email address or phone number the guest gives (the iPad asks for one), and re-rolls do not apply. false = guests wait at the iPad and see the result there. In Capture Station mode the setting does not apply. The dashboard switches this on when it picks the slowest model (Nano Banana Pro 4K); this API changes it only when you send it"},"promptText":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A plain prompt: its text (first variant). null when the prompt has a body of pieces (hasPromptDoc) — read and write `body` instead."},"hasPromptDoc":{"type":"boolean","description":"true = the prompt has a body of pieces (`body`), published from the dashboard prompt editor or through this API; false = a plain prompt (`promptText`)"},"body":{"type":"array","items":{"oneOf":[{"type":"object","properties":{"kind":{"type":"string","const":"text"},"value":{"type":"string"}},"required":["kind","value"],"additionalProperties":false,"description":"Prompt text, used as written"},{"type":"object","properties":{"kind":{"type":"string","const":"fieldRef"},"fieldId":{"type":"string"},"usageId":{"type":"string","description":"The id of this one use of the field"},"role":{"type":"string","enum":["image","text"],"description":"'image' = the use of an image choice that sends the picked picture to the image model (its first use); 'text' = the answer as words"},"phrasing":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"string"},"description":"For a choice field: the words each choice puts into the prompt, by choice id"}},"required":["kind","fieldId","usageId","role","phrasing"],"additionalProperties":false,"description":"The guest's answer to a field, placed in the prompt"}]},"description":"The prompt as an ordered list of pieces: text, and the guest's answers to `fields`. For a plain prompt, its text as one piece"},"fields":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string","description":"string (free text), full_name, multi_select (a choice from a list) or image_multi_select (a choice between pictures)"},"name":{"type":"string","description":"The data label: what the answer is called in exports"},"text":{"type":"string","description":"The question the guest reads"},"choices":{"description":"The choices of a choice field, in the order guests see them","type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"text":{"type":"string"},"imageUrl":{"description":"An image choice's picture (7-day signed URL — re-fetch to refresh)","type":"string"},"imageDescription":{"description":"A short phrase naming what the picture shows; the prompt refers to the picture by it","type":"string"}},"required":["id","text"],"additionalProperties":false}}},"required":["id","type","name","text"],"additionalProperties":false},"description":"The questions a guest answers before a generation, in the order they are asked"},"versionNumber":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"How many times the prompt has been published; null = never"},"publishedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When it was last published; null = never"},"referenceImages":{"type":"array","items":{"type":"object","properties":{"storagePath":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pass to remove_ai_prompt_reference_image. null on PUBLIC catalog prompts: the path is namespaced by the account that created them, so returning it would disclose another account's uid and our storage layout — and they cannot be edited anyway."},"url":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Browser-fetchable (7-day signed URL — re-fetch to refresh); null when the picture could not be linked"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A short phrase naming what the image shows (e.g. \"a red baseball cap\"), written automatically when the image is added — the dashboard assistant refers to the image by it. null = not described."}},"required":["storagePath","url","description"],"additionalProperties":false},"description":"Style reference images sent with every generation"},"hasReferenceImages":{"type":"boolean"}},"required":["id","name","createdDate","modifiedDate","isPublic","previewImageUrl","model","disabled","rerollsAllowed","serverProcessDesired","promptText","hasPromptDoc","body","fields","versionNumber","publishedAt","referenceImages","hasReferenceImages"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/ai-prompts/{id}/test":{"post":{"operationId":"test_ai_prompt","summary":"Run one test generation of an AI prompt and get the image","description":"Generates one image with the prompt as it is published, the way the dashboard's prompt editor tests it, and returns it at full resolution (imageUrl) with a 1024 px copy (thumbnailUrl). The photo: uploadPath (create_upload with purpose 'ai-test-photo', then PUT) or a public https sourceUrl; leave both out to use a sample portrait, the one the dashboard offers by default. Your photo is used for this test only: it is not kept, nor added to the dashboard's 'First image' choices. Fields: `answers` gives the guest's answer per field id (a choice id, or text); a field left out takes the prompt's stored preview pick, else its first choice. `useAsPreview` makes the result the prompt's guest preview: a prompt is NOT shown to guests until it has one, so this is the usual way to finish a prompt made through the API. It costs the account's AI credits (list_ai_models: testCredits per model, from 0.25; a 4K model is tested at 2K) and is refused with `insufficient_credits` (402) when the balance is short; nothing is charged for a refused generation or one the model fails. The call waits for the model: typically 30-100 seconds, up to 4 minutes; a generation still running then may finish and be charged with its image lost, so check get_ai_credits before retrying a timed-out call. The test is recorded in the prompt's history in the dashboard.","parameters":[{"name":"id","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"imageUrl":{"type":"string","description":"The generated image at full resolution, as the model made it (7-day signed URL — download it, do not store the link)"},"thumbnailUrl":{"type":"string","description":"A 1024 px copy, for a quick look (7-day signed URL); the full-resolution link when the copy could not be made"},"modelUsed":{"type":"string","description":"The model that ran. A 4K model is tested at 2K, so this can differ from the prompt"},"durationSeconds":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"How long the model took"},"creditsUsed":{"type":"number","description":"AI credits this test consumed"},"creditsRemaining":{"type":"number","description":"The account's AI credit balance afterwards"},"text":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Words the model returned with the image, if any; usually null"},"isPreview":{"type":"boolean","description":"true = this image is now the prompt's guest preview. false after useAsPreview means it could not be set: set_ai_prompt_preview_image with the image"}},"required":["imageUrl","thumbnailUrl","modelUsed","durationSeconds","creditsUsed","creditsRemaining","text","isPreview"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"uploadPath":{"description":"storagePath from create_upload (after PUTting the bytes)","type":"string"},"sourceUrl":{"description":"Public https URL fetched server-side (must not redirect) — alternative to uploadPath","type":"string"},"answers":{"description":"The guest’s answer per field id: a choice id for a choice field, the text for a free-text field. Fields left out take the prompt’s preview picks (get_ai_prompt lists the fields)","type":"object","propertyNames":{"type":"string","minLength":1,"maxLength":64},"additionalProperties":{"type":"string","maxLength":1000}},"aspectRatio":{"description":"The shape of the result. Left out, it follows the photo (what a guest gets at the booth). Models that cannot do a shape ignore it","type":"string","enum":["1:1","3:4","4:3"]},"useAsPreview":{"description":"Also make the result the prompt's guest preview (your own prompts only)","type":"boolean"}}}}}}}},"/ai-portraits":{"get":{"operationId":"list_ai_portraits","summary":"List AI portrait styles (the catalog guests can pick from)","description":"The default catalog plus any styles private to your account, newest first, each with a browser-fetchable previewImageUrl (public catalog: stable, never expires; account-private: 7-day signed — re-list to refresh). Built for operator webapps that let end customers choose a style. Sibling of list_ai_prompts.","parameters":[{"name":"limit","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","default":25,"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string"}},{"name":"includePublic","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","description":"false = only styles private to this account","type":"boolean"},"description":"false = only styles private to this account"}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Upstream style-category label (values like 'public', 'custom', 'aiPortrait') — informational only; availability is governed by isPublic"},"isPublic":{"type":"boolean","description":"true = available to every account (use this, not type, to pick catalog entries); false = private to this account"},"previewImageUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Browser-fetchable thumbnail. Public catalog entries get a stable URL that never expires; account-private ones get a 7-day signed URL — refresh by re-listing."},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","type","isPublic","previewImageUrl","createdDate"],"additionalProperties":false}},"hasMore":{"type":"boolean"},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["data","hasMore","nextCursor"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/ai-portraits/{portraitId}":{"get":{"operationId":"get_ai_portrait","summary":"Get one AI portrait style (yours or a public one)","parameters":[{"name":"portraitId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"type":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Upstream style-category label (values like 'public', 'custom', 'aiPortrait') — informational only; availability is governed by isPublic"},"isPublic":{"type":"boolean","description":"true = available to every account (use this, not type, to pick catalog entries); false = private to this account"},"previewImageUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Browser-fetchable thumbnail. Public catalog entries get a stable URL that never expires; account-private ones get a 7-day signed URL — refresh by re-listing."},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","type","isPublic","previewImageUrl","createdDate"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/account/ai-credits":{"get":{"operationId":"get_ai_credits","summary":"The account's AI image credit balance","description":"Each AI generation costs 1-4 credits depending on the model (list_ai_models). The balance is one for the whole account, shared by every event: there is no limit per event, so one busy event can use it all, and once it runs out guests are told the account does not have enough AI credits and get no AI picture. Balance low before an event? The operator tops up in the dashboard (Account → AI credits).","x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"balance":{"type":"number","description":"Remaining AI image credits"}},"required":["balance"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/ai-models":{"get":{"operationId":"list_ai_models","summary":"The image models an AI prompt can run on, with credits per image, speed and reference-image caps","description":"Static catalogue, no pagination. Pick a model here for create_ai_prompt / update_ai_prompt: cheaper rows cost fewer credits per guest photo and return faster at the booth; the pricier rows render at higher quality or resolution. All models are available to every account.","x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"defaultModel":{"type":"string","description":"What create_ai_prompt uses when `model` is omitted"},"data":{"type":"array","items":{"type":"object","properties":{"model":{"type":"string","description":"The value to pass as `model` to create_ai_prompt / update_ai_prompt"},"name":{"type":"string","description":"Display name, as the dashboard shows it"},"provider":{"type":"string","enum":["gemini","openai"]},"credits":{"type":"number","description":"AI credits consumed per generated image at an event (get_ai_credits for the balance)"},"testCredits":{"type":"number","description":"AI credits one test generation (test_ai_prompt) consumes: less than an event generation on the smaller models; a 4K model is tested at 2K"},"expectedSeconds":{"type":"number","description":"Typical generation time per image, in seconds"},"maxReferenceImages":{"type":"number","description":"Reference-image slots a prompt on this model can hold (add_ai_prompt_reference_image)"},"isDefault":{"type":"boolean","description":"The model create_ai_prompt uses when none is given"},"retired":{"type":"boolean","description":"true = the provider no longer runs it: generation fails, and update_ai_prompt should move the prompt to a current model. Retired models stay listed so stored prompts can still be read."}},"required":["model","name","provider","credits","testCredits","expectedSeconds","maxReferenceImages","isDefault","retired"],"additionalProperties":false}}},"required":["defaultModel","data"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/events/{eventId}/stats":{"get":{"operationId":"get_event_stats","summary":"Guest-engagement analytics for one event (views, downloads, shares, top media)","description":"Lifetime totals plus a daily time-series for the range (default last 90 days, max 366). Brand-new events return zeroed totals. timeseries/captureTypes are null for legacy galleries that predate fine-grained tracking. topMedia image URLs are ~7-day signed — re-fetch to refresh. Use list_pay_transactions for revenue; this is engagement.","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}},{"name":"from","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"YYYY-MM-DD (UTC day) — analytics are day-granular, unlike the full ISO timestamps elsewhere"},"description":"YYYY-MM-DD (UTC day) — analytics are day-granular, unlike the full ISO timestamps elsewhere"},{"name":"to","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"YYYY-MM-DD (UTC day) — analytics are day-granular, unlike the full ISO timestamps elsewhere"},"description":"YYYY-MM-DD (UTC day) — analytics are day-granular, unlike the full ISO timestamps elsewhere"},{"name":"response_format","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","default":"detailed","description":"'concise' returns timeseries: null and topMedia: null (the two largest sections) — totals and leaderboards stay","type":"string","enum":["detailed","concise"]},"description":"'concise' returns timeseries: null and topMedia: null (the two largest sections) — totals and leaderboards stay"}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"eventId":{"type":"string"},"galleryId":{"type":"string"},"totals":{"type":"object","properties":{"views":{"type":"number"},"sessionViews":{"type":"number"},"galleryViews":{"type":"number"},"slideshowViews":{"type":"number"},"mediaViews":{"type":"number"},"downloads":{"type":"number","description":"ZIP downloads (whole gallery or one session)"},"shares":{"type":"object","properties":{"email":{"type":"number"},"sms":{"type":"number"},"web":{"type":"number"},"total":{"type":"number"}},"required":["email","sms","web","total"],"additionalProperties":false,"description":"Rollup: email + sms are photos the booth sent to guests; web is EVERY guest share (all platforms), not just the web-share channel"},"sessionCount":{"type":"number"},"mediaCount":{"type":"number"},"visits":{"type":"number","description":"Each day's distinct visitors, added up — the dashboard's Visits tile. A guest who comes back on three days counts three times."},"kBytes":{"description":"Total stored media size (kilobytes)","type":"number"},"aiPortraitCount":{"description":"Prefers the booth.events credits ledger (dashboard parity), SG total as fallback","type":"number"},"aiCustomPromptCount":{"type":"number"}},"required":["views","sessionViews","galleryViews","slideshowViews","mediaViews","downloads","shares","sessionCount","mediaCount","visits"],"additionalProperties":false},"zeroFilledTotals":{"description":"Totals the gallery had no record for, reported as 0. On a brand-new event that means \"nothing yet\"; on a legacy gallery it can mean the metric was never tracked — treat those as unknown rather than as a real zero.","type":"array","items":{"type":"string"}},"timeseries":{"anyOf":[{"type":"object","properties":{"start":{"type":"string"},"end":{"type":"string"},"days":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"views":{"type":"number"},"mediaViews":{"type":"number"},"downloads":{"type":"number","description":"ZIP downloads (whole gallery or one session)"},"shares":{"type":"number","description":"Photos guests took away — any platform, any means"},"uniqueVisitors":{"type":"number","description":"Distinct visitors that day"}},"required":["date","views","mediaViews","downloads","shares","uniqueVisitors"],"additionalProperties":false}}},"required":["start","end","days"],"additionalProperties":false},{"type":"null"}],"description":"null = legacy gallery, fine-grained tracking predates it"},"shareChannels":{"type":"array","items":{"type":"object","properties":{"channel":{"type":"string"},"count":{"type":"number"}},"required":["channel","count"],"additionalProperties":false},"description":"Per-channel breakdown ('email', 'sms', 'web-share'). 'copy-link' was retired 2026-08 and is never returned; the 'web-share' entry only accumulated from 2026-08-07, so prefer totals.shares.web — exact since tracking began"},"devices":{"anyOf":[{"type":"object","properties":{"mobile":{"type":"number"},"desktop":{"type":"number"}},"required":["mobile","desktop"],"additionalProperties":false},{"type":"null"}]},"captureTypes":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"}},{"type":"null"}],"description":"Per-capture-type media counts (photo, video, aiPhoto, …); null = no data yet"},"topAiPrompts":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"promptId":{"type":"string"},"count":{"type":"number"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Resolved server-side; null = prompt since deleted"},"previewImageUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"7-day signed URL — re-fetch to refresh"}},"required":["promptId","count","name","previewImageUrl"],"additionalProperties":false}},{"type":"null"}],"description":"Most-used AI prompts (max 10); null = legacy gallery"},"topAiPortraits":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"portraitId":{"type":"string"},"count":{"type":"number"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"previewImageUrl":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["portraitId","count","name","previewImageUrl"],"additionalProperties":false}},{"type":"null"}]},"topMedia":{"anyOf":[{"type":"array","items":{"type":"object","properties":{"media":{"type":"object","properties":{"id":{"type":"string"},"thumbUrl":{"description":"~7-day signed — re-fetch to refresh","type":"string"},"fullUrl":{"type":"string"},"width":{"type":"number"},"height":{"type":"number"},"contentType":{"type":"string"},"filename":{"type":"string"}},"required":["id"],"additionalProperties":false},"views":{"type":"number"},"downloads":{"type":"number","description":"Frozen from 2026-08 — individual photos count as shares"},"shares":{"type":"number","description":"Times a guest took this photo away"}},"required":["media","views","downloads","shares"],"additionalProperties":false}},{"type":"null"}]},"aiCreditsUsed":{"description":"AI credits consumed by this event (booth.events credits ledger)","type":"number"},"trackingSince":{"description":"First day with fine-grained data (YYYY-MM-DD)","type":"string"}},"required":["eventId","galleryId","totals","timeseries","shareChannels","devices","captureTypes","topAiPrompts","topAiPortraits","topMedia"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/account/analytics":{"get":{"operationId":"get_account_analytics","summary":"Account-wide analytics: engagement totals, capture types, AI usage, top events","description":"Cross-event rollup incl. hard-deleted history (default range last 365 days, max 750; the range only affects the daily series — totals are lifetime). EXPENSIVE: computed on read across all galleries; cache results rather than polling.","parameters":[{"name":"from","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"YYYY-MM-DD (UTC day) — analytics are day-granular, unlike the full ISO timestamps elsewhere"},"description":"YYYY-MM-DD (UTC day) — analytics are day-granular, unlike the full ISO timestamps elsewhere"},{"name":"to","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","pattern":"^\\d{4}-\\d{2}-\\d{2}$","description":"YYYY-MM-DD (UTC day) — analytics are day-granular, unlike the full ISO timestamps elsewhere"},"description":"YYYY-MM-DD (UTC day) — analytics are day-granular, unlike the full ISO timestamps elsewhere"}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"totals":{"type":"object","properties":{"galleryCount":{"type":"number"},"galleriesWithMedia":{"type":"number"},"mediaCountTotal":{"type":"number"},"sessionCountTotal":{"type":"number"},"sentEmailCountTotal":{"type":"number"},"sentSmsCountTotal":{"type":"number"},"engagement":{"type":"object","properties":{"galleryViews":{"type":"number"},"sessionViews":{"type":"number"},"slideshowViews":{"type":"number"},"mediaViews":{"type":"number"},"downloads":{"type":"number"},"shares":{"type":"number"},"visits":{"type":"number","description":"Each day's distinct visitors, added up — the dashboard's Visits tile. A guest who comes back on three days counts three times."},"devices":{"type":"object","properties":{"mobile":{"type":"number"},"desktop":{"type":"number"}},"required":["mobile","desktop"],"additionalProperties":false}},"required":["galleryViews","sessionViews","slideshowViews","mediaViews","downloads","shares","visits","devices"],"additionalProperties":false},"captureTypes":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"number"}},"ai":{"type":"object","properties":{"promptsGenerated":{"type":"number"},"portraits":{"type":"number"},"creditsUsed":{"type":"number"}},"required":["promptsGenerated","portraits","creditsUsed"],"additionalProperties":false}},"required":["galleryCount","galleriesWithMedia","mediaCountTotal","sessionCountTotal","sentEmailCountTotal","sentSmsCountTotal","engagement","captureTypes","ai"],"additionalProperties":false},"daily":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string"},"galleryViews":{"type":"number"},"sessionViews":{"type":"number"},"slideshowViews":{"type":"number"},"mediaViews":{"type":"number"},"downloads":{"type":"number","description":"ZIP downloads (whole gallery or one session)"},"shares":{"type":"number","description":"Photos guests took away — any platform, any means"},"uniqueVisitors":{"type":"number","description":"Distinct visitors that day, added up across your galleries"},"devices":{"type":"object","properties":{"mobile":{"type":"number"},"desktop":{"type":"number"}},"required":["mobile","desktop"],"additionalProperties":false}},"required":["date","galleryViews","sessionViews","slideshowViews","mediaViews","downloads","shares","uniqueVisitors","devices"],"additionalProperties":false},"description":"One row per day, from the first day with activity in the requested range to its end (quiet days in between are rows of zeros); empty when the range has no activity"},"topEvents":{"type":"array","items":{"type":"object","properties":{"galleryId":{"type":"string"},"eventId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Resolved from your events; null = no matching event"},"name":{"type":"string"},"mediaCount":{"type":"number"},"sessionCount":{"type":"number"},"expired":{"type":"boolean"}},"required":["galleryId","eventId","name","mediaCount","sessionCount","expired"],"additionalProperties":false}},"topEventsByAi":{"type":"array","items":{"type":"object","properties":{"galleryId":{"type":"string"},"eventId":{"anyOf":[{"type":"string"},{"type":"null"}]},"name":{"type":"string"},"aiCreationCount":{"type":"number"},"expired":{"type":"boolean"}},"required":["galleryId","eventId","name","aiCreationCount","expired"],"additionalProperties":false}}},"required":["totals","daily","topEvents","topEventsByAi"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/events/{eventId}/report":{"get":{"operationId":"get_event_report","summary":"The client report: config, publish state, and the shareable link","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"eventId":{"type":"string"},"published":{"type":"boolean"},"reportUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The shareable client link (stable until regenerated) — null until first publish"},"previewUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Operator-only preview link — renders even while unpublished. Never send it to the client: it carries its own token, and turning the report off does not stop it. null until the report is first saved or published"},"usesContentBuilder":{"type":"boolean","description":"true = the report shows Content Builder blocks (config.customContent), which take the place of the Message & Button block (config.customContentHeader)"},"config":{"anyOf":[{"type":"object","properties":{"title":{"description":"Default: the event name","type":"string","maxLength":120},"introMessage":{"description":"Personal note to the client","type":"string","maxLength":500},"accentColor":{"description":"#rrggbb. Left out, the gallery's main colour is stored — or black when that colour is too light for the report's white page","type":"string","maxLength":20},"logoUrl":{"description":"A logo for the report's heading, by https address. It is shown whole, up to 144 px tall, so any shape works: send it at least 288 px tall. Left out = the event logo; '' = no logo","type":"string","maxLength":2000},"showHeroBackground":{"description":"true = the gallery's background image (set_event_branding_image, slot 'gallery-background') fills the report's heading, darkened so the heading reads on it","type":"boolean"},"sections":{"description":"Which sections the report shows. A section left out is shown","type":"object","properties":{"trend":{"type":"boolean"},"captureTypes":{"type":"boolean"},"topPhotos":{"type":"boolean"},"shareChannels":{"type":"boolean"},"devices":{"type":"boolean"}},"additionalProperties":false},"customContentHeader":{"description":"The \"Message & Button\" block under the report's heading, above the figures: a heading, a message and one button, in a centred column. A report shows this or Content Builder blocks (customContent), not both","type":"object","properties":{"headerText":{"description":"The heading","type":"string","maxLength":200},"text":{"description":"The message under the heading","type":"string","maxLength":1000},"buttonText":{"type":"string","maxLength":120},"buttonLink":{"description":"Where the button goes. The button shows only with buttonText and a usable link","type":"string","maxLength":2048},"buttonColor":{"description":"#rrggbb behind the button's white text, so a very light colour is refused. Default: the accent colour","type":"string","maxLength":32}},"additionalProperties":false},"customContent":{"description":"The report's Content Builder blocks, in place of the Message & Button block; null = none. topBlocks show under the report's heading, above the figures; bottomBlocks close the page, after the top photos. Three block types: section_title (text: a bold, centred heading), rich_text (content: HTML shown as written, the width of the report; give images an absolute https address) and button (buttonText, buttonLink, buttonColor: a rounded button with white text that opens the link in a new tab; 2 or 3 buttons one after another share a line, longer runs stack, and a button without text or without a usable link is not shown; give links with https://, as one without a scheme opens as http://)","anyOf":[{"type":"object","properties":{"topBlocks":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"}},"required":["type"],"additionalProperties":{}}},"bottomBlocks":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"}},"required":["type"],"additionalProperties":{}}}},"required":["topBlocks","bottomBlocks"],"additionalProperties":false},{"type":"null"}]},"hiddenMetrics":{"type":"array","items":{"type":"string"}},"topPhotosCount":{"anyOf":[{"type":"number","const":4},{"type":"number","const":8},{"type":"number","const":12}]},"range":{"description":"YYYY-MM-DD; absent = all-time","type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"}},"additionalProperties":false},"hideBoothEventsBranding":{"description":"Always true once the report is saved: like the dashboard's, every save hides the Booth.Events name and logo on the report, so it carries only the operator's brand","type":"boolean"}},"additionalProperties":false},{"type":"null"}],"description":"null = never configured (defaults apply)"}},"required":["eventId","published","reportUrl","previewUrl","usesContentBuilder","config"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}},"patch":{"operationId":"update_event_report","summary":"Update the client report's configuration","description":"REPLACES the whole config (not a merge) — send the complete desired config each time (a null config from get_event_report means never configured: start from {}). The one exception is customContent, the Content Builder blocks: left out, the report keeps its blocks as they are, and null removes them; no two blocks may share an id (a kept list's included), and the blocks may come to at most 200 KB. Publishing state and the link are controlled by publish_event_report and never appear inside config. The capture-type names are edited in the dashboard only, and this call keeps them as they are. A report shows a Message & Button block (customContentHeader) or Content Builder blocks (customContent), never both: sending both is refused with 'validation_failed', and a customContentHeader while the report keeps its blocks is refused with 'conflict' (send customContent: null with it to switch). Button colours carry white text, so a colour too light for it is refused, as in the dashboard.","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"eventId":{"type":"string"},"published":{"type":"boolean"},"reportUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The shareable client link (stable until regenerated) — null until first publish"},"previewUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Operator-only preview link — renders even while unpublished. Never send it to the client: it carries its own token, and turning the report off does not stop it. null until the report is first saved or published"},"usesContentBuilder":{"type":"boolean","description":"true = the report shows Content Builder blocks (config.customContent), which take the place of the Message & Button block (config.customContentHeader)"},"config":{"anyOf":[{"type":"object","properties":{"title":{"description":"Default: the event name","type":"string","maxLength":120},"introMessage":{"description":"Personal note to the client","type":"string","maxLength":500},"accentColor":{"description":"#rrggbb. Left out, the gallery's main colour is stored — or black when that colour is too light for the report's white page","type":"string","maxLength":20},"logoUrl":{"description":"A logo for the report's heading, by https address. It is shown whole, up to 144 px tall, so any shape works: send it at least 288 px tall. Left out = the event logo; '' = no logo","type":"string","maxLength":2000},"showHeroBackground":{"description":"true = the gallery's background image (set_event_branding_image, slot 'gallery-background') fills the report's heading, darkened so the heading reads on it","type":"boolean"},"sections":{"description":"Which sections the report shows. A section left out is shown","type":"object","properties":{"trend":{"type":"boolean"},"captureTypes":{"type":"boolean"},"topPhotos":{"type":"boolean"},"shareChannels":{"type":"boolean"},"devices":{"type":"boolean"}},"additionalProperties":false},"customContentHeader":{"description":"The \"Message & Button\" block under the report's heading, above the figures: a heading, a message and one button, in a centred column. A report shows this or Content Builder blocks (customContent), not both","type":"object","properties":{"headerText":{"description":"The heading","type":"string","maxLength":200},"text":{"description":"The message under the heading","type":"string","maxLength":1000},"buttonText":{"type":"string","maxLength":120},"buttonLink":{"description":"Where the button goes. The button shows only with buttonText and a usable link","type":"string","maxLength":2048},"buttonColor":{"description":"#rrggbb behind the button's white text, so a very light colour is refused. Default: the accent colour","type":"string","maxLength":32}},"additionalProperties":false},"customContent":{"description":"The report's Content Builder blocks, in place of the Message & Button block; null = none. topBlocks show under the report's heading, above the figures; bottomBlocks close the page, after the top photos. Three block types: section_title (text: a bold, centred heading), rich_text (content: HTML shown as written, the width of the report; give images an absolute https address) and button (buttonText, buttonLink, buttonColor: a rounded button with white text that opens the link in a new tab; 2 or 3 buttons one after another share a line, longer runs stack, and a button without text or without a usable link is not shown; give links with https://, as one without a scheme opens as http://)","anyOf":[{"type":"object","properties":{"topBlocks":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"}},"required":["type"],"additionalProperties":{}}},"bottomBlocks":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"}},"required":["type"],"additionalProperties":{}}}},"required":["topBlocks","bottomBlocks"],"additionalProperties":false},{"type":"null"}]},"hiddenMetrics":{"type":"array","items":{"type":"string"}},"topPhotosCount":{"anyOf":[{"type":"number","const":4},{"type":"number","const":8},{"type":"number","const":12}]},"range":{"description":"YYYY-MM-DD; absent = all-time","type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"}},"additionalProperties":false},"hideBoothEventsBranding":{"description":"Always true once the report is saved: like the dashboard's, every save hides the Booth.Events name and logo on the report, so it carries only the operator's brand","type":"boolean"}},"additionalProperties":false},{"type":"null"}],"description":"null = never configured (defaults apply)"}},"required":["eventId","published","reportUrl","previewUrl","usesContentBuilder","config"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"config":{"type":"object","properties":{"title":{"description":"Default: the event name","type":"string","maxLength":120},"introMessage":{"description":"Personal note to the client","type":"string","maxLength":500},"accentColor":{"description":"#rrggbb. Left out, the gallery's main colour is stored — or black when that colour is too light for the report's white page","type":"string","maxLength":20},"logoUrl":{"description":"A logo for the report's heading, by https address. It is shown whole, up to 144 px tall, so any shape works: send it at least 288 px tall. Left out = the event logo; '' = no logo","type":"string","maxLength":2000},"showHeroBackground":{"description":"true = the gallery's background image (set_event_branding_image, slot 'gallery-background') fills the report's heading, darkened so the heading reads on it","type":"boolean"},"sections":{"description":"Which sections the report shows. A section left out is shown","type":"object","properties":{"trend":{"type":"boolean"},"captureTypes":{"type":"boolean"},"topPhotos":{"type":"boolean"},"shareChannels":{"type":"boolean"},"devices":{"type":"boolean"}}},"customContentHeader":{"description":"The \"Message & Button\" block under the report's heading, above the figures: a heading, a message and one button, in a centred column. A report shows this or Content Builder blocks (customContent), not both","type":"object","properties":{"headerText":{"description":"The heading","type":"string","maxLength":200},"text":{"description":"The message under the heading","type":"string","maxLength":1000},"buttonText":{"type":"string","maxLength":120},"buttonLink":{"description":"Where the button goes. The button shows only with buttonText and a usable link","type":"string","maxLength":2048},"buttonColor":{"description":"#rrggbb behind the button's white text, so a very light colour is refused. Default: the accent colour","type":"string","maxLength":32}}},"customContent":{"description":"The report's Content Builder blocks, in place of the Message & Button block. topBlocks show under the report's heading, above the figures; bottomBlocks close the page, after the top photos. Three block types: section_title (text: a bold, centred heading), rich_text (content: HTML shown as written, the width of the report; give images an absolute https address) and button (buttonText, buttonLink, buttonColor: a rounded button with white text that opens the link in a new tab; 2 or 3 buttons one after another share a line, longer runs stack, and a button without text or without a usable link is not shown; give links with https://, as one without a scheme opens as http://). Left out, the stored blocks stay as they are; null, or no blocks left, removes them; a list you send replaces that list, and a list left out stays. Blank blocks are not stored. Send a block's id to keep the block, and leave it out for a new one. A button without a buttonColor takes the accent colour. When accentColor changes, stored buttons still in the old accent follow it if you leave their list out; buttons you send keep the colour you send","anyOf":[{"type":"object","properties":{"topBlocks":{"description":"Under the report's heading, above the figures. Left out = the stored list stays","maxItems":40,"type":"array","items":{"oneOf":[{"type":"object","properties":{"id":{"description":"Leave out for a new block","type":"string","minLength":1,"maxLength":64},"type":{"type":"string","const":"section_title"},"text":{"type":"string","maxLength":200}},"required":["type","text"]},{"type":"object","properties":{"id":{"description":"Leave out for a new block","type":"string","minLength":1,"maxLength":64},"type":{"type":"string","const":"rich_text"},"content":{"type":"string","maxLength":25000,"description":"HTML, shown as written across the width of the report. Give images an absolute https address"}},"required":["type","content"]},{"type":"object","properties":{"id":{"description":"Leave out for a new block","type":"string","minLength":1,"maxLength":64},"type":{"type":"string","const":"button"},"buttonText":{"type":"string","maxLength":120},"buttonLink":{"type":"string","maxLength":2048},"buttonColor":{"description":"#rrggbb behind the button's white text, so a very light colour is refused. Default: the accent colour","type":"string","pattern":"^#[0-9a-fA-F]{6}$"}},"required":["type","buttonText","buttonLink"]}]}},"bottomBlocks":{"description":"At the end of the report, after the top photos. Left out = the stored list stays","maxItems":40,"type":"array","items":{"oneOf":[{"type":"object","properties":{"id":{"description":"Leave out for a new block","type":"string","minLength":1,"maxLength":64},"type":{"type":"string","const":"section_title"},"text":{"type":"string","maxLength":200}},"required":["type","text"]},{"type":"object","properties":{"id":{"description":"Leave out for a new block","type":"string","minLength":1,"maxLength":64},"type":{"type":"string","const":"rich_text"},"content":{"type":"string","maxLength":25000,"description":"HTML, shown as written across the width of the report. Give images an absolute https address"}},"required":["type","content"]},{"type":"object","properties":{"id":{"description":"Leave out for a new block","type":"string","minLength":1,"maxLength":64},"type":{"type":"string","const":"button"},"buttonText":{"type":"string","maxLength":120},"buttonLink":{"type":"string","maxLength":2048},"buttonColor":{"description":"#rrggbb behind the button's white text, so a very light colour is refused. Default: the accent colour","type":"string","pattern":"^#[0-9a-fA-F]{6}$"}},"required":["type","buttonText","buttonLink"]}]}}}},{"type":"null"}]},"hiddenMetrics":{"type":"array","items":{"type":"string"}},"topPhotosCount":{"anyOf":[{"type":"number","const":4},{"type":"number","const":8},{"type":"number","const":12}]},"range":{"description":"YYYY-MM-DD; absent = all-time","type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"}}}}}},"required":["config"]}}}}}},"/events/{eventId}/report/publish":{"post":{"operationId":"publish_event_report","summary":"Publish (or unpublish) the client report and get the shareable link","description":"First publish mints the capability link; regenerate:true revokes the old link and mints a new one. The returned reportUrl is what you email to the end client — it needs no sign-in. Note: the link may redirect (307) to a region-tagged URL, and a REVOKED link still answers HTTP 200 with an expired page — do not health-check links by status code.","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"eventId":{"type":"string"},"published":{"type":"boolean"},"reportUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The shareable client link (stable until regenerated) — null until first publish"},"previewUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Operator-only preview link — renders even while unpublished. Never send it to the client: it carries its own token, and turning the report off does not stop it. null until the report is first saved or published"},"usesContentBuilder":{"type":"boolean","description":"true = the report shows Content Builder blocks (config.customContent), which take the place of the Message & Button block (config.customContentHeader)"},"config":{"anyOf":[{"type":"object","properties":{"title":{"description":"Default: the event name","type":"string","maxLength":120},"introMessage":{"description":"Personal note to the client","type":"string","maxLength":500},"accentColor":{"description":"#rrggbb. Left out, the gallery's main colour is stored — or black when that colour is too light for the report's white page","type":"string","maxLength":20},"logoUrl":{"description":"A logo for the report's heading, by https address. It is shown whole, up to 144 px tall, so any shape works: send it at least 288 px tall. Left out = the event logo; '' = no logo","type":"string","maxLength":2000},"showHeroBackground":{"description":"true = the gallery's background image (set_event_branding_image, slot 'gallery-background') fills the report's heading, darkened so the heading reads on it","type":"boolean"},"sections":{"description":"Which sections the report shows. A section left out is shown","type":"object","properties":{"trend":{"type":"boolean"},"captureTypes":{"type":"boolean"},"topPhotos":{"type":"boolean"},"shareChannels":{"type":"boolean"},"devices":{"type":"boolean"}},"additionalProperties":false},"customContentHeader":{"description":"The \"Message & Button\" block under the report's heading, above the figures: a heading, a message and one button, in a centred column. A report shows this or Content Builder blocks (customContent), not both","type":"object","properties":{"headerText":{"description":"The heading","type":"string","maxLength":200},"text":{"description":"The message under the heading","type":"string","maxLength":1000},"buttonText":{"type":"string","maxLength":120},"buttonLink":{"description":"Where the button goes. The button shows only with buttonText and a usable link","type":"string","maxLength":2048},"buttonColor":{"description":"#rrggbb behind the button's white text, so a very light colour is refused. Default: the accent colour","type":"string","maxLength":32}},"additionalProperties":false},"customContent":{"description":"The report's Content Builder blocks, in place of the Message & Button block; null = none. topBlocks show under the report's heading, above the figures; bottomBlocks close the page, after the top photos. Three block types: section_title (text: a bold, centred heading), rich_text (content: HTML shown as written, the width of the report; give images an absolute https address) and button (buttonText, buttonLink, buttonColor: a rounded button with white text that opens the link in a new tab; 2 or 3 buttons one after another share a line, longer runs stack, and a button without text or without a usable link is not shown; give links with https://, as one without a scheme opens as http://)","anyOf":[{"type":"object","properties":{"topBlocks":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"}},"required":["type"],"additionalProperties":{}}},"bottomBlocks":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string"}},"required":["type"],"additionalProperties":{}}}},"required":["topBlocks","bottomBlocks"],"additionalProperties":false},{"type":"null"}]},"hiddenMetrics":{"type":"array","items":{"type":"string"}},"topPhotosCount":{"anyOf":[{"type":"number","const":4},{"type":"number","const":8},{"type":"number","const":12}]},"range":{"description":"YYYY-MM-DD; absent = all-time","type":"object","properties":{"from":{"type":"string"},"to":{"type":"string"}},"additionalProperties":false},"hideBoothEventsBranding":{"description":"Always true once the report is saved: like the dashboard's, every save hides the Booth.Events name and logo on the report, so it carries only the operator's brand","type":"boolean"}},"additionalProperties":false},{"type":"null"}],"description":"null = never configured (defaults apply)"}},"required":["eventId","published","reportUrl","previewUrl","usesContentBuilder","config"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"published":{"default":true,"type":"boolean"},"regenerate":{"default":false,"description":"Revoke the old link and mint a new one","type":"boolean"}}}}}}}},"/whoami":{"get":{"operationId":"whoami","summary":"The key, scope, account, event credits, and API version this request is authenticated as","description":"Call this first when unsure what you are connected to. scope tells you whether write operations will work (read keys never see them); apiVersion is what THIS request resolved to — a header/query override, the key pin, or the genesis version when the key has no pin (pinnedVersion null). Compare apiVersion to currentVersion to see whether the key is behind. eventCredits is the pay-per-event balance an event creation may draw on.","x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"keyPrefix":{"type":"string","description":"Display prefix of the key, e.g. 'be_live_a1b2c3d4'"},"keyName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Operator-assigned key label"},"scope":{"type":"string","enum":["read","read-write"]},"apiVersion":{"type":"string","description":"The API version this request resolved to"},"pinnedVersion":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The version pinned on the key doc; null = unpinned (resolves to genesis)"},"currentVersion":{"type":"string","description":"The newest API version"},"accountEmail":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Email of the account that owns the key"},"eventCredits":{"type":"object","properties":{"unused":{"type":"number","description":"Pay-per-event credits not yet spent"},"lastSpentAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 — when a credit was last SPENT (creating or extending an event), not bought; null = never"}},"required":["unused","lastSpentAt"],"additionalProperties":false,"description":"Pay-per-event credits. create_event and duplicate_event spend one when the account has no subscription quota to draw on; the operator buys more in the dashboard (Account → Billing). Check before creating events for an account without a subscription."}},"required":["keyPrefix","keyName","scope","apiVersion","pinnedVersion","currentVersion","accountEmail","eventCredits"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/profile":{"get":{"operationId":"get_profile","summary":"The connected account: a stable id, its name and its email","description":"Identifies WHICH Booth.Events account this connection belongs to — for a host that lets a person connect several accounts, and for an agent confirming it is acting on the right one. id never changes for an account; name and email are left out when the account has none. For the key, scope, credits and API version use whoami.","x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","description":"Opaque, stable identifier of the account"},"name":{"description":"The account name, as shown in its dashboard","type":"string"},"email":{"description":"The email address the account signs in with","type":"string"}},"required":["id"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/audit-events":{"get":{"operationId":"list_audit_events","summary":"The account's audit log (who did what, when), newest first","description":"Covers iPad, dashboard, system, and API actions. Filter by action (e.g. event.launch) OR by eventId — not both in one call.","parameters":[{"name":"limit","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","default":50,"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string"}},{"name":"action","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}},{"name":"eventId","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}},{"name":"since","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},{"name":"until","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"action":{"type":"string","description":"Dot-namespaced, e.g. 'event.launch', 'media.delete'"},"source":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"'ios' | 'web' | 'system' | 'api'"},"trigger":{"anyOf":[{"type":"string"},{"type":"null"}]},"at":{"anyOf":[{"type":"string"},{"type":"null"}]},"impersonatedBy":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Support-agent uid when the action happened during an impersonation session (SOL-2731); server-set, unforgeable"},"actor":{"anyOf":[{"type":"object","properties":{"uid":{"anyOf":[{"type":"string"},{"type":"null"}]},"deviceId":{"anyOf":[{"type":"string"},{"type":"null"}]},"email":{"anyOf":[{"type":"string"},{"type":"null"}]},"appVersion":{"anyOf":[{"type":"string"},{"type":"null"}]},"label":{"anyOf":[{"type":"string"},{"type":"null"}]},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A team member's or operator's name when they acted (stamped at the time); null = the owner"},"role":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"'admin' | 'operator' when a team member or operator acted; null = the owner"},"deviceName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The iPad's name, for rows the app wrote"}},"required":["uid","deviceId","email","appVersion","label","name","role","deviceName"],"additionalProperties":false},{"type":"null"}]},"location":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}]},"target":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}],"description":"What the action was done to: ids and names, by action"},"metadata":{"anyOf":[{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},{"type":"null"}],"description":"Details of the action, e.g. section and changedFields"}},"required":["id","action","source","trigger","at","impersonatedBy","actor","location","target","metadata"],"additionalProperties":false}},"hasMore":{"type":"boolean"},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["data","hasMore","nextCursor"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/communication-settings":{"get":{"operationId":"get_communication_settings","summary":"Get the account's default guest emails and text message","description":"The three messages guests receive, as the account sets them: `email` (the share email with the link to their photos), `emailZip` (the gallery-download email) and `sms` (the text message). These are the defaults for every event without its own override; get_event_communication_settings shows what one event actually sends. A section is null when the account has none stored.","x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"email":{"anyOf":[{"type":"object","properties":{"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The email's subject line. {{galleryName}} becomes the event's name and {{tenantName}} your business name. An email without a subject is not sent."},"from":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The Reply-To address: guests' replies go here (the email itself always comes from hello@shared.gallery). Without one, replies go to the account owner's login email (emails sent through this API included), except an email sent while a team member is signed in (on the dashboard or an iPad), which then has no Reply-To. An address equal to the account's own email is that default and is not stored."},"senderName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The name guests see as the sender, e.g. your business name. Without one they see 'shared.gallery'."},"topFirstLine":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The first line of text above the button, in grey."},"topSecondLine":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The second line of text above the button, in black."},"bottomLine":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The line of text under the button, in grey."},"body":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Only for an account with custom email (customEmail: true), where it replaces topFirstLine, topSecondLine and bottomLine: the whole email, as HTML, sent as written with its placeholders filled in. {{link}} is the link to the guest's photos (include it: nothing else adds it), {{galleryName}} the event's name, {{tenantName}} your business name, {{passcode}} 'Passcode: 1234' and {{passcodeOnly}} the passcode alone (both nothing when the gallery has no passcode or hides it in shares), {{linkWebsite}} the website link in the event or your profile, {{eventImage}} and {{tenantImage}} the event's and your image URLs, and {{color1}}, {{color2}} and {{color3}} the event's colours; any other {{…}} is removed. It cannot be cleared. null for any other account: its email is built from the lines."}},"required":["subject","from","senderName","topFirstLine","topSecondLine","bottomLine","body"],"additionalProperties":false},{"type":"null"}],"description":"The share email: what a guest gets when their photos are shared to their address, at the booth or from the gallery. Around the editable lines it always has the event logo at the top, the passcode when the gallery has one and shows it in shares, a 'View Photos' button in the gallery's primary colour, the link itself, and your logo and business name. The lines take {{galleryName}}, {{tenantName}}, {{link}}, {{passcode}} ('Passcode: 1234' or nothing) and {{passcodeOnly}} (the passcode alone). An account with custom email (customEmail) has no lines and no fixed design: its email is `body`."},"emailZip":{"anyOf":[{"type":"object","properties":{"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The email's subject line. {{galleryName}} becomes the event's name and {{tenantName}} your business name. An email without a subject is not sent."},"from":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The Reply-To address: guests' replies go here (the email itself always comes from hello@shared.gallery). Without one, replies go to the account owner's login email (emails sent through this API included), except an email sent while a team member is signed in (on the dashboard or an iPad), which then has no Reply-To. An address equal to the account's own email is that default and is not stored."},"senderName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The name guests see as the sender, e.g. your business name. Without one they see 'shared.gallery'."},"topFirstLine":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The first line of text above the button, in grey."},"topSecondLine":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The second line of text above the button, in black."},"bottomLine":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The line of text under the button, in grey."},"body":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Only for an account with custom email (customEmail: true), where it replaces topFirstLine, topSecondLine, bottomLine and linkText: the whole gallery-download email, as HTML, sent as written. {{link}} is the link to the gallery's downloads (include it: nothing else adds it), {{galleryName}} the event's name and {{tenantName}} your business name. It cannot be cleared. null for any other account: its email is built from the lines."},"linkText":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The button's text. Without one the button reads 'Open the Gallery', in the account's language."}},"required":["subject","from","senderName","topFirstLine","topSecondLine","bottomLine","body","linkText"],"additionalProperties":false},{"type":"null"}],"description":"The gallery-download email, sent from the dashboard's event page ('Email Gallery download link') to the address entered there. Its button opens the gallery with the ZIP downloads ready and no passcode needed. Its fixed parts (the default button text and the footer) are in the account's language. The lines take {{galleryName}}, {{tenantName}} and {{link}}. An account with custom email (customEmail) has no lines: its email is `body`."},"sms":{"anyOf":[{"type":"object","properties":{"body":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The text message, as sent. {{link}} becomes the link to the guest's photos, {{passcode}} 'Passcode: 1234' and {{passcodeOnly}} the passcode alone (both nothing when the gallery has no passcode or hides it in shares), {{galleryName}} the event's name and {{tenantName}} your business name; any other {{…}} is removed. The default, 'View your photos at {{link}} {{passcode}}', is sent instead when the text is empty, has no {{link}}, or has no {{passcode}} while the gallery shows a passcode. Unless your own Twilio account sends the texts (twilioConnected), the text is refused when it would go out as more than one SMS: built as it is sent, with the short link (about 36 characters, longer on your custom domain), the passcode, and the opt-out footer unless disableOptOut, but without {{galleryName}} and {{tenantName}}, it must fit in 160 characters of the plain SMS alphabet (GSM-7), or 70 when it has any other character, such as an emoji. Curly quotes and long dashes are made plain when sent, so they count as plain. Your default text is checked with a 4-character passcode and with none, one event's own text with that event's passcode. When the text with the names would take more than one SMS, the names are left out of it. With your own Twilio the text is sent as written, however long. A number in a country reached by WhatsApp gets a fixed WhatsApp message instead. Carriers may filter changed texts as spam; the default is the most reliable."},"disableOptOut":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true leaves off the footer ' From Booth.Events. Reply STOP to opt-out' that is otherwise added to every text Booth.Events sends. Texts sent through the account's own Twilio account (twilioConnected) never get it."}},"required":["body","disableOptOut"],"additionalProperties":false},{"type":"null"}],"description":"The text message a guest gets when their photos are shared to their phone number."},"twilioConnected":{"type":"boolean","description":"Whether the account's own Twilio account sends its texts (connected on the dashboard, not here). It is account-wide: every event's texts use it."},"customEmail":{"type":"boolean","description":"Whether the account writes each email whole, as HTML (`body`), rather than as lines of text around a fixed design. Set by Booth.Events (resellers and accounts given custom email), not here."}},"required":["email","emailZip","sms","twilioConnected","customEmail"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}},"patch":{"operationId":"update_communication_settings","summary":"Change the account's default guest emails and text message","description":"Changes the defaults for every event that has no version of its own for that section (update_event_communication_settings gives one event its own). Only the sections you send change. Within a section, the fields you send are laid over the stored ones: a field you leave out keeps its value, and an empty string clears it (an email's subject cannot be cleared, nor a custom email's body). Returns the account's settings.","x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"email":{"anyOf":[{"type":"object","properties":{"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The email's subject line. {{galleryName}} becomes the event's name and {{tenantName}} your business name. An email without a subject is not sent."},"from":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The Reply-To address: guests' replies go here (the email itself always comes from hello@shared.gallery). Without one, replies go to the account owner's login email (emails sent through this API included), except an email sent while a team member is signed in (on the dashboard or an iPad), which then has no Reply-To. An address equal to the account's own email is that default and is not stored."},"senderName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The name guests see as the sender, e.g. your business name. Without one they see 'shared.gallery'."},"topFirstLine":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The first line of text above the button, in grey."},"topSecondLine":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The second line of text above the button, in black."},"bottomLine":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The line of text under the button, in grey."},"body":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Only for an account with custom email (customEmail: true), where it replaces topFirstLine, topSecondLine and bottomLine: the whole email, as HTML, sent as written with its placeholders filled in. {{link}} is the link to the guest's photos (include it: nothing else adds it), {{galleryName}} the event's name, {{tenantName}} your business name, {{passcode}} 'Passcode: 1234' and {{passcodeOnly}} the passcode alone (both nothing when the gallery has no passcode or hides it in shares), {{linkWebsite}} the website link in the event or your profile, {{eventImage}} and {{tenantImage}} the event's and your image URLs, and {{color1}}, {{color2}} and {{color3}} the event's colours; any other {{…}} is removed. It cannot be cleared. null for any other account: its email is built from the lines."}},"required":["subject","from","senderName","topFirstLine","topSecondLine","bottomLine","body"],"additionalProperties":false},{"type":"null"}],"description":"The share email: what a guest gets when their photos are shared to their address, at the booth or from the gallery. Around the editable lines it always has the event logo at the top, the passcode when the gallery has one and shows it in shares, a 'View Photos' button in the gallery's primary colour, the link itself, and your logo and business name. The lines take {{galleryName}}, {{tenantName}}, {{link}}, {{passcode}} ('Passcode: 1234' or nothing) and {{passcodeOnly}} (the passcode alone). An account with custom email (customEmail) has no lines and no fixed design: its email is `body`."},"emailZip":{"anyOf":[{"type":"object","properties":{"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The email's subject line. {{galleryName}} becomes the event's name and {{tenantName}} your business name. An email without a subject is not sent."},"from":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The Reply-To address: guests' replies go here (the email itself always comes from hello@shared.gallery). Without one, replies go to the account owner's login email (emails sent through this API included), except an email sent while a team member is signed in (on the dashboard or an iPad), which then has no Reply-To. An address equal to the account's own email is that default and is not stored."},"senderName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The name guests see as the sender, e.g. your business name. Without one they see 'shared.gallery'."},"topFirstLine":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The first line of text above the button, in grey."},"topSecondLine":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The second line of text above the button, in black."},"bottomLine":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The line of text under the button, in grey."},"body":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Only for an account with custom email (customEmail: true), where it replaces topFirstLine, topSecondLine, bottomLine and linkText: the whole gallery-download email, as HTML, sent as written. {{link}} is the link to the gallery's downloads (include it: nothing else adds it), {{galleryName}} the event's name and {{tenantName}} your business name. It cannot be cleared. null for any other account: its email is built from the lines."},"linkText":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The button's text. Without one the button reads 'Open the Gallery', in the account's language."}},"required":["subject","from","senderName","topFirstLine","topSecondLine","bottomLine","body","linkText"],"additionalProperties":false},{"type":"null"}],"description":"The gallery-download email, sent from the dashboard's event page ('Email Gallery download link') to the address entered there. Its button opens the gallery with the ZIP downloads ready and no passcode needed. Its fixed parts (the default button text and the footer) are in the account's language. The lines take {{galleryName}}, {{tenantName}} and {{link}}. An account with custom email (customEmail) has no lines: its email is `body`."},"sms":{"anyOf":[{"type":"object","properties":{"body":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The text message, as sent. {{link}} becomes the link to the guest's photos, {{passcode}} 'Passcode: 1234' and {{passcodeOnly}} the passcode alone (both nothing when the gallery has no passcode or hides it in shares), {{galleryName}} the event's name and {{tenantName}} your business name; any other {{…}} is removed. The default, 'View your photos at {{link}} {{passcode}}', is sent instead when the text is empty, has no {{link}}, or has no {{passcode}} while the gallery shows a passcode. Unless your own Twilio account sends the texts (twilioConnected), the text is refused when it would go out as more than one SMS: built as it is sent, with the short link (about 36 characters, longer on your custom domain), the passcode, and the opt-out footer unless disableOptOut, but without {{galleryName}} and {{tenantName}}, it must fit in 160 characters of the plain SMS alphabet (GSM-7), or 70 when it has any other character, such as an emoji. Curly quotes and long dashes are made plain when sent, so they count as plain. Your default text is checked with a 4-character passcode and with none, one event's own text with that event's passcode. When the text with the names would take more than one SMS, the names are left out of it. With your own Twilio the text is sent as written, however long. A number in a country reached by WhatsApp gets a fixed WhatsApp message instead. Carriers may filter changed texts as spam; the default is the most reliable."},"disableOptOut":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true leaves off the footer ' From Booth.Events. Reply STOP to opt-out' that is otherwise added to every text Booth.Events sends. Texts sent through the account's own Twilio account (twilioConnected) never get it."}},"required":["body","disableOptOut"],"additionalProperties":false},{"type":"null"}],"description":"The text message a guest gets when their photos are shared to their phone number."},"twilioConnected":{"type":"boolean","description":"Whether the account's own Twilio account sends its texts (connected on the dashboard, not here). It is account-wide: every event's texts use it."},"customEmail":{"type":"boolean","description":"Whether the account writes each email whole, as HTML (`body`), rather than as lines of text around a fixed design. Set by Booth.Events (resellers and accounts given custom email), not here."}},"required":["email","emailZip","sms","twilioConnected","customEmail"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"email":{"description":"The share email: what a guest gets when their photos are shared to their address, at the booth or from the gallery. Around the editable lines it always has the event logo at the top, the passcode when the gallery has one and shows it in shares, a 'View Photos' button in the gallery's primary colour, the link itself, and your logo and business name. The lines take {{galleryName}}, {{tenantName}}, {{link}}, {{passcode}} ('Passcode: 1234' or nothing) and {{passcodeOnly}} (the passcode alone). An account with custom email (customEmail) has no lines and no fixed design: its email is `body`.","type":"object","properties":{"subject":{"type":"string","maxLength":300,"description":"The email's subject line. {{galleryName}} becomes the event's name and {{tenantName}} your business name. An email without a subject is not sent."},"from":{"description":"The Reply-To address: guests' replies go here (the email itself always comes from hello@shared.gallery). Without one, replies go to the account owner's login email (emails sent through this API included), except an email sent while a team member is signed in (on the dashboard or an iPad), which then has no Reply-To. An address equal to the account's own email is that default and is not stored. '' clears it.","anyOf":[{"type":"string","const":""},{"type":"string","maxLength":300,"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"}]},"senderName":{"type":"string","maxLength":200,"description":"The name guests see as the sender, e.g. your business name. Without one they see 'shared.gallery'."},"topFirstLine":{"type":"string","maxLength":500,"description":"The first line of text above the button, in grey."},"topSecondLine":{"type":"string","maxLength":500,"description":"The second line of text above the button, in black."},"bottomLine":{"type":"string","maxLength":500,"description":"The line of text under the button, in grey."},"body":{"type":"string","maxLength":100000,"description":"Only for an account with custom email (customEmail: true), where it replaces topFirstLine, topSecondLine and bottomLine: the whole email, as HTML, sent as written with its placeholders filled in. {{link}} is the link to the guest's photos (include it: nothing else adds it), {{galleryName}} the event's name, {{tenantName}} your business name, {{passcode}} 'Passcode: 1234' and {{passcodeOnly}} the passcode alone (both nothing when the gallery has no passcode or hides it in shares), {{linkWebsite}} the website link in the event or your profile, {{eventImage}} and {{tenantImage}} the event's and your image URLs, and {{color1}}, {{color2}} and {{color3}} the event's colours; any other {{…}} is removed. It cannot be cleared. null for any other account: its email is built from the lines."}}},"emailZip":{"description":"The gallery-download email, sent from the dashboard's event page ('Email Gallery download link') to the address entered there. Its button opens the gallery with the ZIP downloads ready and no passcode needed. Its fixed parts (the default button text and the footer) are in the account's language. The lines take {{galleryName}}, {{tenantName}} and {{link}}. An account with custom email (customEmail) has no lines: its email is `body`.","type":"object","properties":{"subject":{"type":"string","maxLength":300,"description":"The email's subject line. {{galleryName}} becomes the event's name and {{tenantName}} your business name. An email without a subject is not sent."},"from":{"description":"The Reply-To address: guests' replies go here (the email itself always comes from hello@shared.gallery). Without one, replies go to the account owner's login email (emails sent through this API included), except an email sent while a team member is signed in (on the dashboard or an iPad), which then has no Reply-To. An address equal to the account's own email is that default and is not stored. '' clears it.","anyOf":[{"type":"string","const":""},{"type":"string","maxLength":300,"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"}]},"senderName":{"type":"string","maxLength":200,"description":"The name guests see as the sender, e.g. your business name. Without one they see 'shared.gallery'."},"topFirstLine":{"type":"string","maxLength":500,"description":"The first line of text above the button, in grey."},"topSecondLine":{"type":"string","maxLength":500,"description":"The second line of text above the button, in black."},"bottomLine":{"type":"string","maxLength":500,"description":"The line of text under the button, in grey."},"body":{"description":"Only for an account with custom email (customEmail: true), where it replaces topFirstLine, topSecondLine, bottomLine and linkText: the whole gallery-download email, as HTML, sent as written. {{link}} is the link to the gallery's downloads (include it: nothing else adds it), {{galleryName}} the event's name and {{tenantName}} your business name. It cannot be cleared. null for any other account: its email is built from the lines.","type":"string","maxLength":100000},"linkText":{"description":"The button's text. Without one the button reads 'Open the Gallery', in the account's language.","type":"string","maxLength":200}}},"sms":{"description":"The text message a guest gets when their photos are shared to their phone number.","type":"object","properties":{"body":{"description":"The text message, as sent. {{link}} becomes the link to the guest's photos, {{passcode}} 'Passcode: 1234' and {{passcodeOnly}} the passcode alone (both nothing when the gallery has no passcode or hides it in shares), {{galleryName}} the event's name and {{tenantName}} your business name; any other {{…}} is removed. The default, 'View your photos at {{link}} {{passcode}}', is sent instead when the text is empty, has no {{link}}, or has no {{passcode}} while the gallery shows a passcode. Unless your own Twilio account sends the texts (twilioConnected), the text is refused when it would go out as more than one SMS: built as it is sent, with the short link (about 36 characters, longer on your custom domain), the passcode, and the opt-out footer unless disableOptOut, but without {{galleryName}} and {{tenantName}}, it must fit in 160 characters of the plain SMS alphabet (GSM-7), or 70 when it has any other character, such as an emoji. Curly quotes and long dashes are made plain when sent, so they count as plain. Your default text is checked with a 4-character passcode and with none, one event's own text with that event's passcode. When the text with the names would take more than one SMS, the names are left out of it. With your own Twilio the text is sent as written, however long. A number in a country reached by WhatsApp gets a fixed WhatsApp message instead. Carriers may filter changed texts as spam; the default is the most reliable.","type":"string","maxLength":1000},"disableOptOut":{"description":"true leaves off the footer ' From Booth.Events. Reply STOP to opt-out' that is otherwise added to every text Booth.Events sends. Texts sent through the account's own Twilio account (twilioConnected) never get it.","type":"boolean"}}}}}}}}}},"/events/{eventId}/communication-settings":{"get":{"operationId":"get_event_communication_settings","summary":"Get the guest emails and text message one event sends","description":"What this event's guests receive. For each of `email`, `emailZip` and `sms`: the event's own version when it has one, else the account's default (get_communication_settings). `overrides` says which are the event's own. An event's own section replaces the account's whole: no field of it comes from the account. Twilio is account-wide (twilioConnected).","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"The event (from list_events)"}}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"email":{"anyOf":[{"type":"object","properties":{"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The email's subject line. {{galleryName}} becomes the event's name and {{tenantName}} your business name. An email without a subject is not sent."},"from":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The Reply-To address: guests' replies go here (the email itself always comes from hello@shared.gallery). Without one, replies go to the account owner's login email (emails sent through this API included), except an email sent while a team member is signed in (on the dashboard or an iPad), which then has no Reply-To. An address equal to the account's own email is that default and is not stored."},"senderName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The name guests see as the sender, e.g. your business name. Without one they see 'shared.gallery'."},"topFirstLine":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The first line of text above the button, in grey."},"topSecondLine":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The second line of text above the button, in black."},"bottomLine":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The line of text under the button, in grey."},"body":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Only for an account with custom email (customEmail: true), where it replaces topFirstLine, topSecondLine and bottomLine: the whole email, as HTML, sent as written with its placeholders filled in. {{link}} is the link to the guest's photos (include it: nothing else adds it), {{galleryName}} the event's name, {{tenantName}} your business name, {{passcode}} 'Passcode: 1234' and {{passcodeOnly}} the passcode alone (both nothing when the gallery has no passcode or hides it in shares), {{linkWebsite}} the website link in the event or your profile, {{eventImage}} and {{tenantImage}} the event's and your image URLs, and {{color1}}, {{color2}} and {{color3}} the event's colours; any other {{…}} is removed. It cannot be cleared. null for any other account: its email is built from the lines."}},"required":["subject","from","senderName","topFirstLine","topSecondLine","bottomLine","body"],"additionalProperties":false},{"type":"null"}],"description":"The share email: what a guest gets when their photos are shared to their address, at the booth or from the gallery. Around the editable lines it always has the event logo at the top, the passcode when the gallery has one and shows it in shares, a 'View Photos' button in the gallery's primary colour, the link itself, and your logo and business name. The lines take {{galleryName}}, {{tenantName}}, {{link}}, {{passcode}} ('Passcode: 1234' or nothing) and {{passcodeOnly}} (the passcode alone). An account with custom email (customEmail) has no lines and no fixed design: its email is `body`."},"emailZip":{"anyOf":[{"type":"object","properties":{"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The email's subject line. {{galleryName}} becomes the event's name and {{tenantName}} your business name. An email without a subject is not sent."},"from":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The Reply-To address: guests' replies go here (the email itself always comes from hello@shared.gallery). Without one, replies go to the account owner's login email (emails sent through this API included), except an email sent while a team member is signed in (on the dashboard or an iPad), which then has no Reply-To. An address equal to the account's own email is that default and is not stored."},"senderName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The name guests see as the sender, e.g. your business name. Without one they see 'shared.gallery'."},"topFirstLine":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The first line of text above the button, in grey."},"topSecondLine":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The second line of text above the button, in black."},"bottomLine":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The line of text under the button, in grey."},"body":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Only for an account with custom email (customEmail: true), where it replaces topFirstLine, topSecondLine, bottomLine and linkText: the whole gallery-download email, as HTML, sent as written. {{link}} is the link to the gallery's downloads (include it: nothing else adds it), {{galleryName}} the event's name and {{tenantName}} your business name. It cannot be cleared. null for any other account: its email is built from the lines."},"linkText":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The button's text. Without one the button reads 'Open the Gallery', in the account's language."}},"required":["subject","from","senderName","topFirstLine","topSecondLine","bottomLine","body","linkText"],"additionalProperties":false},{"type":"null"}],"description":"The gallery-download email, sent from the dashboard's event page ('Email Gallery download link') to the address entered there. Its button opens the gallery with the ZIP downloads ready and no passcode needed. Its fixed parts (the default button text and the footer) are in the account's language. The lines take {{galleryName}}, {{tenantName}} and {{link}}. An account with custom email (customEmail) has no lines: its email is `body`."},"sms":{"anyOf":[{"type":"object","properties":{"body":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The text message, as sent. {{link}} becomes the link to the guest's photos, {{passcode}} 'Passcode: 1234' and {{passcodeOnly}} the passcode alone (both nothing when the gallery has no passcode or hides it in shares), {{galleryName}} the event's name and {{tenantName}} your business name; any other {{…}} is removed. The default, 'View your photos at {{link}} {{passcode}}', is sent instead when the text is empty, has no {{link}}, or has no {{passcode}} while the gallery shows a passcode. Unless your own Twilio account sends the texts (twilioConnected), the text is refused when it would go out as more than one SMS: built as it is sent, with the short link (about 36 characters, longer on your custom domain), the passcode, and the opt-out footer unless disableOptOut, but without {{galleryName}} and {{tenantName}}, it must fit in 160 characters of the plain SMS alphabet (GSM-7), or 70 when it has any other character, such as an emoji. Curly quotes and long dashes are made plain when sent, so they count as plain. Your default text is checked with a 4-character passcode and with none, one event's own text with that event's passcode. When the text with the names would take more than one SMS, the names are left out of it. With your own Twilio the text is sent as written, however long. A number in a country reached by WhatsApp gets a fixed WhatsApp message instead. Carriers may filter changed texts as spam; the default is the most reliable."},"disableOptOut":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true leaves off the footer ' From Booth.Events. Reply STOP to opt-out' that is otherwise added to every text Booth.Events sends. Texts sent through the account's own Twilio account (twilioConnected) never get it."}},"required":["body","disableOptOut"],"additionalProperties":false},{"type":"null"}],"description":"The text message a guest gets when their photos are shared to their phone number."},"twilioConnected":{"type":"boolean","description":"Whether the account's own Twilio account sends its texts (connected on the dashboard, not here). It is account-wide: every event's texts use it."},"customEmail":{"type":"boolean","description":"Whether the account writes each email whole, as HTML (`body`), rather than as lines of text around a fixed design. Set by Booth.Events (resellers and accounts given custom email), not here."},"overrides":{"type":"object","properties":{"email":{"type":"boolean"},"emailZip":{"type":"boolean"},"sms":{"type":"boolean"}},"required":["email","emailZip","sms"],"additionalProperties":false,"description":"Which sections are this event's own (true) rather than the account's (false). The values above are what the event sends either way."}},"required":["email","emailZip","sms","twilioConnected","customEmail","overrides"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}},"patch":{"operationId":"update_event_communication_settings","summary":"Give one event its own guest emails or text message","description":"Per section (`email`, `emailZip`, `sms`): leave it out to keep it as it is; send null to drop the event's own version, so the event sends the account's default again (get_communication_settings); send an object to give the event its own version. The object is laid over what the event sends now (its own version, else the account's): a field you leave out keeps its current value, an empty string clears a field (an email's subject cannot be cleared, nor a custom email's body), and the result is stored whole, so later changes to the account's default no longer reach that section of this event. Returns the same as get_event_communication_settings.","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"The event (from list_events)"}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"email":{"anyOf":[{"type":"object","properties":{"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The email's subject line. {{galleryName}} becomes the event's name and {{tenantName}} your business name. An email without a subject is not sent."},"from":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The Reply-To address: guests' replies go here (the email itself always comes from hello@shared.gallery). Without one, replies go to the account owner's login email (emails sent through this API included), except an email sent while a team member is signed in (on the dashboard or an iPad), which then has no Reply-To. An address equal to the account's own email is that default and is not stored."},"senderName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The name guests see as the sender, e.g. your business name. Without one they see 'shared.gallery'."},"topFirstLine":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The first line of text above the button, in grey."},"topSecondLine":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The second line of text above the button, in black."},"bottomLine":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The line of text under the button, in grey."},"body":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Only for an account with custom email (customEmail: true), where it replaces topFirstLine, topSecondLine and bottomLine: the whole email, as HTML, sent as written with its placeholders filled in. {{link}} is the link to the guest's photos (include it: nothing else adds it), {{galleryName}} the event's name, {{tenantName}} your business name, {{passcode}} 'Passcode: 1234' and {{passcodeOnly}} the passcode alone (both nothing when the gallery has no passcode or hides it in shares), {{linkWebsite}} the website link in the event or your profile, {{eventImage}} and {{tenantImage}} the event's and your image URLs, and {{color1}}, {{color2}} and {{color3}} the event's colours; any other {{…}} is removed. It cannot be cleared. null for any other account: its email is built from the lines."}},"required":["subject","from","senderName","topFirstLine","topSecondLine","bottomLine","body"],"additionalProperties":false},{"type":"null"}],"description":"The share email: what a guest gets when their photos are shared to their address, at the booth or from the gallery. Around the editable lines it always has the event logo at the top, the passcode when the gallery has one and shows it in shares, a 'View Photos' button in the gallery's primary colour, the link itself, and your logo and business name. The lines take {{galleryName}}, {{tenantName}}, {{link}}, {{passcode}} ('Passcode: 1234' or nothing) and {{passcodeOnly}} (the passcode alone). An account with custom email (customEmail) has no lines and no fixed design: its email is `body`."},"emailZip":{"anyOf":[{"type":"object","properties":{"subject":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The email's subject line. {{galleryName}} becomes the event's name and {{tenantName}} your business name. An email without a subject is not sent."},"from":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The Reply-To address: guests' replies go here (the email itself always comes from hello@shared.gallery). Without one, replies go to the account owner's login email (emails sent through this API included), except an email sent while a team member is signed in (on the dashboard or an iPad), which then has no Reply-To. An address equal to the account's own email is that default and is not stored."},"senderName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The name guests see as the sender, e.g. your business name. Without one they see 'shared.gallery'."},"topFirstLine":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The first line of text above the button, in grey."},"topSecondLine":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The second line of text above the button, in black."},"bottomLine":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The line of text under the button, in grey."},"body":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Only for an account with custom email (customEmail: true), where it replaces topFirstLine, topSecondLine, bottomLine and linkText: the whole gallery-download email, as HTML, sent as written. {{link}} is the link to the gallery's downloads (include it: nothing else adds it), {{galleryName}} the event's name and {{tenantName}} your business name. It cannot be cleared. null for any other account: its email is built from the lines."},"linkText":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The button's text. Without one the button reads 'Open the Gallery', in the account's language."}},"required":["subject","from","senderName","topFirstLine","topSecondLine","bottomLine","body","linkText"],"additionalProperties":false},{"type":"null"}],"description":"The gallery-download email, sent from the dashboard's event page ('Email Gallery download link') to the address entered there. Its button opens the gallery with the ZIP downloads ready and no passcode needed. Its fixed parts (the default button text and the footer) are in the account's language. The lines take {{galleryName}}, {{tenantName}} and {{link}}. An account with custom email (customEmail) has no lines: its email is `body`."},"sms":{"anyOf":[{"type":"object","properties":{"body":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The text message, as sent. {{link}} becomes the link to the guest's photos, {{passcode}} 'Passcode: 1234' and {{passcodeOnly}} the passcode alone (both nothing when the gallery has no passcode or hides it in shares), {{galleryName}} the event's name and {{tenantName}} your business name; any other {{…}} is removed. The default, 'View your photos at {{link}} {{passcode}}', is sent instead when the text is empty, has no {{link}}, or has no {{passcode}} while the gallery shows a passcode. Unless your own Twilio account sends the texts (twilioConnected), the text is refused when it would go out as more than one SMS: built as it is sent, with the short link (about 36 characters, longer on your custom domain), the passcode, and the opt-out footer unless disableOptOut, but without {{galleryName}} and {{tenantName}}, it must fit in 160 characters of the plain SMS alphabet (GSM-7), or 70 when it has any other character, such as an emoji. Curly quotes and long dashes are made plain when sent, so they count as plain. Your default text is checked with a 4-character passcode and with none, one event's own text with that event's passcode. When the text with the names would take more than one SMS, the names are left out of it. With your own Twilio the text is sent as written, however long. A number in a country reached by WhatsApp gets a fixed WhatsApp message instead. Carriers may filter changed texts as spam; the default is the most reliable."},"disableOptOut":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true leaves off the footer ' From Booth.Events. Reply STOP to opt-out' that is otherwise added to every text Booth.Events sends. Texts sent through the account's own Twilio account (twilioConnected) never get it."}},"required":["body","disableOptOut"],"additionalProperties":false},{"type":"null"}],"description":"The text message a guest gets when their photos are shared to their phone number."},"twilioConnected":{"type":"boolean","description":"Whether the account's own Twilio account sends its texts (connected on the dashboard, not here). It is account-wide: every event's texts use it."},"customEmail":{"type":"boolean","description":"Whether the account writes each email whole, as HTML (`body`), rather than as lines of text around a fixed design. Set by Booth.Events (resellers and accounts given custom email), not here."},"overrides":{"type":"object","properties":{"email":{"type":"boolean"},"emailZip":{"type":"boolean"},"sms":{"type":"boolean"}},"required":["email","emailZip","sms"],"additionalProperties":false,"description":"Which sections are this event's own (true) rather than the account's (false). The values above are what the event sends either way."}},"required":["email","emailZip","sms","twilioConnected","customEmail","overrides"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"email":{"description":"The share email: what a guest gets when their photos are shared to their address, at the booth or from the gallery. Around the editable lines it always has the event logo at the top, the passcode when the gallery has one and shows it in shares, a 'View Photos' button in the gallery's primary colour, the link itself, and your logo and business name. The lines take {{galleryName}}, {{tenantName}}, {{link}}, {{passcode}} ('Passcode: 1234' or nothing) and {{passcodeOnly}} (the passcode alone). An account with custom email (customEmail) has no lines and no fixed design: its email is `body`.","anyOf":[{"type":"object","properties":{"subject":{"type":"string","maxLength":300,"description":"The email's subject line. {{galleryName}} becomes the event's name and {{tenantName}} your business name. An email without a subject is not sent."},"from":{"description":"The Reply-To address: guests' replies go here (the email itself always comes from hello@shared.gallery). Without one, replies go to the account owner's login email (emails sent through this API included), except an email sent while a team member is signed in (on the dashboard or an iPad), which then has no Reply-To. An address equal to the account's own email is that default and is not stored. '' clears it.","anyOf":[{"type":"string","const":""},{"type":"string","maxLength":300,"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"}]},"senderName":{"type":"string","maxLength":200,"description":"The name guests see as the sender, e.g. your business name. Without one they see 'shared.gallery'."},"topFirstLine":{"type":"string","maxLength":500,"description":"The first line of text above the button, in grey."},"topSecondLine":{"type":"string","maxLength":500,"description":"The second line of text above the button, in black."},"bottomLine":{"type":"string","maxLength":500,"description":"The line of text under the button, in grey."},"body":{"type":"string","maxLength":100000,"description":"Only for an account with custom email (customEmail: true), where it replaces topFirstLine, topSecondLine and bottomLine: the whole email, as HTML, sent as written with its placeholders filled in. {{link}} is the link to the guest's photos (include it: nothing else adds it), {{galleryName}} the event's name, {{tenantName}} your business name, {{passcode}} 'Passcode: 1234' and {{passcodeOnly}} the passcode alone (both nothing when the gallery has no passcode or hides it in shares), {{linkWebsite}} the website link in the event or your profile, {{eventImage}} and {{tenantImage}} the event's and your image URLs, and {{color1}}, {{color2}} and {{color3}} the event's colours; any other {{…}} is removed. It cannot be cleared. null for any other account: its email is built from the lines."}}},{"type":"null"}]},"emailZip":{"description":"The gallery-download email, sent from the dashboard's event page ('Email Gallery download link') to the address entered there. Its button opens the gallery with the ZIP downloads ready and no passcode needed. Its fixed parts (the default button text and the footer) are in the account's language. The lines take {{galleryName}}, {{tenantName}} and {{link}}. An account with custom email (customEmail) has no lines: its email is `body`.","anyOf":[{"type":"object","properties":{"subject":{"type":"string","maxLength":300,"description":"The email's subject line. {{galleryName}} becomes the event's name and {{tenantName}} your business name. An email without a subject is not sent."},"from":{"description":"The Reply-To address: guests' replies go here (the email itself always comes from hello@shared.gallery). Without one, replies go to the account owner's login email (emails sent through this API included), except an email sent while a team member is signed in (on the dashboard or an iPad), which then has no Reply-To. An address equal to the account's own email is that default and is not stored. '' clears it.","anyOf":[{"type":"string","const":""},{"type":"string","maxLength":300,"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"}]},"senderName":{"type":"string","maxLength":200,"description":"The name guests see as the sender, e.g. your business name. Without one they see 'shared.gallery'."},"topFirstLine":{"type":"string","maxLength":500,"description":"The first line of text above the button, in grey."},"topSecondLine":{"type":"string","maxLength":500,"description":"The second line of text above the button, in black."},"bottomLine":{"type":"string","maxLength":500,"description":"The line of text under the button, in grey."},"body":{"description":"Only for an account with custom email (customEmail: true), where it replaces topFirstLine, topSecondLine, bottomLine and linkText: the whole gallery-download email, as HTML, sent as written. {{link}} is the link to the gallery's downloads (include it: nothing else adds it), {{galleryName}} the event's name and {{tenantName}} your business name. It cannot be cleared. null for any other account: its email is built from the lines.","type":"string","maxLength":100000},"linkText":{"description":"The button's text. Without one the button reads 'Open the Gallery', in the account's language.","type":"string","maxLength":200}}},{"type":"null"}]},"sms":{"description":"The text message a guest gets when their photos are shared to their phone number.","anyOf":[{"type":"object","properties":{"body":{"description":"The text message, as sent. {{link}} becomes the link to the guest's photos, {{passcode}} 'Passcode: 1234' and {{passcodeOnly}} the passcode alone (both nothing when the gallery has no passcode or hides it in shares), {{galleryName}} the event's name and {{tenantName}} your business name; any other {{…}} is removed. The default, 'View your photos at {{link}} {{passcode}}', is sent instead when the text is empty, has no {{link}}, or has no {{passcode}} while the gallery shows a passcode. Unless your own Twilio account sends the texts (twilioConnected), the text is refused when it would go out as more than one SMS: built as it is sent, with the short link (about 36 characters, longer on your custom domain), the passcode, and the opt-out footer unless disableOptOut, but without {{galleryName}} and {{tenantName}}, it must fit in 160 characters of the plain SMS alphabet (GSM-7), or 70 when it has any other character, such as an emoji. Curly quotes and long dashes are made plain when sent, so they count as plain. Your default text is checked with a 4-character passcode and with none, one event's own text with that event's passcode. When the text with the names would take more than one SMS, the names are left out of it. With your own Twilio the text is sent as written, however long. A number in a country reached by WhatsApp gets a fixed WhatsApp message instead. Carriers may filter changed texts as spam; the default is the most reliable.","type":"string","maxLength":1000},"disableOptOut":{"description":"true leaves off the footer ' From Booth.Events. Reply STOP to opt-out' that is otherwise added to every text Booth.Events sends. Texts sent through the account's own Twilio account (twilioConnected) never get it.","type":"boolean"}}},{"type":"null"}]}}}}}}}},"/account/branding":{"get":{"operationId":"get_account_branding","summary":"The account's name, logo, default colours and social links on every guest gallery","description":"What every event's guest gallery shows of the account: the bar across its top (the account's logo and name, linking to nameLink, with the sign-in button at the right), the social links shown on a gallery that has none of its own, and the colours a new event starts with. These belong to the account owner's profile (the dashboard's Account → Profile) and are account-wide: a change shows at once on every event's guest gallery, including galleries already shared with guests. An event's own look is separate: get_event_branding and get_gallery_settings.","x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","description":"The account's name, in the bar across the top of every guest gallery (beside the logo, linking to nameLink), in the gallery's page titles ('<event> | <name>'), as 'Prepared by' on client reports, and wherever a guest email or text uses {{tenantName}}"},"nameLink":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Where the account's name and logo in the gallery's top bar link to, opened in a new tab: usually the business's website. null = they are not a link"},"logoUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The account's logo: left of the name in the top bar of every guest gallery, and {{tenantImage}} in guest emails. Set with set_account_logo; null = none"},"colors":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"The account's three default colours, which each event created afterwards with create_event starts with: [0] the iPad's main buttons and the gallery's top bar, buttons and links (its primaryColor), [1] the iPad's shutter countdown, timers and borders (and the gallery's secondaryColor), [2] the iPad's icons and actions such as Share and Done. Existing events keep their own colours. Each '#rrggbb'; white text sits on them, so a very light colour is refused"},"socialUrls":{"anyOf":[{"type":"object","properties":{"facebookUrl":{"type":"string"},"twitterUrl":{"type":"string"},"linkedinUrl":{"type":"string"},"instagramUrl":{"type":"string"},"tiktokUrl":{"type":"string"},"youtubeUrl":{"type":"string"},"websiteUrl":{"type":"string"}},"required":["facebookUrl","twitterUrl","linkedinUrl","instagramUrl","tiktokUrl","youtubeUrl","websiteUrl"],"additionalProperties":false},{"type":"null"}],"description":"The account's default social links: round icons under the event name on every guest gallery that has no links of its own (a gallery's own are set with update_gallery_settings, where socialUrls null means \"show the account's\"). websiteUrl is also the link of the logo in guest emails and {{linkWebsite}}, unless the gallery has links of its own. Each '' (none) or an http(s) URL; twitterUrl is X. null = none"}},"required":["name","nameLink","logoUrl","colors","socialUrls"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}},"patch":{"operationId":"update_account_branding","summary":"Change the account's name, its link, default colours and social links","description":"Partial update: only the fields you send change. These belong to the account owner's profile (the dashboard's Account → Profile) and are account-wide: a change shows at once on every event's guest gallery, including galleries already shared with guests. name and nameLink are the bar across the top of each gallery; the name is also in page titles, client reports and {{tenantName}}. socialUrls are the default links, shown on every gallery without links of its own: send all seven keys (a key left out is stored blank); all blank, or null, removes them. colors only seed events created from now on with create_event: existing events keep theirs (change those with update_event and update_gallery_settings). The logo is set with set_account_logo. A gallery can hide the whole top bar (update_gallery_settings hideTenantHeader).","x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","description":"The account's name, in the bar across the top of every guest gallery (beside the logo, linking to nameLink), in the gallery's page titles ('<event> | <name>'), as 'Prepared by' on client reports, and wherever a guest email or text uses {{tenantName}}"},"nameLink":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Where the account's name and logo in the gallery's top bar link to, opened in a new tab: usually the business's website. null = they are not a link"},"logoUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The account's logo: left of the name in the top bar of every guest gallery, and {{tenantImage}} in guest emails. Set with set_account_logo; null = none"},"colors":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"The account's three default colours, which each event created afterwards with create_event starts with: [0] the iPad's main buttons and the gallery's top bar, buttons and links (its primaryColor), [1] the iPad's shutter countdown, timers and borders (and the gallery's secondaryColor), [2] the iPad's icons and actions such as Share and Done. Existing events keep their own colours. Each '#rrggbb'; white text sits on them, so a very light colour is refused"},"socialUrls":{"anyOf":[{"type":"object","properties":{"facebookUrl":{"type":"string"},"twitterUrl":{"type":"string"},"linkedinUrl":{"type":"string"},"instagramUrl":{"type":"string"},"tiktokUrl":{"type":"string"},"youtubeUrl":{"type":"string"},"websiteUrl":{"type":"string"}},"required":["facebookUrl","twitterUrl","linkedinUrl","instagramUrl","tiktokUrl","youtubeUrl","websiteUrl"],"additionalProperties":false},{"type":"null"}],"description":"The account's default social links: round icons under the event name on every guest gallery that has no links of its own (a gallery's own are set with update_gallery_settings, where socialUrls null means \"show the account's\"). websiteUrl is also the link of the logo in guest emails and {{linkWebsite}}, unless the gallery has links of its own. Each '' (none) or an http(s) URL; twitterUrl is X. null = none"}},"required":["name","nameLink","logoUrl","colors","socialUrls"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"description":"The account's name, in the bar across the top of every guest gallery (beside the logo, linking to nameLink), in the gallery's page titles ('<event> | <name>'), as 'Prepared by' on client reports, and wherever a guest email or text uses {{tenantName}}. 4 to 100 characters","type":"string","minLength":4,"maxLength":100},"nameLink":{"description":"Where the account's name and logo in the gallery's top bar link to, opened in a new tab: usually the business's website. null = they are not a link. An http(s) URL; null or '' removes it","anyOf":[{"type":"string","maxLength":2048},{"type":"null"}]},"colors":{"description":"The account's three default colours, which each event created afterwards with create_event starts with: [0] the iPad's main buttons and the gallery's top bar, buttons and links (its primaryColor), [1] the iPad's shutter countdown, timers and borders (and the gallery's secondaryColor), [2] the iPad's icons and actions such as Share and Done. Existing events keep their own colours. Each '#rrggbb'; white text sits on them, so a very light colour is refused","minItems":3,"maxItems":3,"type":"array","items":{"type":"string","pattern":"^#[0-9a-fA-F]{6}$"}},"socialUrls":{"description":"The account's default social links: round icons under the event name on every guest gallery that has no links of its own (a gallery's own are set with update_gallery_settings, where socialUrls null means \"show the account's\"). websiteUrl is also the link of the logo in guest emails and {{linkWebsite}}, unless the gallery has links of its own. Each '' (none) or an http(s) URL; twitterUrl is X. null = none","anyOf":[{"type":"object","properties":{"facebookUrl":{"type":"string","maxLength":2048},"twitterUrl":{"description":"X (formerly Twitter)","type":"string","maxLength":2048},"linkedinUrl":{"type":"string","maxLength":2048},"instagramUrl":{"type":"string","maxLength":2048},"tiktokUrl":{"type":"string","maxLength":2048},"youtubeUrl":{"type":"string","maxLength":2048},"websiteUrl":{"type":"string","maxLength":2048}}},{"type":"null"}]}}}}}}}},"/account/branding/logo":{"post":{"operationId":"set_account_logo","summary":"Set the account's logo, shown in the top bar of every guest gallery","description":"Replaces the account's logo. These belong to the account owner's profile (the dashboard's Account → Profile) and are account-wide: a change shows at once on every event's guest gallery, including galleries already shared with guests. Every gallery shows it in the bar across its top, left of the account name (both link to nameLink), on that gallery's primaryColor. It is also {{tenantImage}} in guest emails (the default share email draws it as a 60 px circle above the account name) and the account's picture in the dashboard. It is not an event logo: each event's own is set with set_event_branding_image.\n\nProvide a PNG or JPEG (up to 8 MB) via uploadPath (create_upload with purpose 'account-logo', then PUT) or a public https sourceUrl. Supply it SQUARE: the top bar draws it 56×56 px with slightly rounded corners, cropped to a square from the middle, so a wide logo loses its ends there. 512×512 px is plenty (at least 168×168 px keeps it sharp on a phone); anything over 1024 px on a side is scaled down to fit. It is stored as a JPEG, as the dashboard stores it, so transparency is lost: transparent areas turn white. Give the logo its own background, one that looks right on the galleries' primary colour.\n\nIt can be replaced but not removed, as in the dashboard. A gallery can hide the whole top bar (update_gallery_settings hideTenantHeader).","x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","description":"The account's name, in the bar across the top of every guest gallery (beside the logo, linking to nameLink), in the gallery's page titles ('<event> | <name>'), as 'Prepared by' on client reports, and wherever a guest email or text uses {{tenantName}}"},"nameLink":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Where the account's name and logo in the gallery's top bar link to, opened in a new tab: usually the business's website. null = they are not a link"},"logoUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The account's logo: left of the name in the top bar of every guest gallery, and {{tenantImage}} in guest emails. Set with set_account_logo; null = none"},"colors":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"The account's three default colours, which each event created afterwards with create_event starts with: [0] the iPad's main buttons and the gallery's top bar, buttons and links (its primaryColor), [1] the iPad's shutter countdown, timers and borders (and the gallery's secondaryColor), [2] the iPad's icons and actions such as Share and Done. Existing events keep their own colours. Each '#rrggbb'; white text sits on them, so a very light colour is refused"},"socialUrls":{"anyOf":[{"type":"object","properties":{"facebookUrl":{"type":"string"},"twitterUrl":{"type":"string"},"linkedinUrl":{"type":"string"},"instagramUrl":{"type":"string"},"tiktokUrl":{"type":"string"},"youtubeUrl":{"type":"string"},"websiteUrl":{"type":"string"}},"required":["facebookUrl","twitterUrl","linkedinUrl","instagramUrl","tiktokUrl","youtubeUrl","websiteUrl"],"additionalProperties":false},{"type":"null"}],"description":"The account's default social links: round icons under the event name on every guest gallery that has no links of its own (a gallery's own are set with update_gallery_settings, where socialUrls null means \"show the account's\"). websiteUrl is also the link of the logo in guest emails and {{linkWebsite}}, unless the gallery has links of its own. Each '' (none) or an http(s) URL; twitterUrl is X. null = none"}},"required":["name","nameLink","logoUrl","colors","socialUrls"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"uploadPath":{"description":"storagePath from create_upload (after PUTting the bytes)","type":"string"},"sourceUrl":{"description":"Public https URL fetched server-side (must not redirect) — alternative to uploadPath","type":"string"}}}}}}}},"/webhooks":{"get":{"operationId":"get_webhooks","summary":"Where outbound webhooks are delivered","x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"mediaWebhookUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Called when a guest uploads media (payload event: media.uploaded), with the eventId and eventIdUser it belongs to"},"sendWebhookUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Called when a guest's photo session is sent to them by email or text (payload event: session.sent). It names the gallery (galleryId), not the event: match it to the event whose galleryId it is (list_events, get_event)"}},"required":["mediaWebhookUrl","sendWebhookUrl"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}},"put":{"operationId":"set_webhooks","summary":"Set where outbound webhooks are delivered","description":"https URLs only; endpoints must answer without redirecting. Send null (or an empty string) to turn a webhook off. Deliveries POST JSON with an `event` discriminator (media.uploaded | session.sent), a unique `deliveryId`, and their own additive-only payload `apiVersion` — keys are added over time, never removed or renamed. Deliveries are not signed: to tell them from anyone else's calls, put a long random secret in the URL (e.g. ?token=…) and check it on arrival. Each is sent once and not retried, so a delivery your endpoint missed is not sent again: reconcile now and then with list_sessions and list_media. The URLs are the account's, for every event.","x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"mediaWebhookUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Called when a guest uploads media (payload event: media.uploaded), with the eventId and eventIdUser it belongs to"},"sendWebhookUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Called when a guest's photo session is sent to them by email or text (payload event: session.sent). It names the gallery (galleryId), not the event: match it to the event whose galleryId it is (list_events, get_event)"}},"required":["mediaWebhookUrl","sendWebhookUrl"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"mediaWebhookUrl":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]},"sendWebhookUrl":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}]}}}}}}}},"/devices":{"get":{"operationId":"list_devices","summary":"List the iPads/iPhones registered to the account","description":"Read-only. Every device record on the account with its licence state; when it was last heard from (lastSeen — its latest status report or app launch); who is signed in on it (holder); what it last reported about itself (status: battery, storage, Guided Access, printers with prints left, camera — sent every ~5 minutes while the app is open; null for app versions that do not report it); the event launched on it with how many files that device has uploaded into the event's gallery over about the last 90 days (launchedEvent); and how its booth is run (controls: whether its app runs by schedule, whether the booth is open, and the schedule's blocks) — including unlicensed records and the iPads operators hold, which the dashboard shows on the Team page rather than the Devices page. Join an operator's iPad to their grant via holder.memberUid = list_members' memberUid. Use get_device for a single device.","x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"licensed":{"type":"boolean"},"lastSeen":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601, when the device was last heard from: the later of its last app launch / sign-in and its last status report (status.reportedAt — every ~5 minutes while the app is open)"},"lastUpload":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601, when the device last requested an upload token for captured media — i.e. when it was last actually working at an event. Unlike lastSeen it is not touched by merely opening the app. null = never uploaded."},"deviceIdiom":{"anyOf":[{"type":"string","enum":["phone","pad"]},{"type":"null"}]},"preferredLocale":{"anyOf":[{"type":"string"},{"type":"null"}]},"payTerminal":{"anyOf":[{"type":"object","properties":{"provider":{"type":"string","description":"'nayax' | 'stripe' — the pairing selects the payment rail"},"terminalId":{"type":"string"},"pairedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["provider","terminalId","pairedAt"],"additionalProperties":false},{"type":"null"}],"description":"Paired card terminal for pay-per-use; null = not paired"},"holder":{"anyOf":[{"type":"object","properties":{"role":{"type":"string","enum":["owner","admin","operator"]},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"memberUid":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The team member or operator's memberUid in list_members; null for the owner"}},"required":["role","name","memberUid"],"additionalProperties":false},{"type":"null"}],"description":"Who is signed in on the device now — not whose licence it is. role operator = an operator's iPad, running only the events of their grant (list_members). null = not reported yet"},"status":{"anyOf":[{"type":"object","properties":{"reportedAt":{"type":"string","description":"ISO-8601, when the device sent this status"},"app":{"type":"object","properties":{"version":{"type":"string"},"build":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"state":{"anyOf":[{"type":"string","enum":["active","inactive"]},{"type":"null"}],"description":"'active' = the app is on screen; 'inactive' = the operator left it (another app, Settings, the Home Screen) and it reported on the way out. null for app builds before this field"},"stateChangedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601, when state last changed (the launch, or the last leave / return); null for app builds before this field"},"transitions":{"type":"array","items":{"type":"object","properties":{"state":{"type":"string","enum":["active","inactive"]},"at":{"type":"string","description":"ISO-8601, device clock"},"launch":{"type":"boolean","description":"true = the app was launched here (a kill in between shows as a gap before it)"}},"required":["state","at","launch"],"additionalProperties":false},"description":"The last 50 moves in and out of the app, oldest first, kept on the device across launches and offline stretches. Empty for app builds before this field"}},"required":["version","build","state","stateChangedAt","transitions"],"additionalProperties":false},"os":{"type":"object","properties":{"name":{"type":"string"},"version":{"type":"string"}},"required":["name","version"],"additionalProperties":false},"platform":{"type":"string","enum":["ipad","iphone","mac"]},"battery":{"anyOf":[{"type":"object","properties":{"level":{"anyOf":[{"type":"number","minimum":0,"maximum":1},{"type":"null"}],"description":"0–1; null when unknown"},"pluggedIn":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"full":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"trend":{"anyOf":[{"type":"string","enum":["gaining","steady","losing","unknown"]},{"type":"null"}],"description":"While plugged in and not full: 'gaining' charging, 'steady' not moving (e.g. a charge limit), 'losing' draining despite the power (e.g. an underpowered hub), 'unknown' not measured yet. null otherwise"}},"required":["level","pluggedIn","full","trend"],"additionalProperties":false},{"type":"null"}],"description":"null on a Mac"},"guidedAccess":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Whether Guided Access is on (the device is locked to the app); null on a Mac"},"storage":{"anyOf":[{"type":"object","properties":{"availableBytes":{"type":"number","description":"Free space for the app to use"},"totalBytes":{"type":"number"}},"required":["availableBytes","totalBytes"],"additionalProperties":false},{"type":"null"}],"description":"The device's own storage; null when unknown"},"printingEnabled":{"type":"boolean","description":"Whether printing is switched on in the app"},"printers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"route":{"type":"string","enum":["usb","printEvents","airprint","aircastPro","other"],"description":"How the device reaches the printer: 'usb' cable, 'printEvents' through Print.Events, 'airprint' AirPrint, 'aircastPro' through an AirCast Pro print server, 'other'"},"name":{"type":"string"},"model":{"type":"string"},"ok":{"type":"boolean","description":"false = the printer reports a problem (see problem)"},"problem":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The problem as the app shows it; null when ok"},"printsLeft":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Prints left on the loaded media; null when the printer does not report a number"},"printsLeftText":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Prints left, when the printer reports it as text rather than a number"},"mediaSize":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The loaded paper size as the printer names it"},"checkedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601, when the app last read the printer's state; null when not tracked"},"inRotation":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Whether the launched event prints on this printer: it has no problem and its paper fits one of the event's templates. null when no event is launched"},"notInRotationReason":{"anyOf":[{"type":"string","enum":["problem","paperDoesNotFit"]},{"type":"null"}],"description":"Why inRotation is false; null otherwise"}},"required":["id","route","name","model","ok","problem","printsLeft","printsLeftText","mediaSize","checkedAt","inRotation","notInRotationReason"],"additionalProperties":false},"description":"Every printer the app is set up with"},"camera":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["connected","gopro","builtIn"],"description":"'connected' = a camera connected to the device (DSLR / mirrorless), 'gopro' = a GoPro, 'builtIn' = the device's own camera"},"model":{"anyOf":[{"type":"string"},{"type":"null"}]},"manufacturer":{"anyOf":[{"type":"string"},{"type":"null"}]},"battery":{"anyOf":[{"type":"integer","minimum":0,"maximum":100},{"type":"null"}],"description":"Percent, in the camera's own steps (0, 25, 50, 75, 100); null when unknown"},"onMains":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Whether the camera runs on mains power"},"card":{"anyOf":[{"type":"object","properties":{"slot":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Card slot 1 or 2; null when unknown"},"capacityBytes":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"null when the camera does not report it"},"availableBytes":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"null when the camera does not report it"}},"required":["slot","capacityBytes","availableBytes"],"additionalProperties":false},{"type":"null"}],"description":"The memory card the camera saves to; null when unknown"},"shotsLeft":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]}},"required":["kind","model","manufacturer","battery","onMains","card","shotsLeft"],"additionalProperties":false},{"type":"null"}],"description":"The camera in use; null when none is set up"}},"required":["reportedAt","app","os","platform","battery","guidedAccess","storage","printingEnabled","printers","camera"],"additionalProperties":false},{"type":"null"}],"description":"What the device last reported about itself: battery, storage, Guided Access, printers and camera. The app reports every ~5 minutes while it is open, and sooner when something changes — an old reportedAt means the device is off, offline or the app is closed. null when the device's app version does not report status, it has not reported in the last 30 days, or the iPad is now signed in to an account that is not this one or one of its members."},"launchedEvent":{"anyOf":[{"type":"object","properties":{"eventId":{"type":"string","description":"The event, as in get_event"},"uploads":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Media files this device has uploaded to the event's gallery over about the last 90 days — a capture's original photos and its finished image count separately. Uploads older than that drop out of this count (the gallery keeps them), and files still waiting on the device are not counted yet. null when it cannot be counted, e.g. the event has no gallery"}},"required":["eventId","uploads"],"additionalProperties":false},{"type":"null"}],"description":"The event launched on the device, as the device last synced it; null when none is"},"controls":{"anyOf":[{"type":"object","properties":{"runsBy":{"anyOf":[{"type":"string","enum":["schedule","appTooOld"]},{"type":"null"}],"description":"Whether events can be put on this iPad from here, from the app build it last synced. 'schedule' = app build 776 or later (January 2026 on): set_device_schedule puts events on it, now or later, and open_device / close_device and pause_device_schedule / resume_device_schedule work. 'appTooOld' = an older app, which runs events only from the iPad itself: update the Booth.Events app on the iPad. null = the app has not synced its settings to this account yet: open it on the iPad first."},"openedClosedState":{"anyOf":[{"type":"string","enum":["open","closed"]},{"type":"null"}],"description":"Whether the booth is open, as the iPad last saved it or open_device / close_device last set it (runsBy schedule): 'open' = an event is on screen (or, with none launched, the iPad's event list); 'closed' = the account's closed screen, or the countdown screen before the next block starts. null = never set, which the iPad treats as open."},"schedulePaused":{"type":"boolean","description":"true = the iPad is not following its schedule (pause_device_schedule): the booth stays as it is, and open_device / close_device work."},"schedule":{"type":"array","items":{"type":"object","properties":{"eventId":{"type":"string","description":"The event the iPad runs during this block, as in get_event"},"stationType":{"type":"string","enum":["allInOne","camera","sharingStation"],"description":"What the iPad runs the event as — the dashboard's Station Type: 'allInOne' = Capture & Share mode (guests take their photos, then share and print them on this iPad), 'camera' = Capture Station mode (capture only), 'sharingStation' = Share Station mode (the dashboard's 'Sharing Station': guests find the photos taken on the event's other iPads and share or print them there). Capture Station mode (stationType 'camera' in set_device_schedule; the dashboard's Station Type 'Capture') is capture only: after each capture the iPad goes straight back to its home screen, with no share, print or gallery screen and no gallery button, so guests get none of the share options and nothing prints there, whatever the event's settings say. Their photos upload to the event's online gallery, where guests find, share and print them at a Share Station iPad (stationType 'sharingStation') or in the gallery itself. So Capture Station mode needs the online gallery (sharedGalleryEnabled): without it the photos reach no one."},"start":{"type":"string","description":"ISO-8601 (UTC): the instant the iPad launches the event, whatever its time zone"},"end":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601 (UTC): the instant the iPad closes the booth; null = the block runs until the schedule changes"}},"required":["eventId","stationType","start","end"],"additionalProperties":false},"description":"The schedule's blocks, in the order stored (set_device_schedule stores them by start time). Empty when there are none — and when one stored block is malformed, because the iPad then ignores the whole schedule."}},"required":["runsBy","openedClosedState","schedulePaused","schedule"],"additionalProperties":false},{"type":"null"}],"description":"How the booth on this iPad is run. null for an operator's iPad: its settings live in the operator's own account, where neither the dashboard nor this API reaches them."}},"required":["id","name","licensed","lastSeen","lastUpload","deviceIdiom","preferredLocale","payTerminal","holder","status","launchedEvent","controls"],"additionalProperties":false}},"hasMore":{"type":"boolean","const":false},"nextCursor":{"type":"null"}},"required":["data","hasMore","nextCursor"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/devices/{deviceId}":{"get":{"operationId":"get_device","summary":"Get one registered device","description":"Read-only. The fields list_devices returns, for one device: licence state, lastSeen, holder, its last self-reported status (battery, storage, Guided Access, printers, camera) the event launched on it with its upload count (launchedEvent), and how its booth is run (controls: schedule, open or closed).","parameters":[{"name":"deviceId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"Device id from list_devices"}}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"licensed":{"type":"boolean"},"lastSeen":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601, when the device was last heard from: the later of its last app launch / sign-in and its last status report (status.reportedAt — every ~5 minutes while the app is open)"},"lastUpload":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601, when the device last requested an upload token for captured media — i.e. when it was last actually working at an event. Unlike lastSeen it is not touched by merely opening the app. null = never uploaded."},"deviceIdiom":{"anyOf":[{"type":"string","enum":["phone","pad"]},{"type":"null"}]},"preferredLocale":{"anyOf":[{"type":"string"},{"type":"null"}]},"payTerminal":{"anyOf":[{"type":"object","properties":{"provider":{"type":"string","description":"'nayax' | 'stripe' — the pairing selects the payment rail"},"terminalId":{"type":"string"},"pairedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["provider","terminalId","pairedAt"],"additionalProperties":false},{"type":"null"}],"description":"Paired card terminal for pay-per-use; null = not paired"},"holder":{"anyOf":[{"type":"object","properties":{"role":{"type":"string","enum":["owner","admin","operator"]},"name":{"anyOf":[{"type":"string"},{"type":"null"}]},"memberUid":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The team member or operator's memberUid in list_members; null for the owner"}},"required":["role","name","memberUid"],"additionalProperties":false},{"type":"null"}],"description":"Who is signed in on the device now — not whose licence it is. role operator = an operator's iPad, running only the events of their grant (list_members). null = not reported yet"},"status":{"anyOf":[{"type":"object","properties":{"reportedAt":{"type":"string","description":"ISO-8601, when the device sent this status"},"app":{"type":"object","properties":{"version":{"type":"string"},"build":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"state":{"anyOf":[{"type":"string","enum":["active","inactive"]},{"type":"null"}],"description":"'active' = the app is on screen; 'inactive' = the operator left it (another app, Settings, the Home Screen) and it reported on the way out. null for app builds before this field"},"stateChangedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601, when state last changed (the launch, or the last leave / return); null for app builds before this field"},"transitions":{"type":"array","items":{"type":"object","properties":{"state":{"type":"string","enum":["active","inactive"]},"at":{"type":"string","description":"ISO-8601, device clock"},"launch":{"type":"boolean","description":"true = the app was launched here (a kill in between shows as a gap before it)"}},"required":["state","at","launch"],"additionalProperties":false},"description":"The last 50 moves in and out of the app, oldest first, kept on the device across launches and offline stretches. Empty for app builds before this field"}},"required":["version","build","state","stateChangedAt","transitions"],"additionalProperties":false},"os":{"type":"object","properties":{"name":{"type":"string"},"version":{"type":"string"}},"required":["name","version"],"additionalProperties":false},"platform":{"type":"string","enum":["ipad","iphone","mac"]},"battery":{"anyOf":[{"type":"object","properties":{"level":{"anyOf":[{"type":"number","minimum":0,"maximum":1},{"type":"null"}],"description":"0–1; null when unknown"},"pluggedIn":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"full":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"trend":{"anyOf":[{"type":"string","enum":["gaining","steady","losing","unknown"]},{"type":"null"}],"description":"While plugged in and not full: 'gaining' charging, 'steady' not moving (e.g. a charge limit), 'losing' draining despite the power (e.g. an underpowered hub), 'unknown' not measured yet. null otherwise"}},"required":["level","pluggedIn","full","trend"],"additionalProperties":false},{"type":"null"}],"description":"null on a Mac"},"guidedAccess":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Whether Guided Access is on (the device is locked to the app); null on a Mac"},"storage":{"anyOf":[{"type":"object","properties":{"availableBytes":{"type":"number","description":"Free space for the app to use"},"totalBytes":{"type":"number"}},"required":["availableBytes","totalBytes"],"additionalProperties":false},{"type":"null"}],"description":"The device's own storage; null when unknown"},"printingEnabled":{"type":"boolean","description":"Whether printing is switched on in the app"},"printers":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"route":{"type":"string","enum":["usb","printEvents","airprint","aircastPro","other"],"description":"How the device reaches the printer: 'usb' cable, 'printEvents' through Print.Events, 'airprint' AirPrint, 'aircastPro' through an AirCast Pro print server, 'other'"},"name":{"type":"string"},"model":{"type":"string"},"ok":{"type":"boolean","description":"false = the printer reports a problem (see problem)"},"problem":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The problem as the app shows it; null when ok"},"printsLeft":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Prints left on the loaded media; null when the printer does not report a number"},"printsLeftText":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Prints left, when the printer reports it as text rather than a number"},"mediaSize":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The loaded paper size as the printer names it"},"checkedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601, when the app last read the printer's state; null when not tracked"},"inRotation":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Whether the launched event prints on this printer: it has no problem and its paper fits one of the event's templates. null when no event is launched"},"notInRotationReason":{"anyOf":[{"type":"string","enum":["problem","paperDoesNotFit"]},{"type":"null"}],"description":"Why inRotation is false; null otherwise"}},"required":["id","route","name","model","ok","problem","printsLeft","printsLeftText","mediaSize","checkedAt","inRotation","notInRotationReason"],"additionalProperties":false},"description":"Every printer the app is set up with"},"camera":{"anyOf":[{"type":"object","properties":{"kind":{"type":"string","enum":["connected","gopro","builtIn"],"description":"'connected' = a camera connected to the device (DSLR / mirrorless), 'gopro' = a GoPro, 'builtIn' = the device's own camera"},"model":{"anyOf":[{"type":"string"},{"type":"null"}]},"manufacturer":{"anyOf":[{"type":"string"},{"type":"null"}]},"battery":{"anyOf":[{"type":"integer","minimum":0,"maximum":100},{"type":"null"}],"description":"Percent, in the camera's own steps (0, 25, 50, 75, 100); null when unknown"},"onMains":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"Whether the camera runs on mains power"},"card":{"anyOf":[{"type":"object","properties":{"slot":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Card slot 1 or 2; null when unknown"},"capacityBytes":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"null when the camera does not report it"},"availableBytes":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"null when the camera does not report it"}},"required":["slot","capacityBytes","availableBytes"],"additionalProperties":false},{"type":"null"}],"description":"The memory card the camera saves to; null when unknown"},"shotsLeft":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}]}},"required":["kind","model","manufacturer","battery","onMains","card","shotsLeft"],"additionalProperties":false},{"type":"null"}],"description":"The camera in use; null when none is set up"}},"required":["reportedAt","app","os","platform","battery","guidedAccess","storage","printingEnabled","printers","camera"],"additionalProperties":false},{"type":"null"}],"description":"What the device last reported about itself: battery, storage, Guided Access, printers and camera. The app reports every ~5 minutes while it is open, and sooner when something changes — an old reportedAt means the device is off, offline or the app is closed. null when the device's app version does not report status, it has not reported in the last 30 days, or the iPad is now signed in to an account that is not this one or one of its members."},"launchedEvent":{"anyOf":[{"type":"object","properties":{"eventId":{"type":"string","description":"The event, as in get_event"},"uploads":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"Media files this device has uploaded to the event's gallery over about the last 90 days — a capture's original photos and its finished image count separately. Uploads older than that drop out of this count (the gallery keeps them), and files still waiting on the device are not counted yet. null when it cannot be counted, e.g. the event has no gallery"}},"required":["eventId","uploads"],"additionalProperties":false},{"type":"null"}],"description":"The event launched on the device, as the device last synced it; null when none is"},"controls":{"anyOf":[{"type":"object","properties":{"runsBy":{"anyOf":[{"type":"string","enum":["schedule","appTooOld"]},{"type":"null"}],"description":"Whether events can be put on this iPad from here, from the app build it last synced. 'schedule' = app build 776 or later (January 2026 on): set_device_schedule puts events on it, now or later, and open_device / close_device and pause_device_schedule / resume_device_schedule work. 'appTooOld' = an older app, which runs events only from the iPad itself: update the Booth.Events app on the iPad. null = the app has not synced its settings to this account yet: open it on the iPad first."},"openedClosedState":{"anyOf":[{"type":"string","enum":["open","closed"]},{"type":"null"}],"description":"Whether the booth is open, as the iPad last saved it or open_device / close_device last set it (runsBy schedule): 'open' = an event is on screen (or, with none launched, the iPad's event list); 'closed' = the account's closed screen, or the countdown screen before the next block starts. null = never set, which the iPad treats as open."},"schedulePaused":{"type":"boolean","description":"true = the iPad is not following its schedule (pause_device_schedule): the booth stays as it is, and open_device / close_device work."},"schedule":{"type":"array","items":{"type":"object","properties":{"eventId":{"type":"string","description":"The event the iPad runs during this block, as in get_event"},"stationType":{"type":"string","enum":["allInOne","camera","sharingStation"],"description":"What the iPad runs the event as — the dashboard's Station Type: 'allInOne' = Capture & Share mode (guests take their photos, then share and print them on this iPad), 'camera' = Capture Station mode (capture only), 'sharingStation' = Share Station mode (the dashboard's 'Sharing Station': guests find the photos taken on the event's other iPads and share or print them there). Capture Station mode (stationType 'camera' in set_device_schedule; the dashboard's Station Type 'Capture') is capture only: after each capture the iPad goes straight back to its home screen, with no share, print or gallery screen and no gallery button, so guests get none of the share options and nothing prints there, whatever the event's settings say. Their photos upload to the event's online gallery, where guests find, share and print them at a Share Station iPad (stationType 'sharingStation') or in the gallery itself. So Capture Station mode needs the online gallery (sharedGalleryEnabled): without it the photos reach no one."},"start":{"type":"string","description":"ISO-8601 (UTC): the instant the iPad launches the event, whatever its time zone"},"end":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601 (UTC): the instant the iPad closes the booth; null = the block runs until the schedule changes"}},"required":["eventId","stationType","start","end"],"additionalProperties":false},"description":"The schedule's blocks, in the order stored (set_device_schedule stores them by start time). Empty when there are none — and when one stored block is malformed, because the iPad then ignores the whole schedule."}},"required":["runsBy","openedClosedState","schedulePaused","schedule"],"additionalProperties":false},{"type":"null"}],"description":"How the booth on this iPad is run. null for an operator's iPad: its settings live in the operator's own account, where neither the dashboard nor this API reaches them."}},"required":["id","name","licensed","lastSeen","lastUpload","deviceIdiom","preferredLocale","payTerminal","holder","status","launchedEvent","controls"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/devices/{deviceId}/schedule":{"put":{"operationId":"set_device_schedule","summary":"Put an event on an iPad, now or on a schedule","description":"This is how you set the event an iPad runs. To put an event on it NOW, send one block starting now with no end: the iPad launches that 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. To run events later, send a block per event with its start and end. Setting the schedule always turns it on: a schedule that was paused (pause_device_schedule; controls.schedulePaused) is resumed by the same write, and the iPad applies the new schedule at once, never the old one. (The dashboard asks you to turn a paused schedule back on before you can change it.) The dashboard's Schedule, for an iPad whose app runs by schedule (controls.runsBy 'schedule' in get_device). Replaces the whole schedule: each block runs one of your events on the iPad from `start` to `end`. This changes a LIVE booth as soon as the iPad is online; a guest in the middle of a capture finishes first. A block whose start has passed and whose end has not launches its event at once, in place of whatever is on screen. At a block's end the iPad shows your closed screen, and in the hours before the next block starts (4 by default) the countdown screen; both are designed on the dashboard's Devices page. While the schedule has blocks and is not paused, nobody at the iPad can leave the event. To run an event now, send one block starting now with no end. Before the first block starts the booth is closed: a schedule whose blocks all start later ends the event on screen at once and shows the closed screen (or the countdown, in the hours before the start), and nobody at the iPad can leave it until the first block starts or you pause or clear the schedule. So a booking made days ahead closes a booth that is running today. Send the schedule close to the event, or send the running event's block along with the later one (starting now, ending at least 10 minutes before it). Times are instants: send ISO-8601 with Z or an offset, in 2026 or later; a time without an offset is refused. The iPad starts and ends each block at that instant whatever its own time zone, and shows times to guests (the countdown, the closing time in its menu) in its own time zone. Times are kept to the minute. Blocks must not overlap, only the last may have no end, and each must start at least 10 minutes after the one before it ends. They are stored in start order. A block that has already ended only leaves the booth closed after it. [] clears the schedule: the booth stays as it is (a countdown becomes the closed screen) and open_device / close_device work again. An iPad a team member is signed in on is the account's own and works the same; an operator's iPad cannot be scheduled. Needs a Pro+ plan. Returns the controls as stored.","parameters":[{"name":"deviceId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"Device id from list_devices"}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"deviceId":{"type":"string"},"launchedEventId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The event launched on the iPad as its settings say now (list_devices' launchedEvent.eventId). An iPad that runs by schedule sets it itself when it launches a block's event."},"controls":{"type":"object","properties":{"runsBy":{"anyOf":[{"type":"string","enum":["schedule","appTooOld"]},{"type":"null"}],"description":"Whether events can be put on this iPad from here, from the app build it last synced. 'schedule' = app build 776 or later (January 2026 on): set_device_schedule puts events on it, now or later, and open_device / close_device and pause_device_schedule / resume_device_schedule work. 'appTooOld' = an older app, which runs events only from the iPad itself: update the Booth.Events app on the iPad. null = the app has not synced its settings to this account yet: open it on the iPad first."},"openedClosedState":{"anyOf":[{"type":"string","enum":["open","closed"]},{"type":"null"}],"description":"Whether the booth is open, as the iPad last saved it or open_device / close_device last set it (runsBy schedule): 'open' = an event is on screen (or, with none launched, the iPad's event list); 'closed' = the account's closed screen, or the countdown screen before the next block starts. null = never set, which the iPad treats as open."},"schedulePaused":{"type":"boolean","description":"true = the iPad is not following its schedule (pause_device_schedule): the booth stays as it is, and open_device / close_device work."},"schedule":{"type":"array","items":{"type":"object","properties":{"eventId":{"type":"string","description":"The event the iPad runs during this block, as in get_event"},"stationType":{"type":"string","enum":["allInOne","camera","sharingStation"],"description":"What the iPad runs the event as — the dashboard's Station Type: 'allInOne' = Capture & Share mode (guests take their photos, then share and print them on this iPad), 'camera' = Capture Station mode (capture only), 'sharingStation' = Share Station mode (the dashboard's 'Sharing Station': guests find the photos taken on the event's other iPads and share or print them there). Capture Station mode (stationType 'camera' in set_device_schedule; the dashboard's Station Type 'Capture') is capture only: after each capture the iPad goes straight back to its home screen, with no share, print or gallery screen and no gallery button, so guests get none of the share options and nothing prints there, whatever the event's settings say. Their photos upload to the event's online gallery, where guests find, share and print them at a Share Station iPad (stationType 'sharingStation') or in the gallery itself. So Capture Station mode needs the online gallery (sharedGalleryEnabled): without it the photos reach no one."},"start":{"type":"string","description":"ISO-8601 (UTC): the instant the iPad launches the event, whatever its time zone"},"end":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601 (UTC): the instant the iPad closes the booth; null = the block runs until the schedule changes"}},"required":["eventId","stationType","start","end"],"additionalProperties":false},"description":"The schedule's blocks, in the order stored (set_device_schedule stores them by start time). Empty when there are none — and when one stored block is malformed, because the iPad then ignores the whole schedule."}},"required":["runsBy","openedClosedState","schedulePaused","schedule"],"additionalProperties":false}},"required":["deviceId","launchedEventId","controls"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"schedule":{"maxItems":100,"type":"array","items":{"type":"object","properties":{"eventId":{"type":"string","minLength":1,"description":"The event to run: one of the account's own (list_events)"},"stationType":{"default":"allInOne","description":"What the iPad runs the event as — the dashboard's Station Type: 'allInOne' = Capture & Share mode (guests take their photos, then share and print them on this iPad), 'camera' = Capture Station mode (capture only), 'sharingStation' = Share Station mode (the dashboard's 'Sharing Station': guests find the photos taken on the event's other iPads and share or print them there). Capture Station mode (stationType 'camera' in set_device_schedule; the dashboard's Station Type 'Capture') is capture only: after each capture the iPad goes straight back to its home screen, with no share, print or gallery screen and no gallery button, so guests get none of the share options and nothing prints there, whatever the event's settings say. Their photos upload to the event's online gallery, where guests find, share and print them at a Share Station iPad (stationType 'sharingStation') or in the gallery itself. So Capture Station mode needs the online gallery (sharedGalleryEnabled): without it the photos reach no one. Default 'allInOne'.","type":"string","enum":["allInOne","camera","sharingStation"]},"start":{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$","description":"ISO-8601 with Z or an offset, e.g. 2026-10-12T18:00:00-04:00 (6 pm in New York), in 2026 or later: when the iPad launches the event"},"end":{"default":null,"description":"ISO-8601 with Z or an offset, in 2026 or later: when the iPad closes the booth. null (the default) = the block runs until the schedule changes; only the last block may have none","anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}]}},"required":["eventId","start"]},"description":"The whole schedule, up to 100 blocks; [] clears it"}},"required":["schedule"]}}}}}},"/devices/{deviceId}/open":{"post":{"operationId":"open_device","summary":"Open the booth on a device","description":"The dashboard's Open Device, for an iPad that runs by schedule. This changes a LIVE booth as soon as the iPad is online; a guest in the middle of a capture finishes first. In place of the closed or countdown screen the iPad shows the event last launched on it (launchedEventId) or, with none, its own event list, until close_device or the schedule closes it. Refused (conflict) while the schedule has blocks and is not paused: the schedule opens and closes the booth then. Needs a Pro+ plan.","parameters":[{"name":"deviceId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"Device id from list_devices"}}],"x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"deviceId":{"type":"string"},"launchedEventId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The event launched on the iPad as its settings say now (list_devices' launchedEvent.eventId). An iPad that runs by schedule sets it itself when it launches a block's event."},"controls":{"type":"object","properties":{"runsBy":{"anyOf":[{"type":"string","enum":["schedule","appTooOld"]},{"type":"null"}],"description":"Whether events can be put on this iPad from here, from the app build it last synced. 'schedule' = app build 776 or later (January 2026 on): set_device_schedule puts events on it, now or later, and open_device / close_device and pause_device_schedule / resume_device_schedule work. 'appTooOld' = an older app, which runs events only from the iPad itself: update the Booth.Events app on the iPad. null = the app has not synced its settings to this account yet: open it on the iPad first."},"openedClosedState":{"anyOf":[{"type":"string","enum":["open","closed"]},{"type":"null"}],"description":"Whether the booth is open, as the iPad last saved it or open_device / close_device last set it (runsBy schedule): 'open' = an event is on screen (or, with none launched, the iPad's event list); 'closed' = the account's closed screen, or the countdown screen before the next block starts. null = never set, which the iPad treats as open."},"schedulePaused":{"type":"boolean","description":"true = the iPad is not following its schedule (pause_device_schedule): the booth stays as it is, and open_device / close_device work."},"schedule":{"type":"array","items":{"type":"object","properties":{"eventId":{"type":"string","description":"The event the iPad runs during this block, as in get_event"},"stationType":{"type":"string","enum":["allInOne","camera","sharingStation"],"description":"What the iPad runs the event as — the dashboard's Station Type: 'allInOne' = Capture & Share mode (guests take their photos, then share and print them on this iPad), 'camera' = Capture Station mode (capture only), 'sharingStation' = Share Station mode (the dashboard's 'Sharing Station': guests find the photos taken on the event's other iPads and share or print them there). Capture Station mode (stationType 'camera' in set_device_schedule; the dashboard's Station Type 'Capture') is capture only: after each capture the iPad goes straight back to its home screen, with no share, print or gallery screen and no gallery button, so guests get none of the share options and nothing prints there, whatever the event's settings say. Their photos upload to the event's online gallery, where guests find, share and print them at a Share Station iPad (stationType 'sharingStation') or in the gallery itself. So Capture Station mode needs the online gallery (sharedGalleryEnabled): without it the photos reach no one."},"start":{"type":"string","description":"ISO-8601 (UTC): the instant the iPad launches the event, whatever its time zone"},"end":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601 (UTC): the instant the iPad closes the booth; null = the block runs until the schedule changes"}},"required":["eventId","stationType","start","end"],"additionalProperties":false},"description":"The schedule's blocks, in the order stored (set_device_schedule stores them by start time). Empty when there are none — and when one stored block is malformed, because the iPad then ignores the whole schedule."}},"required":["runsBy","openedClosedState","schedulePaused","schedule"],"additionalProperties":false}},"required":["deviceId","launchedEventId","controls"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/devices/{deviceId}/close":{"post":{"operationId":"close_device","summary":"Close the booth on a device","description":"The dashboard's Close Device, for an iPad that runs by schedule. This changes a LIVE booth as soon as the iPad is online; a guest in the middle of a capture finishes first. Guests see your closed screen at once (the event on screen ends for them) until open_device or the schedule opens the booth again. Refused (conflict) while the schedule has blocks and is not paused: the schedule opens and closes the booth then. Needs a Pro+ plan.","parameters":[{"name":"deviceId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"Device id from list_devices"}}],"x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"deviceId":{"type":"string"},"launchedEventId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The event launched on the iPad as its settings say now (list_devices' launchedEvent.eventId). An iPad that runs by schedule sets it itself when it launches a block's event."},"controls":{"type":"object","properties":{"runsBy":{"anyOf":[{"type":"string","enum":["schedule","appTooOld"]},{"type":"null"}],"description":"Whether events can be put on this iPad from here, from the app build it last synced. 'schedule' = app build 776 or later (January 2026 on): set_device_schedule puts events on it, now or later, and open_device / close_device and pause_device_schedule / resume_device_schedule work. 'appTooOld' = an older app, which runs events only from the iPad itself: update the Booth.Events app on the iPad. null = the app has not synced its settings to this account yet: open it on the iPad first."},"openedClosedState":{"anyOf":[{"type":"string","enum":["open","closed"]},{"type":"null"}],"description":"Whether the booth is open, as the iPad last saved it or open_device / close_device last set it (runsBy schedule): 'open' = an event is on screen (or, with none launched, the iPad's event list); 'closed' = the account's closed screen, or the countdown screen before the next block starts. null = never set, which the iPad treats as open."},"schedulePaused":{"type":"boolean","description":"true = the iPad is not following its schedule (pause_device_schedule): the booth stays as it is, and open_device / close_device work."},"schedule":{"type":"array","items":{"type":"object","properties":{"eventId":{"type":"string","description":"The event the iPad runs during this block, as in get_event"},"stationType":{"type":"string","enum":["allInOne","camera","sharingStation"],"description":"What the iPad runs the event as — the dashboard's Station Type: 'allInOne' = Capture & Share mode (guests take their photos, then share and print them on this iPad), 'camera' = Capture Station mode (capture only), 'sharingStation' = Share Station mode (the dashboard's 'Sharing Station': guests find the photos taken on the event's other iPads and share or print them there). Capture Station mode (stationType 'camera' in set_device_schedule; the dashboard's Station Type 'Capture') is capture only: after each capture the iPad goes straight back to its home screen, with no share, print or gallery screen and no gallery button, so guests get none of the share options and nothing prints there, whatever the event's settings say. Their photos upload to the event's online gallery, where guests find, share and print them at a Share Station iPad (stationType 'sharingStation') or in the gallery itself. So Capture Station mode needs the online gallery (sharedGalleryEnabled): without it the photos reach no one."},"start":{"type":"string","description":"ISO-8601 (UTC): the instant the iPad launches the event, whatever its time zone"},"end":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601 (UTC): the instant the iPad closes the booth; null = the block runs until the schedule changes"}},"required":["eventId","stationType","start","end"],"additionalProperties":false},"description":"The schedule's blocks, in the order stored (set_device_schedule stores them by start time). Empty when there are none — and when one stored block is malformed, because the iPad then ignores the whole schedule."}},"required":["runsBy","openedClosedState","schedulePaused","schedule"],"additionalProperties":false}},"required":["deviceId","launchedEventId","controls"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/devices/{deviceId}/schedule/pause":{"post":{"operationId":"pause_device_schedule","summary":"Pause a device's schedule","description":"The dashboard's switch that disables a device's schedule, for an iPad that runs by schedule. The iPad stops following its schedule and leaves the booth exactly as it is — an event on screen keeps running past its end — and someone at the iPad can leave the event again; open_device and close_device work while it is paused. resume_device_schedule turns the schedule back on, and so does set_device_schedule: setting a schedule resumes it. Needs a Pro+ plan.","parameters":[{"name":"deviceId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"Device id from list_devices"}}],"x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"deviceId":{"type":"string"},"launchedEventId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The event launched on the iPad as its settings say now (list_devices' launchedEvent.eventId). An iPad that runs by schedule sets it itself when it launches a block's event."},"controls":{"type":"object","properties":{"runsBy":{"anyOf":[{"type":"string","enum":["schedule","appTooOld"]},{"type":"null"}],"description":"Whether events can be put on this iPad from here, from the app build it last synced. 'schedule' = app build 776 or later (January 2026 on): set_device_schedule puts events on it, now or later, and open_device / close_device and pause_device_schedule / resume_device_schedule work. 'appTooOld' = an older app, which runs events only from the iPad itself: update the Booth.Events app on the iPad. null = the app has not synced its settings to this account yet: open it on the iPad first."},"openedClosedState":{"anyOf":[{"type":"string","enum":["open","closed"]},{"type":"null"}],"description":"Whether the booth is open, as the iPad last saved it or open_device / close_device last set it (runsBy schedule): 'open' = an event is on screen (or, with none launched, the iPad's event list); 'closed' = the account's closed screen, or the countdown screen before the next block starts. null = never set, which the iPad treats as open."},"schedulePaused":{"type":"boolean","description":"true = the iPad is not following its schedule (pause_device_schedule): the booth stays as it is, and open_device / close_device work."},"schedule":{"type":"array","items":{"type":"object","properties":{"eventId":{"type":"string","description":"The event the iPad runs during this block, as in get_event"},"stationType":{"type":"string","enum":["allInOne","camera","sharingStation"],"description":"What the iPad runs the event as — the dashboard's Station Type: 'allInOne' = Capture & Share mode (guests take their photos, then share and print them on this iPad), 'camera' = Capture Station mode (capture only), 'sharingStation' = Share Station mode (the dashboard's 'Sharing Station': guests find the photos taken on the event's other iPads and share or print them there). Capture Station mode (stationType 'camera' in set_device_schedule; the dashboard's Station Type 'Capture') is capture only: after each capture the iPad goes straight back to its home screen, with no share, print or gallery screen and no gallery button, so guests get none of the share options and nothing prints there, whatever the event's settings say. Their photos upload to the event's online gallery, where guests find, share and print them at a Share Station iPad (stationType 'sharingStation') or in the gallery itself. So Capture Station mode needs the online gallery (sharedGalleryEnabled): without it the photos reach no one."},"start":{"type":"string","description":"ISO-8601 (UTC): the instant the iPad launches the event, whatever its time zone"},"end":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601 (UTC): the instant the iPad closes the booth; null = the block runs until the schedule changes"}},"required":["eventId","stationType","start","end"],"additionalProperties":false},"description":"The schedule's blocks, in the order stored (set_device_schedule stores them by start time). Empty when there are none — and when one stored block is malformed, because the iPad then ignores the whole schedule."}},"required":["runsBy","openedClosedState","schedulePaused","schedule"],"additionalProperties":false}},"required":["deviceId","launchedEventId","controls"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/devices/{deviceId}/schedule/resume":{"post":{"operationId":"resume_device_schedule","summary":"Resume a device's paused schedule","description":"Turns a paused schedule back on, for an iPad that runs by schedule. This changes a LIVE booth as soon as the iPad is online; a guest in the middle of a capture finishes first. The iPad applies its schedule at once: the block running now launches its event in place of what is on screen, and between blocks the booth closes (closed or countdown screen). Needs a Pro+ plan.","parameters":[{"name":"deviceId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1,"description":"Device id from list_devices"}}],"x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"deviceId":{"type":"string"},"launchedEventId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The event launched on the iPad as its settings say now (list_devices' launchedEvent.eventId). An iPad that runs by schedule sets it itself when it launches a block's event."},"controls":{"type":"object","properties":{"runsBy":{"anyOf":[{"type":"string","enum":["schedule","appTooOld"]},{"type":"null"}],"description":"Whether events can be put on this iPad from here, from the app build it last synced. 'schedule' = app build 776 or later (January 2026 on): set_device_schedule puts events on it, now or later, and open_device / close_device and pause_device_schedule / resume_device_schedule work. 'appTooOld' = an older app, which runs events only from the iPad itself: update the Booth.Events app on the iPad. null = the app has not synced its settings to this account yet: open it on the iPad first."},"openedClosedState":{"anyOf":[{"type":"string","enum":["open","closed"]},{"type":"null"}],"description":"Whether the booth is open, as the iPad last saved it or open_device / close_device last set it (runsBy schedule): 'open' = an event is on screen (or, with none launched, the iPad's event list); 'closed' = the account's closed screen, or the countdown screen before the next block starts. null = never set, which the iPad treats as open."},"schedulePaused":{"type":"boolean","description":"true = the iPad is not following its schedule (pause_device_schedule): the booth stays as it is, and open_device / close_device work."},"schedule":{"type":"array","items":{"type":"object","properties":{"eventId":{"type":"string","description":"The event the iPad runs during this block, as in get_event"},"stationType":{"type":"string","enum":["allInOne","camera","sharingStation"],"description":"What the iPad runs the event as — the dashboard's Station Type: 'allInOne' = Capture & Share mode (guests take their photos, then share and print them on this iPad), 'camera' = Capture Station mode (capture only), 'sharingStation' = Share Station mode (the dashboard's 'Sharing Station': guests find the photos taken on the event's other iPads and share or print them there). Capture Station mode (stationType 'camera' in set_device_schedule; the dashboard's Station Type 'Capture') is capture only: after each capture the iPad goes straight back to its home screen, with no share, print or gallery screen and no gallery button, so guests get none of the share options and nothing prints there, whatever the event's settings say. Their photos upload to the event's online gallery, where guests find, share and print them at a Share Station iPad (stationType 'sharingStation') or in the gallery itself. So Capture Station mode needs the online gallery (sharedGalleryEnabled): without it the photos reach no one."},"start":{"type":"string","description":"ISO-8601 (UTC): the instant the iPad launches the event, whatever its time zone"},"end":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601 (UTC): the instant the iPad closes the booth; null = the block runs until the schedule changes"}},"required":["eventId","stationType","start","end"],"additionalProperties":false},"description":"The schedule's blocks, in the order stored (set_device_schedule stores them by start time). Empty when there are none — and when one stored block is malformed, because the iPad then ignores the whole schedule."}},"required":["runsBy","openedClosedState","schedulePaused","schedule"],"additionalProperties":false}},"required":["deviceId","launchedEventId","controls"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/members":{"get":{"operationId":"list_members","summary":"List the account's team members, operators and open invites","description":"members: everyone you granted access — admin members (logins) and operators (iPad-only) — including those whose access has ended; `status` says which. An operator's iPad appears in list_devices with holder.memberUid equal to their memberUid. invites: the OPEN invites only (not yet redeemed or revoked); one past inviteExpiresAt can no longer be redeemed.","x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"members":{"type":"array","items":{"type":"object","properties":{"memberUid":{"type":"string","description":"Also list_devices' holder.memberUid for an iPad they hold"},"role":{"type":"string","enum":["admin","operator"],"description":"admin = everything except billing/plan/team, with a login; operator = runs only the events in eventIds on an iPad, with no login and no dashboard"},"status":{"type":"string","enum":["active","expired","left","removed"],"description":"active = has access; expired = access ended on its own (expiresAt passed); left = the member ended it themselves; removed = you removed them. Ended rows are history and cannot be changed."},"eventIds":{"type":"array","items":{"type":"string"},"description":"Events an operator may run; [] for admin"},"expiresAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601 access end; null = no expiry. For an ended row, when it ended"},"leftDate":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When the member left (status left)"},"removedDate":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When you removed them (status removed)"},"displayName":{"anyOf":[{"type":"string"},{"type":"null"}]},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A member's login email; for an operator, the address they typed when joining"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["memberUid","role","status","eventIds","expiresAt","leftDate","removedDate","displayName","email","createdDate","modifiedDate"],"additionalProperties":false}},"invites":{"type":"array","items":{"type":"object","properties":{"inviteId":{"type":"string"},"role":{"type":"string","enum":["admin","operator"],"description":"admin = everything except billing/plan/team, with a login; operator = runs only the events in eventIds on an iPad, with no login and no dashboard"},"eventIds":{"type":"array","items":{"type":"string"},"description":"Events an operator may run; [] for admin"},"expiresAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Membership expiry applied at redemption"},"displayName":{"anyOf":[{"type":"string"},{"type":"null"}]},"tokenPrefix":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The start of a member invite's token; null for an operator code (none of it is shown)"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"inviteExpiresAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"After this the invite can no longer be redeemed"}},"required":["inviteId","role","eventIds","expiresAt","displayName","tokenPrefix","createdDate","inviteExpiresAt"],"additionalProperties":false}}},"required":["members","invites"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/members/invites":{"post":{"operationId":"create_member_invite","summary":"Invite a team member (a /join link) or an operator (a code for the iPad)","description":"admin members get a one-time `joinUrl` (and its `token`) and sign in on the dashboard with a login made for the invite. Operators run only the events in eventIds on an iPad, with no login: they get a six-character `code`, entered on a signed-out iPad under \"Join with an invite\". The token/code is returned ONCE and never again.\n\nemail: when given, Booth.Events emails the invite from hello@booth.events with your account email as reply-to. REQUIRED for operators — they receive our email (the QR code, the code and the steps). Optional for members: WITHOUT an email, YOU are responsible for getting the joinUrl to the person. emailSent false means no email went out — the invite still exists.\n\nOperators also need expiresAt (their access always ends — e.g. the event date + 7 days). Unredeemed invites expire after 7 days. Use list_members first to see who already has access.\n\nOperators can't run events that take payments: an operator invite naming an event with a paySetId is refused (validation_failed on eventIds).\n\nLimits per account, the dashboard's invites included: 50 invite emails a day (UTC) and 100 open invites (not yet redeemed or revoked; revoke_member_invite frees one). Past either, rate_limited — the daily one with Retry-After until midnight UTC.","x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"inviteId":{"type":"string"},"token":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Member invites: the token inside joinUrl, shown once. null for operators"},"code":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Operator invites: the six-character code for the iPad, shown once. null for members"},"tokenPrefix":{"anyOf":[{"type":"string"},{"type":"null"}]},"joinUrl":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Member invites: the link to send (the token rides in the URL fragment). null for operators — they never get a link"},"emailSent":{"type":"boolean","description":"Whether the invite email went out (false when no email was given)"}},"required":["inviteId","token","code","tokenPrefix","joinUrl","emailSent"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"role":{"type":"string","enum":["admin","operator"],"description":"admin = everything except billing/plan/team, with a login; operator = runs only the events in eventIds on an iPad, with no login and no dashboard"},"eventIds":{"maxItems":200,"type":"array","items":{"type":"string","minLength":1,"maxLength":200},"description":"Events an operator may run. Required (at least one) when role is operator; ignored for admin, who reaches every event."},"expiresAt":{"description":"When access ends, applied at redemption; must be in the future. Required for operators; for members omit or null = no expiry","anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}]},"displayName":{"description":"Label for who this is, e.g. \"Dave — Smith wedding\"","type":"string","maxLength":200},"email":{"description":"Where we email the invite. Required for operators; optional for members","type":"string","maxLength":320,"format":"email","pattern":"^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$"},"notes":{"description":"Free text for the invitee, shown on the join page or the iPad and in the email","type":"string","maxLength":2000}},"required":["role"]}}}}}},"/members/invites/{inviteId}":{"delete":{"operationId":"revoke_member_invite","summary":"Revoke a pending (unredeemed) invite","parameters":[{"name":"inviteId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"deleted":{"type":"boolean","const":true},"id":{"type":"string"}},"required":["deleted","id"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/members/{memberUid}":{"patch":{"operationId":"update_member","summary":"Change an admin member's access end or role","description":"Partial update of an ACTIVE admin member: expiresAt: an ISO date moves the access end, null clears it (no expiry), absent leaves it; role: a login role (never operator, which is another kind of identity). An operator's grant (events, access end) is fixed when the invite is created — remove the operator and invite again instead. Ended memberships (status expired, left or removed) cannot be changed; invite the person again.","parameters":[{"name":"memberUid","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"memberUid":{"type":"string","description":"Also list_devices' holder.memberUid for an iPad they hold"},"role":{"type":"string","enum":["admin","operator"],"description":"admin = everything except billing/plan/team, with a login; operator = runs only the events in eventIds on an iPad, with no login and no dashboard"},"status":{"type":"string","enum":["active","expired","left","removed"],"description":"active = has access; expired = access ended on its own (expiresAt passed); left = the member ended it themselves; removed = you removed them. Ended rows are history and cannot be changed."},"eventIds":{"type":"array","items":{"type":"string"},"description":"Events an operator may run; [] for admin"},"expiresAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601 access end; null = no expiry. For an ended row, when it ended"},"leftDate":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When the member left (status left)"},"removedDate":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When you removed them (status removed)"},"displayName":{"anyOf":[{"type":"string"},{"type":"null"}]},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A member's login email; for an operator, the address they typed when joining"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["memberUid","role","status","eventIds","expiresAt","leftDate","removedDate","displayName","email","createdDate","modifiedDate"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"role":{"type":"string","enum":["admin"],"description":"A login role: admin = everything except billing/plan/team"},"expiresAt":{"anyOf":[{"type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"},{"type":"null"}]}}}}}}},"delete":{"operationId":"remove_member","summary":"Remove a team member or operator (ends their access; the record stays)","description":"Ends the person's access on every surface within the hour (dashboard, iPad, galleries) and releases any iPad licence they held. The record stays as history with status removed (list_members). Calling it on a membership that has already ended (expired, left or removed) changes nothing and returns it as it is — records are never deleted through the API.","parameters":[{"name":"memberUid","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"memberUid":{"type":"string","description":"Also list_devices' holder.memberUid for an iPad they hold"},"role":{"type":"string","enum":["admin","operator"],"description":"admin = everything except billing/plan/team, with a login; operator = runs only the events in eventIds on an iPad, with no login and no dashboard"},"status":{"type":"string","enum":["active","expired","left","removed"],"description":"active = has access; expired = access ended on its own (expiresAt passed); left = the member ended it themselves; removed = you removed them. Ended rows are history and cannot be changed."},"eventIds":{"type":"array","items":{"type":"string"},"description":"Events an operator may run; [] for admin"},"expiresAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO-8601 access end; null = no expiry. For an ended row, when it ended"},"leftDate":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When the member left (status left)"},"removedDate":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"When you removed them (status removed)"},"displayName":{"anyOf":[{"type":"string"},{"type":"null"}]},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"A member's login email; for an operator, the address they typed when joining"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["memberUid","role","status","eventIds","expiresAt","leftDate","removedDate","displayName","email","createdDate","modifiedDate"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/pay-sets":{"get":{"operationId":"list_pay_sets","summary":"List your pay-per-use pricing sets","description":"Newest first. A pay set defines what guests pay at the booth (products, tiers, currency); assign one to an event via update_event paySetId.","parameters":[{"name":"limit","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","default":25,"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string"}}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"currency":{"type":"string","description":"Lowercase ISO 4217; must match the paired payment rail account currency"},"products":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["session","print","captureType","share"],"description":"What the guest pays for at the booth"},"enabled":{"type":"boolean"},"pricing":{"minItems":1,"type":"array","items":{"type":"object","properties":{"quantity":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"1 = single purchase; >1 = bundle option (session/print only)"},"price":{"type":"integer","minimum":1,"maximum":99999999,"description":"Integer MINOR units (e.g. cents) — 500 = $5.00; at most 99999999"}},"required":["quantity","price"],"additionalProperties":false},"description":"Multiple tiers = single-vs-bundle choice"},"captureTypes":{"description":"captureType products only: e.g. 'photo', 'video', 'aiPhoto'","type":"array","items":{"type":"string","minLength":1}},"payScreen":{"type":"object","properties":{"contentHtml":{"description":"Operator-authored guest screen HTML","type":"string","maxLength":50000},"backgroundColor":{"type":"string","maxLength":50}},"additionalProperties":false}},"required":["type","enabled","pricing"],"additionalProperties":false}},"tax":{"anyOf":[{"type":"object","properties":{"source":{"anyOf":[{"type":"string","enum":["manual","stripe"]},{"type":"null"}]},"rate":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Percent, e.g. 13 or 8.25; null or 0 = no tax"},"inclusive":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true = the price already includes tax; otherwise tax is added on top"},"label":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Guest-facing label; null = the localized default"}},"required":["source","rate","inclusive","label"],"additionalProperties":false},{"type":"null"}],"description":"Sales tax applied to every charge from this set; null = no tax configured"},"readerUnavailable":{"anyOf":[{"type":"object","properties":{"block":{"type":"boolean","description":"true = the gated action is refused when the card reader is unavailable"},"contentHtml":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Operator-authored screen shown when blocking"},"backgroundColor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["block","contentHtml","backgroundColor"],"additionalProperties":false},{"type":"null"}],"description":"What the booth does when the card reader is unavailable; null = the default (the action is free)"},"isPublic":{"type":"boolean"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","currency","products","tax","readerUnavailable","isPublic","createdDate","modifiedDate"],"additionalProperties":false}},"hasMore":{"type":"boolean"},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["data","hasMore","nextCursor"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}},"post":{"operationId":"create_pay_set","summary":"Create a pay-per-use pricing set","description":"Prices are integer MINOR units (500 = $5.00) and the currency must match the operator's payment-rail account currency or charges will be refused at the booth. Assign the set to an event with update_event paySetId.","x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"currency":{"type":"string","description":"Lowercase ISO 4217; must match the paired payment rail account currency"},"products":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["session","print","captureType","share"],"description":"What the guest pays for at the booth"},"enabled":{"type":"boolean"},"pricing":{"minItems":1,"type":"array","items":{"type":"object","properties":{"quantity":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"1 = single purchase; >1 = bundle option (session/print only)"},"price":{"type":"integer","minimum":1,"maximum":99999999,"description":"Integer MINOR units (e.g. cents) — 500 = $5.00; at most 99999999"}},"required":["quantity","price"],"additionalProperties":false},"description":"Multiple tiers = single-vs-bundle choice"},"captureTypes":{"description":"captureType products only: e.g. 'photo', 'video', 'aiPhoto'","type":"array","items":{"type":"string","minLength":1}},"payScreen":{"type":"object","properties":{"contentHtml":{"description":"Operator-authored guest screen HTML","type":"string","maxLength":50000},"backgroundColor":{"type":"string","maxLength":50}},"additionalProperties":false}},"required":["type","enabled","pricing"],"additionalProperties":false}},"tax":{"anyOf":[{"type":"object","properties":{"source":{"anyOf":[{"type":"string","enum":["manual","stripe"]},{"type":"null"}]},"rate":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Percent, e.g. 13 or 8.25; null or 0 = no tax"},"inclusive":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true = the price already includes tax; otherwise tax is added on top"},"label":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Guest-facing label; null = the localized default"}},"required":["source","rate","inclusive","label"],"additionalProperties":false},{"type":"null"}],"description":"Sales tax applied to every charge from this set; null = no tax configured"},"readerUnavailable":{"anyOf":[{"type":"object","properties":{"block":{"type":"boolean","description":"true = the gated action is refused when the card reader is unavailable"},"contentHtml":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Operator-authored screen shown when blocking"},"backgroundColor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["block","contentHtml","backgroundColor"],"additionalProperties":false},{"type":"null"}],"description":"What the booth does when the card reader is unavailable; null = the default (the action is free)"},"isPublic":{"type":"boolean"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","currency","products","tax","readerUnavailable","isPublic","createdDate","modifiedDate"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"currency":{"type":"string","pattern":"^[a-z]{3}$","description":"Lowercase ISO 4217, e.g. 'usd' — one currency per pay set"},"products":{"minItems":1,"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["session","print","captureType","share"],"description":"What the guest pays for at the booth"},"enabled":{"type":"boolean"},"pricing":{"minItems":1,"type":"array","items":{"type":"object","properties":{"quantity":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"1 = single purchase; >1 = bundle option (session/print only)"},"price":{"type":"integer","minimum":1,"maximum":99999999,"description":"Integer MINOR units (e.g. cents) — 500 = $5.00; at most 99999999"}},"required":["quantity","price"]},"description":"Multiple tiers = single-vs-bundle choice"},"captureTypes":{"description":"captureType products only: e.g. 'photo', 'video', 'aiPhoto'","type":"array","items":{"type":"string","minLength":1}},"payScreen":{"type":"object","properties":{"contentHtml":{"description":"Operator-authored guest screen HTML","type":"string","maxLength":50000},"backgroundColor":{"type":"string","maxLength":50}}}},"required":["type","enabled","pricing"]}}},"required":["name","currency","products"]}}}}}},"/pay-sets/{paySetId}":{"get":{"operationId":"get_pay_set","summary":"Get one pay set (yours, shared by your reseller, or a public one)","parameters":[{"name":"paySetId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"currency":{"type":"string","description":"Lowercase ISO 4217; must match the paired payment rail account currency"},"products":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["session","print","captureType","share"],"description":"What the guest pays for at the booth"},"enabled":{"type":"boolean"},"pricing":{"minItems":1,"type":"array","items":{"type":"object","properties":{"quantity":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"1 = single purchase; >1 = bundle option (session/print only)"},"price":{"type":"integer","minimum":1,"maximum":99999999,"description":"Integer MINOR units (e.g. cents) — 500 = $5.00; at most 99999999"}},"required":["quantity","price"],"additionalProperties":false},"description":"Multiple tiers = single-vs-bundle choice"},"captureTypes":{"description":"captureType products only: e.g. 'photo', 'video', 'aiPhoto'","type":"array","items":{"type":"string","minLength":1}},"payScreen":{"type":"object","properties":{"contentHtml":{"description":"Operator-authored guest screen HTML","type":"string","maxLength":50000},"backgroundColor":{"type":"string","maxLength":50}},"additionalProperties":false}},"required":["type","enabled","pricing"],"additionalProperties":false}},"tax":{"anyOf":[{"type":"object","properties":{"source":{"anyOf":[{"type":"string","enum":["manual","stripe"]},{"type":"null"}]},"rate":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Percent, e.g. 13 or 8.25; null or 0 = no tax"},"inclusive":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true = the price already includes tax; otherwise tax is added on top"},"label":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Guest-facing label; null = the localized default"}},"required":["source","rate","inclusive","label"],"additionalProperties":false},{"type":"null"}],"description":"Sales tax applied to every charge from this set; null = no tax configured"},"readerUnavailable":{"anyOf":[{"type":"object","properties":{"block":{"type":"boolean","description":"true = the gated action is refused when the card reader is unavailable"},"contentHtml":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Operator-authored screen shown when blocking"},"backgroundColor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["block","contentHtml","backgroundColor"],"additionalProperties":false},{"type":"null"}],"description":"What the booth does when the card reader is unavailable; null = the default (the action is free)"},"isPublic":{"type":"boolean"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","currency","products","tax","readerUnavailable","isPublic","createdDate","modifiedDate"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}},"patch":{"operationId":"update_pay_set","summary":"Update a pay set (name, currency, and/or the whole products array)","description":"Partial update: only the fields you send change. products replaces the whole array (send the complete list). The merged result is revalidated against the composition rules.","parameters":[{"name":"paySetId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"currency":{"type":"string","description":"Lowercase ISO 4217; must match the paired payment rail account currency"},"products":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["session","print","captureType","share"],"description":"What the guest pays for at the booth"},"enabled":{"type":"boolean"},"pricing":{"minItems":1,"type":"array","items":{"type":"object","properties":{"quantity":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"1 = single purchase; >1 = bundle option (session/print only)"},"price":{"type":"integer","minimum":1,"maximum":99999999,"description":"Integer MINOR units (e.g. cents) — 500 = $5.00; at most 99999999"}},"required":["quantity","price"],"additionalProperties":false},"description":"Multiple tiers = single-vs-bundle choice"},"captureTypes":{"description":"captureType products only: e.g. 'photo', 'video', 'aiPhoto'","type":"array","items":{"type":"string","minLength":1}},"payScreen":{"type":"object","properties":{"contentHtml":{"description":"Operator-authored guest screen HTML","type":"string","maxLength":50000},"backgroundColor":{"type":"string","maxLength":50}},"additionalProperties":false}},"required":["type","enabled","pricing"],"additionalProperties":false}},"tax":{"anyOf":[{"type":"object","properties":{"source":{"anyOf":[{"type":"string","enum":["manual","stripe"]},{"type":"null"}]},"rate":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Percent, e.g. 13 or 8.25; null or 0 = no tax"},"inclusive":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true = the price already includes tax; otherwise tax is added on top"},"label":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Guest-facing label; null = the localized default"}},"required":["source","rate","inclusive","label"],"additionalProperties":false},{"type":"null"}],"description":"Sales tax applied to every charge from this set; null = no tax configured"},"readerUnavailable":{"anyOf":[{"type":"object","properties":{"block":{"type":"boolean","description":"true = the gated action is refused when the card reader is unavailable"},"contentHtml":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Operator-authored screen shown when blocking"},"backgroundColor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["block","contentHtml","backgroundColor"],"additionalProperties":false},{"type":"null"}],"description":"What the booth does when the card reader is unavailable; null = the default (the action is free)"},"isPublic":{"type":"boolean"},"createdDate":{"anyOf":[{"type":"string"},{"type":"null"}]},"modifiedDate":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","name","currency","products","tax","readerUnavailable","isPublic","createdDate","modifiedDate"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"currency":{"type":"string","pattern":"^[a-z]{3}$"},"products":{"minItems":1,"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["session","print","captureType","share"],"description":"What the guest pays for at the booth"},"enabled":{"type":"boolean"},"pricing":{"minItems":1,"type":"array","items":{"type":"object","properties":{"quantity":{"type":"integer","minimum":1,"maximum":9007199254740991,"description":"1 = single purchase; >1 = bundle option (session/print only)"},"price":{"type":"integer","minimum":1,"maximum":99999999,"description":"Integer MINOR units (e.g. cents) — 500 = $5.00; at most 99999999"}},"required":["quantity","price"]},"description":"Multiple tiers = single-vs-bundle choice"},"captureTypes":{"description":"captureType products only: e.g. 'photo', 'video', 'aiPhoto'","type":"array","items":{"type":"string","minLength":1}},"payScreen":{"type":"object","properties":{"contentHtml":{"description":"Operator-authored guest screen HTML","type":"string","maxLength":50000},"backgroundColor":{"type":"string","maxLength":50}}}},"required":["type","enabled","pricing"]}}}}}}}},"delete":{"operationId":"delete_pay_set","summary":"Delete a pay set (refused while events still use it)","parameters":[{"name":"paySetId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"deleted":{"type":"boolean","const":true},"id":{"type":"string"}},"required":["deleted","id"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/pay-transactions":{"get":{"operationId":"list_pay_transactions","summary":"List guest payments taken at your booths, newest first","description":"The pay-per-use revenue log — one row per payment attempt on either rail, including charges a team member's iPad took for the account (initiator names them; null = the owner's own iPad). Filter by eventId (e.g. \"how much did we take at this event?\") OR by status — not both in one call. Amounts are integer minor units: amount is the TOTAL charged, and subtotalAmount/taxAmount break it out whenever the pay set applied tax (all null when it did not). Only status \"approved\" rows took money.","parameters":[{"name":"limit","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","default":50,"type":"integer","minimum":1,"maximum":100}},{"name":"cursor","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string"}},{"name":"eventId","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}},{"name":"status","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","enum":["pending","approved","declined","timeout"]}},{"name":"createdFrom","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}},{"name":"createdTo","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","format":"date-time","pattern":"^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"}}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"provider":{"type":"string","description":"'nayax' | 'stripe'"},"eventId":{"anyOf":[{"type":"string"},{"type":"null"}]},"eventName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Event name as it was at charge time"},"deviceId":{"anyOf":[{"type":"string"},{"type":"null"}]},"deviceName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Device (iPad) name as it was at charge time"},"initiator":{"anyOf":[{"type":"object","properties":{"role":{"anyOf":[{"type":"string","enum":["admin","operator"]},{"type":"null"}],"description":"Their role at charge time; null for a role this version does not know"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Their name as it was at charge time"},"memberUid":{"type":"string","description":"Their memberUid in list_members"}},"required":["role","name","memberUid"],"additionalProperties":false},{"type":"null"}],"description":"The team member whose iPad took the charge for the account. null = taken on the account owner's own iPad. The money is the account's either way."},"terminalId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The card terminal that took the payment"},"paySetId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The pay set that priced this charge; null on test charges"},"paySetName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pay set name as it was at charge time"},"productType":{"type":"string","description":"session | print | captureType | share, or 'test'"},"quantity":{"type":"number"},"amount":{"type":"number","description":"Integer MINOR units (e.g. cents) — the TOTAL charged (subtotal + tax)"},"subtotalAmount":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Pre-tax amount in minor units; null when the charge carried no tax"},"taxAmount":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Tax portion in minor units; null when untaxed"},"taxRate":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Tax percent applied at charge time (e.g. 13)"},"taxInclusive":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true when the price already included tax (amount == subtotalAmount + taxAmount either way)"},"taxLabel":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Guest-facing tax label at charge time (e.g. 'HST')"},"taxSource":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"'manual' | 'stripe'"},"currency":{"type":"string"},"status":{"type":"string","enum":["pending","approved","declined","timeout"]},"isTest":{"type":"boolean"},"failureCode":{"anyOf":[{"type":"string"},{"type":"null"}]},"cardLast4":{"anyOf":[{"type":"string"},{"type":"null"}]},"cardBrand":{"anyOf":[{"type":"string"},{"type":"null"}]},"authCode":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Nayax only"},"nayaxRRN":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Nayax only"},"stripePaymentIntentId":{"anyOf":[{"type":"string"},{"type":"null"}]},"stripeChargeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"stripeReaderId":{"anyOf":[{"type":"string"},{"type":"null"}]},"tenantId":{"anyOf":[{"type":"string"},{"type":"null"}]},"galleryId":{"anyOf":[{"type":"string"},{"type":"null"}]},"guestSessionUuid":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The booth-minted session id this charge belongs to. Match it against a gallery session (sessions carry the same value) to find the photos; null on older charges and test charges."},"createdAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"completedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","provider","eventId","eventName","deviceId","deviceName","initiator","terminalId","paySetId","paySetName","productType","quantity","amount","subtotalAmount","taxAmount","taxRate","taxInclusive","taxLabel","taxSource","currency","status","isTest","failureCode","cardLast4","cardBrand","authCode","nayaxRRN","stripePaymentIntentId","stripeChargeId","stripeReaderId","tenantId","galleryId","guestSessionUuid","createdAt","completedAt"],"additionalProperties":false}},"hasMore":{"type":"boolean"},"nextCursor":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["data","hasMore","nextCursor"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/pay-transactions/{payTransactionId}":{"get":{"operationId":"get_pay_transaction","summary":"Get one guest payment","parameters":[{"name":"payTransactionId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string"},"provider":{"type":"string","description":"'nayax' | 'stripe'"},"eventId":{"anyOf":[{"type":"string"},{"type":"null"}]},"eventName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Event name as it was at charge time"},"deviceId":{"anyOf":[{"type":"string"},{"type":"null"}]},"deviceName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Device (iPad) name as it was at charge time"},"initiator":{"anyOf":[{"type":"object","properties":{"role":{"anyOf":[{"type":"string","enum":["admin","operator"]},{"type":"null"}],"description":"Their role at charge time; null for a role this version does not know"},"name":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Their name as it was at charge time"},"memberUid":{"type":"string","description":"Their memberUid in list_members"}},"required":["role","name","memberUid"],"additionalProperties":false},{"type":"null"}],"description":"The team member whose iPad took the charge for the account. null = taken on the account owner's own iPad. The money is the account's either way."},"terminalId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The card terminal that took the payment"},"paySetId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The pay set that priced this charge; null on test charges"},"paySetName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pay set name as it was at charge time"},"productType":{"type":"string","description":"session | print | captureType | share, or 'test'"},"quantity":{"type":"number"},"amount":{"type":"number","description":"Integer MINOR units (e.g. cents) — the TOTAL charged (subtotal + tax)"},"subtotalAmount":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Pre-tax amount in minor units; null when the charge carried no tax"},"taxAmount":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Tax portion in minor units; null when untaxed"},"taxRate":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Tax percent applied at charge time (e.g. 13)"},"taxInclusive":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"true when the price already included tax (amount == subtotalAmount + taxAmount either way)"},"taxLabel":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Guest-facing tax label at charge time (e.g. 'HST')"},"taxSource":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"'manual' | 'stripe'"},"currency":{"type":"string"},"status":{"type":"string","enum":["pending","approved","declined","timeout"]},"isTest":{"type":"boolean"},"failureCode":{"anyOf":[{"type":"string"},{"type":"null"}]},"cardLast4":{"anyOf":[{"type":"string"},{"type":"null"}]},"cardBrand":{"anyOf":[{"type":"string"},{"type":"null"}]},"authCode":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Nayax only"},"nayaxRRN":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Nayax only"},"stripePaymentIntentId":{"anyOf":[{"type":"string"},{"type":"null"}]},"stripeChargeId":{"anyOf":[{"type":"string"},{"type":"null"}]},"stripeReaderId":{"anyOf":[{"type":"string"},{"type":"null"}]},"tenantId":{"anyOf":[{"type":"string"},{"type":"null"}]},"galleryId":{"anyOf":[{"type":"string"},{"type":"null"}]},"guestSessionUuid":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The booth-minted session id this charge belongs to. Match it against a gallery session (sessions carry the same value) to find the photos; null on older charges and test charges."},"createdAt":{"anyOf":[{"type":"string"},{"type":"null"}]},"completedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["id","provider","eventId","eventName","deviceId","deviceName","initiator","terminalId","paySetId","paySetName","productType","quantity","amount","subtotalAmount","taxAmount","taxRate","taxInclusive","taxLabel","taxSource","currency","status","isTest","failureCode","cardLast4","cardBrand","authCode","nayaxRRN","stripePaymentIntentId","stripeChargeId","stripeReaderId","tenantId","galleryId","guestSessionUuid","createdAt","completedAt"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/payment-account":{"get":{"operationId":"get_payment_account","summary":"Pay-per-use connection status per payment rail (Nayax / Stripe)","description":"A snapshot from our records — no live provider calls, safe to poll. Connecting a rail and pairing terminals happen in the dashboard, not this API.","x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"nayax":{"type":"object","properties":{"state":{"type":"string","description":"not_connected | pending_support (nayax) | connected"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}]},"accountName":{"anyOf":[{"type":"string"},{"type":"null"}]},"operatorId":{"anyOf":[{"type":"string"},{"type":"null"}]},"terminalSerials":{"type":"array","items":{"type":"string"}},"connectedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["state","currency","accountName","operatorId","terminalSerials","connectedAt"],"additionalProperties":false},"stripe":{"type":"object","properties":{"state":{"type":"string","description":"not_connected | pending_support (nayax) | connected"},"currency":{"anyOf":[{"type":"string"},{"type":"null"}]},"accountId":{"anyOf":[{"type":"string"},{"type":"null"}]},"chargesEnabled":{"type":"boolean"},"readerIds":{"type":"array","items":{"type":"string"}},"connectedAt":{"anyOf":[{"type":"string"},{"type":"null"}]}},"required":["state","currency","accountId","chargesEnabled","readerIds","connectedAt"],"additionalProperties":false}},"required":["nayax","stripe"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/account/cloud-storage":{"get":{"operationId":"get_cloud_storage","summary":"Which cloud-storage providers the account has connected, and where uploads land","description":"Every guest upload is relayed into the connected providers (Dropbox, Google Drive, SmugMug) — into the root folder here, or into an event's own destination (get_event_cloud_storage). Connecting a provider is an OAuth round trip the operator completes in the dashboard; this read tells you what is already connected, enabled, and healthy (connectionBroken / destinationMissing).","x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"providers":{"type":"array","items":{"type":"object","properties":{"provider":{"type":"string","enum":["dropbox","google-drive","smugmug"]},"connected":{"type":"boolean","description":"Connecting (an OAuth round trip) and disconnecting are not offered here. The operator does this in the dashboard (Account → Cloud storage)."},"enabled":{"type":"boolean","description":"The per-provider switch; false = connected but not uploading"},"accountLabel":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The connected account, as the provider names it"},"rootFolder":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"The provider's rename-proof handle (Dropbox folder id, Drive folderId, SmugMug node id)"},"name":{"type":"string"},"pathDisplay":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Display path where the provider has one (Dropbox); may go stale after a rename"},"driveId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Google shared-drive id, when the folder lives on one"}},"required":["id","name","pathDisplay","driveId"],"additionalProperties":false},{"type":"null"}],"description":"Account-level destination: each event gets a subfolder (SmugMug: an album) named after it in here. Dropbox is fixed to the app folder (Apps/Booth.Events). null = not chosen yet — Google Drive and SmugMug upload nothing until it is."},"connectionBroken":{"type":"boolean","description":"The grant was revoked or expired: uploads stop until the operator reconnects in the dashboard"},"destinationMissing":{"type":"boolean","description":"The root folder is gone on the provider side: choose another with update_cloud_storage_provider (Google Drive: pass rootFolderName to create it again)"},"connectedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601"}},"required":["provider","connected","enabled","accountLabel","rootFolder","connectionBroken","destinationMissing","connectedAt"],"additionalProperties":false},"description":"One row per provider, connected or not"}},"required":["providers"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/account/cloud-storage/{provider}":{"patch":{"operationId":"update_cloud_storage_provider","summary":"Switch a connected provider on or off, or change the account's root folder","description":"The provider must already be connected (get_cloud_storage). rootFolder applies to SmugMug and Google Drive — take it from list_cloud_storage_folders / create_cloud_storage_folder; Dropbox is fixed to its app folder. Google Drive has no folders to pick from until one exists: pass rootFolderName and Booth.Events creates the account folder in the operator's Drive under that name and uses it (a folder it already created there is used again, and keeps its name). The operator can rename or move it in Drive afterwards. Choosing a folder also clears destinationMissing.","parameters":[{"name":"provider","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","enum":["dropbox","google-drive","smugmug"]}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"provider":{"type":"string","enum":["dropbox","google-drive","smugmug"]},"connected":{"type":"boolean","description":"Connecting (an OAuth round trip) and disconnecting are not offered here. The operator does this in the dashboard (Account → Cloud storage)."},"enabled":{"type":"boolean","description":"The per-provider switch; false = connected but not uploading"},"accountLabel":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"The connected account, as the provider names it"},"rootFolder":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"The provider's rename-proof handle (Dropbox folder id, Drive folderId, SmugMug node id)"},"name":{"type":"string"},"pathDisplay":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Display path where the provider has one (Dropbox); may go stale after a rename"},"driveId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Google shared-drive id, when the folder lives on one"}},"required":["id","name","pathDisplay","driveId"],"additionalProperties":false},{"type":"null"}],"description":"Account-level destination: each event gets a subfolder (SmugMug: an album) named after it in here. Dropbox is fixed to the app folder (Apps/Booth.Events). null = not chosen yet — Google Drive and SmugMug upload nothing until it is."},"connectionBroken":{"type":"boolean","description":"The grant was revoked or expired: uploads stop until the operator reconnects in the dashboard"},"destinationMissing":{"type":"boolean","description":"The root folder is gone on the provider side: choose another with update_cloud_storage_provider (Google Drive: pass rootFolderName to create it again)"},"connectedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601"}},"required":["provider","connected","enabled","accountLabel","rootFolder","connectionBroken","destinationMissing","connectedAt"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"enabled":{"type":"boolean"},"rootFolder":{"type":"object","properties":{"id":{"type":"string","minLength":1,"description":"From list_cloud_storage_folders / create_cloud_storage_folder. Google Drive: it must be a folder Booth.Events created (create_cloud_storage_folder, or the account folder) or one the operator chose in the dashboard — Booth.Events cannot reach any other folder in the Drive, even with its id"},"name":{"type":"string","minLength":1,"maxLength":200},"pathDisplay":{"type":"string","maxLength":500},"driveId":{"type":"string","maxLength":200}},"required":["id","name"]},"rootFolderName":{"description":"Google Drive only: create the account folder under this name (or use the one Booth.Events already created) and make it the root. Not together with rootFolder","type":"string","minLength":1,"maxLength":120}}}}}}}},"/account/cloud-storage/{provider}/folders":{"get":{"operationId":"list_cloud_storage_folders","summary":"Browse a connected provider's folders (Dropbox, SmugMug)","description":"One level at a time: omit parent for the provider root, then pass a folder id as parent to drill in. Dropbox browses inside the app folder. SmugMug returns folders (drill in) and albums (selectable leaves). Google Drive cannot be browsed — Booth.Events only sees the folders it created there; make one with create_cloud_storage_folder.","parameters":[{"name":"provider","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","enum":["dropbox","google-drive","smugmug"]}},{"name":"parent","in":"query","required":false,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","description":"Folder id to list inside; omit for the root","type":"string","minLength":1},"description":"Folder id to list inside; omit for the root"}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"folders":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"hasChildren":{"anyOf":[{"type":"boolean"},{"type":"null"}],"description":"null = the provider does not say"},"type":{"anyOf":[{"type":"string","enum":["folder","album"]},{"type":"null"}],"description":"SmugMug lists both: a folder drills in, an album is a selectable leaf (the exact upload destination). null elsewhere."}},"required":["id","name","hasChildren","type"],"additionalProperties":false}}},"required":["folders"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}},"post":{"operationId":"create_cloud_storage_folder","summary":"Create a folder in a connected provider","description":"Creates a folder (SmugMug: a folder node — albums are created by the relay per event) inside parentId, or at the provider root when omitted. Google Drive: the folder goes inside the account folder, which must exist first (update_cloud_storage_provider with rootFolderName); a parentId must be a folder Booth.Events created. Use the returned id as a rootFolder or an event's destination.","parameters":[{"name":"provider","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","enum":["dropbox","google-drive","smugmug"]}}],"x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"id":{"type":"string","description":"The provider's rename-proof handle (Dropbox folder id, Drive folderId, SmugMug node id)"},"name":{"type":"string"},"pathDisplay":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Display path where the provider has one (Dropbox); may go stale after a rename"},"driveId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Google shared-drive id, when the folder lives on one"}},"required":["id","name","pathDisplay","driveId"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"parentId":{"type":"string","minLength":1},"name":{"type":"string","minLength":1,"maxLength":200}},"required":["name"]}}}}}},"/events/{eventId}/cloud-storage":{"get":{"operationId":"get_event_cloud_storage","summary":"An event's cloud-storage settings: uploads on/off, destinations, media filters","description":"Per-event overrides on top of the account setup (get_cloud_storage). A provider only receives this event's media when it is connected and enabled on the account, uploadsEnabled is true here, and it has a folder (this event's override, else the account root).","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"eventId":{"type":"string"},"uploadsEnabled":{"type":"boolean","description":"false = no provider receives this event's media (the dashboard's \"Upload this event\" switch, off)"},"folders":{"type":"object","properties":{"dropbox":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"The provider's rename-proof handle (Dropbox folder id, Drive folderId, SmugMug node id)"},"name":{"type":"string"},"pathDisplay":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Display path where the provider has one (Dropbox); may go stale after a rename"},"driveId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Google shared-drive id, when the folder lives on one"},"exact":{"type":"boolean","description":"true = upload straight into this folder/album; false = create a subfolder/album named after the event inside it. Overrides set here or in the dashboard are always exact; false only survives from older overrides."}},"required":["id","name","pathDisplay","driveId","exact"],"additionalProperties":false},{"type":"null"}]},"google-drive":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"The provider's rename-proof handle (Dropbox folder id, Drive folderId, SmugMug node id)"},"name":{"type":"string"},"pathDisplay":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Display path where the provider has one (Dropbox); may go stale after a rename"},"driveId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Google shared-drive id, when the folder lives on one"},"exact":{"type":"boolean","description":"true = upload straight into this folder/album; false = create a subfolder/album named after the event inside it. Overrides set here or in the dashboard are always exact; false only survives from older overrides."}},"required":["id","name","pathDisplay","driveId","exact"],"additionalProperties":false},{"type":"null"}]},"smugmug":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"The provider's rename-proof handle (Dropbox folder id, Drive folderId, SmugMug node id)"},"name":{"type":"string"},"pathDisplay":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Display path where the provider has one (Dropbox); may go stale after a rename"},"driveId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Google shared-drive id, when the folder lives on one"},"exact":{"type":"boolean","description":"true = upload straight into this folder/album; false = create a subfolder/album named after the event inside it. Overrides set here or in the dashboard are always exact; false only survives from older overrides."}},"required":["id","name","pathDisplay","driveId","exact"],"additionalProperties":false},{"type":"null"}]}},"required":["dropbox","google-drive","smugmug"],"additionalProperties":false,"description":"Per-provider destination override; null = the account root folder (a subfolder/album named after the event is created in it). Dropbox never has one."},"mediaFilters":{"type":"object","properties":{"dropbox":{"type":"object","properties":{"uploadTemplatedPhotos":{"type":"boolean","description":"Photos with the template applied (regular captures)"},"uploadOriginalPhotos":{"type":"boolean","description":"Untemplated photos (regular captures)"},"uploadVideos":{"type":"boolean","description":"Videos, boomerangs and GIFs"},"uploadAiTemplatedPhotos":{"type":"boolean","description":"AI Prompts / AI Portraits results with the template applied"},"uploadAiOriginalPhotos":{"type":"boolean","description":"AI Prompts / AI Portraits results without the template"}},"required":["uploadTemplatedPhotos","uploadOriginalPhotos","uploadVideos","uploadAiTemplatedPhotos","uploadAiOriginalPhotos"],"additionalProperties":false},"google-drive":{"type":"object","properties":{"uploadTemplatedPhotos":{"type":"boolean","description":"Photos with the template applied (regular captures)"},"uploadOriginalPhotos":{"type":"boolean","description":"Untemplated photos (regular captures)"},"uploadVideos":{"type":"boolean","description":"Videos, boomerangs and GIFs"},"uploadAiTemplatedPhotos":{"type":"boolean","description":"AI Prompts / AI Portraits results with the template applied"},"uploadAiOriginalPhotos":{"type":"boolean","description":"AI Prompts / AI Portraits results without the template"}},"required":["uploadTemplatedPhotos","uploadOriginalPhotos","uploadVideos","uploadAiTemplatedPhotos","uploadAiOriginalPhotos"],"additionalProperties":false},"smugmug":{"type":"object","properties":{"uploadTemplatedPhotos":{"type":"boolean","description":"Photos with the template applied (regular captures)"},"uploadOriginalPhotos":{"type":"boolean","description":"Untemplated photos (regular captures)"},"uploadVideos":{"type":"boolean","description":"Videos, boomerangs and GIFs"},"uploadAiTemplatedPhotos":{"type":"boolean","description":"AI Prompts / AI Portraits results with the template applied"},"uploadAiOriginalPhotos":{"type":"boolean","description":"AI Prompts / AI Portraits results without the template"}},"required":["uploadTemplatedPhotos","uploadOriginalPhotos","uploadVideos","uploadAiTemplatedPhotos","uploadAiOriginalPhotos"],"additionalProperties":false}},"required":["dropbox","google-drive","smugmug"],"additionalProperties":false,"description":"Which media types each provider receives. Hydrated: a switch never set reads as on, and an AI switch never set follows its regular twin."}},"required":["eventId","uploadsEnabled","folders","mediaFilters"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}},"patch":{"operationId":"update_event_cloud_storage","summary":"Change an event's cloud-storage settings","description":"Partial: only what you send changes. Inside folders and mediaFilters, only the providers you name change — a folder REPLACES that provider's destination (null = back to the account root folder), a media filter MERGES the switches you send (null = back to everything on). A destination set here is exact: media uploads straight into that folder/album. Dropbox takes no destination (its app folder is fixed).","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"eventId":{"type":"string"},"uploadsEnabled":{"type":"boolean","description":"false = no provider receives this event's media (the dashboard's \"Upload this event\" switch, off)"},"folders":{"type":"object","properties":{"dropbox":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"The provider's rename-proof handle (Dropbox folder id, Drive folderId, SmugMug node id)"},"name":{"type":"string"},"pathDisplay":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Display path where the provider has one (Dropbox); may go stale after a rename"},"driveId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Google shared-drive id, when the folder lives on one"},"exact":{"type":"boolean","description":"true = upload straight into this folder/album; false = create a subfolder/album named after the event inside it. Overrides set here or in the dashboard are always exact; false only survives from older overrides."}},"required":["id","name","pathDisplay","driveId","exact"],"additionalProperties":false},{"type":"null"}]},"google-drive":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"The provider's rename-proof handle (Dropbox folder id, Drive folderId, SmugMug node id)"},"name":{"type":"string"},"pathDisplay":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Display path where the provider has one (Dropbox); may go stale after a rename"},"driveId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Google shared-drive id, when the folder lives on one"},"exact":{"type":"boolean","description":"true = upload straight into this folder/album; false = create a subfolder/album named after the event inside it. Overrides set here or in the dashboard are always exact; false only survives from older overrides."}},"required":["id","name","pathDisplay","driveId","exact"],"additionalProperties":false},{"type":"null"}]},"smugmug":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"The provider's rename-proof handle (Dropbox folder id, Drive folderId, SmugMug node id)"},"name":{"type":"string"},"pathDisplay":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Display path where the provider has one (Dropbox); may go stale after a rename"},"driveId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Google shared-drive id, when the folder lives on one"},"exact":{"type":"boolean","description":"true = upload straight into this folder/album; false = create a subfolder/album named after the event inside it. Overrides set here or in the dashboard are always exact; false only survives from older overrides."}},"required":["id","name","pathDisplay","driveId","exact"],"additionalProperties":false},{"type":"null"}]}},"required":["dropbox","google-drive","smugmug"],"additionalProperties":false,"description":"Per-provider destination override; null = the account root folder (a subfolder/album named after the event is created in it). Dropbox never has one."},"mediaFilters":{"type":"object","properties":{"dropbox":{"type":"object","properties":{"uploadTemplatedPhotos":{"type":"boolean","description":"Photos with the template applied (regular captures)"},"uploadOriginalPhotos":{"type":"boolean","description":"Untemplated photos (regular captures)"},"uploadVideos":{"type":"boolean","description":"Videos, boomerangs and GIFs"},"uploadAiTemplatedPhotos":{"type":"boolean","description":"AI Prompts / AI Portraits results with the template applied"},"uploadAiOriginalPhotos":{"type":"boolean","description":"AI Prompts / AI Portraits results without the template"}},"required":["uploadTemplatedPhotos","uploadOriginalPhotos","uploadVideos","uploadAiTemplatedPhotos","uploadAiOriginalPhotos"],"additionalProperties":false},"google-drive":{"type":"object","properties":{"uploadTemplatedPhotos":{"type":"boolean","description":"Photos with the template applied (regular captures)"},"uploadOriginalPhotos":{"type":"boolean","description":"Untemplated photos (regular captures)"},"uploadVideos":{"type":"boolean","description":"Videos, boomerangs and GIFs"},"uploadAiTemplatedPhotos":{"type":"boolean","description":"AI Prompts / AI Portraits results with the template applied"},"uploadAiOriginalPhotos":{"type":"boolean","description":"AI Prompts / AI Portraits results without the template"}},"required":["uploadTemplatedPhotos","uploadOriginalPhotos","uploadVideos","uploadAiTemplatedPhotos","uploadAiOriginalPhotos"],"additionalProperties":false},"smugmug":{"type":"object","properties":{"uploadTemplatedPhotos":{"type":"boolean","description":"Photos with the template applied (regular captures)"},"uploadOriginalPhotos":{"type":"boolean","description":"Untemplated photos (regular captures)"},"uploadVideos":{"type":"boolean","description":"Videos, boomerangs and GIFs"},"uploadAiTemplatedPhotos":{"type":"boolean","description":"AI Prompts / AI Portraits results with the template applied"},"uploadAiOriginalPhotos":{"type":"boolean","description":"AI Prompts / AI Portraits results without the template"}},"required":["uploadTemplatedPhotos","uploadOriginalPhotos","uploadVideos","uploadAiTemplatedPhotos","uploadAiOriginalPhotos"],"additionalProperties":false}},"required":["dropbox","google-drive","smugmug"],"additionalProperties":false,"description":"Which media types each provider receives. Hydrated: a switch never set reads as on, and an AI switch never set follows its regular twin."}},"required":["eventId","uploadsEnabled","folders","mediaFilters"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"uploadsEnabled":{"type":"boolean"},"folders":{"type":"object","properties":{"dropbox":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","minLength":1,"description":"From list_cloud_storage_folders / create_cloud_storage_folder. Google Drive: it must be a folder Booth.Events created (create_cloud_storage_folder, or the account folder) or one the operator chose in the dashboard — Booth.Events cannot reach any other folder in the Drive, even with its id"},"name":{"type":"string","minLength":1,"maxLength":200},"pathDisplay":{"type":"string","maxLength":500},"driveId":{"type":"string","maxLength":200}},"required":["id","name"]},{"type":"null"}]},"google-drive":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","minLength":1,"description":"From list_cloud_storage_folders / create_cloud_storage_folder. Google Drive: it must be a folder Booth.Events created (create_cloud_storage_folder, or the account folder) or one the operator chose in the dashboard — Booth.Events cannot reach any other folder in the Drive, even with its id"},"name":{"type":"string","minLength":1,"maxLength":200},"pathDisplay":{"type":"string","maxLength":500},"driveId":{"type":"string","maxLength":200}},"required":["id","name"]},{"type":"null"}]},"smugmug":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","minLength":1,"description":"From list_cloud_storage_folders / create_cloud_storage_folder. Google Drive: it must be a folder Booth.Events created (create_cloud_storage_folder, or the account folder) or one the operator chose in the dashboard — Booth.Events cannot reach any other folder in the Drive, even with its id"},"name":{"type":"string","minLength":1,"maxLength":200},"pathDisplay":{"type":"string","maxLength":500},"driveId":{"type":"string","maxLength":200}},"required":["id","name"]},{"type":"null"}]}}},"mediaFilters":{"type":"object","properties":{"dropbox":{"anyOf":[{"type":"object","properties":{"uploadTemplatedPhotos":{"type":"boolean","description":"Photos with the template applied (regular captures)"},"uploadOriginalPhotos":{"type":"boolean","description":"Untemplated photos (regular captures)"},"uploadVideos":{"type":"boolean","description":"Videos, boomerangs and GIFs"},"uploadAiTemplatedPhotos":{"type":"boolean","description":"AI Prompts / AI Portraits results with the template applied"},"uploadAiOriginalPhotos":{"type":"boolean","description":"AI Prompts / AI Portraits results without the template"}}},{"type":"null"}]},"google-drive":{"anyOf":[{"type":"object","properties":{"uploadTemplatedPhotos":{"type":"boolean","description":"Photos with the template applied (regular captures)"},"uploadOriginalPhotos":{"type":"boolean","description":"Untemplated photos (regular captures)"},"uploadVideos":{"type":"boolean","description":"Videos, boomerangs and GIFs"},"uploadAiTemplatedPhotos":{"type":"boolean","description":"AI Prompts / AI Portraits results with the template applied"},"uploadAiOriginalPhotos":{"type":"boolean","description":"AI Prompts / AI Portraits results without the template"}}},{"type":"null"}]},"smugmug":{"anyOf":[{"type":"object","properties":{"uploadTemplatedPhotos":{"type":"boolean","description":"Photos with the template applied (regular captures)"},"uploadOriginalPhotos":{"type":"boolean","description":"Untemplated photos (regular captures)"},"uploadVideos":{"type":"boolean","description":"Videos, boomerangs and GIFs"},"uploadAiTemplatedPhotos":{"type":"boolean","description":"AI Prompts / AI Portraits results with the template applied"},"uploadAiOriginalPhotos":{"type":"boolean","description":"AI Prompts / AI Portraits results without the template"}}},{"type":"null"}]}}}}}}}}}},"/events/{eventId}/cloud-storage/backfill":{"post":{"operationId":"backfill_event_cloud_storage","summary":"Upload an event's existing media to its cloud-storage destinations","description":"The dashboard's \"Upload existing media\": queues every photo and video not yet uploaded (and re-queues failed ones) for each provider that currently applies to the event. Asynchronous — this only enqueues. Safe to call twice; already-uploaded media is skipped. Fails with validation_failed when uploads are off for the event or no provider is connected, enabled and has a folder, and with plan_required when the event itself is not a Pro+ event.","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"eventId":{"type":"string"},"enqueued":{"type":"boolean","const":true}},"required":["eventId","enqueued"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}},"/events/{eventId}/cloud-storage/retry":{"post":{"operationId":"retry_event_cloud_storage","summary":"Retry an event's failed cloud-storage uploads","description":"Re-queues only the uploads that FAILED (e.g. after the operator freed up storage space or reconnected the provider). Never scans for media that was never queued — use backfill_event_cloud_storage for that. Asynchronous — this only enqueues. Fails with plan_required when the event itself is not a Pro+ event.","parameters":[{"name":"eventId","in":"path","required":true,"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"string","minLength":1}}],"x-required-scope":"read-write","responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"$schema":"https://json-schema.org/draft/2020-12/schema","type":"object","properties":{"eventId":{"type":"string"},"enqueued":{"type":"boolean","const":true}},"required":["eventId","enqueued"],"additionalProperties":false}}}},"default":{"$ref":"#/components/responses/Error"}}}}},"components":{"securitySchemes":{"apiKey":{"type":"http","scheme":"bearer","description":"Booth.Events API key (be_live_… / be_test_…), shown once at creation."}},"responses":{"Error":{"description":"Error envelope: { error: { code, message, details?, hint?, requestId } }. Codes: invalid_api_key, revoked_api_key, plan_required, account_unavailable, insufficient_scope, rate_limited, validation_failed, invalid_cursor, payload_too_large, permission_denied, limit_exceeded, insufficient_credits, not_found, method_not_allowed, conflict, dependency_unavailable, internal.","content":{"application/json":{"schema":{"type":"object"}}}}}}}