{"openapi":"3.1.0","info":{"title":"RSVP Sorted venue API","version":"1.2.0","summary":"Hand a booking's party over to make invites, and keep its date, time and status current.","description":"Call the handoff only when a parent clicks your \"Make your invites\" button, from your server, and redirect the parent to the URL it returns with a 303. Nothing is sent before that click.\n\nBooking updates carry no personal data. Send one whenever a booking that might have invites moves or is cancelled; a 404 means the parent never made invites and can be ignored.\n\nVenues choose which party packages offer invites. Read the booking types to decide whether to show the button, and register a package when you add one so the venue can decide before anyone books it.\n\nA handoff needs the name of the person the booking is for, the date and times, and the booking email. Send the occasion for anything other than a children's birthday, the room when your system books one, how many places the booking covers, and the booking mobile number if you take one, so a parent who signs in by text can save the invite too.\n\nEvery error is an RFC 9457 problem details object (`application/problem+json`), including a 405 with an Allow header for a method a path doesn't take and a 404 for a path that doesn't exist.","contact":{"email":"hello@rsvpsorted.com"}},"servers":[{"url":"https://app.rsvpsorted.com"}],"security":[{"venueKey":[]}],"paths":{"/api/v1/venue/handoffs/{externalRef}":{"parameters":[{"name":"externalRef","in":"path","required":true,"description":"The booking's reference in your system. One booking always maps to the same invite.","schema":{"type":"string","pattern":"^[A-Za-z0-9._-]{1,64}$","description":"The booking's own reference in the venue's system: letters, digits, dot, dash or underscore."}}],"put":{"operationId":"handOffBooking","summary":"Hand a booking over to make invites","description":"Creates the parent's draft invite, pre-filled from the booking, or finds the one already made. Idempotent: repeating it for a booking returns the same link and never changes the invite.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HandoffRequest"}}}},"responses":{"200":{"description":"The link to send the parent to.","headers":{"Cache-Control":{"description":"Always no-store: the response is for this request only.","schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HandoffResponse"}}}},"400":{"description":"The body or the booking reference didn't match the contract. `errors` lists each field or parameter that was wrong.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The key is missing, wrong or revoked. Keys start with rsvps_live_ and are made in the venue area.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"403":{"description":"The venue has switched invites off, for every booking or for this booking's package. Don't show the button for it.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests with this key. Wait for the Retry-After seconds and try again.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"headers":{"Retry-After":{"description":"Seconds to wait before trying again.","schema":{"type":"integer","minimum":1}}}},"500":{"description":"Something went wrong on our side. Retry with backoff; the request is safe to repeat.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"503":{"description":"Handoffs are paused for the moment. Show the parent a friendly message and let them try later.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/v1/venue/bookings/{externalRef}":{"parameters":[{"name":"externalRef","in":"path","required":true,"description":"The booking's reference in your system. One booking always maps to the same invite.","schema":{"type":"string","pattern":"^[A-Za-z0-9._-]{1,64}$","description":"The booking's own reference in the venue's system: letters, digits, dot, dash or underscore."}}],"patch":{"operationId":"updateBooking","summary":"Move or cancel a booking","description":"Updates the party's date, times and status. If the invite has gone out, its guests see an \"Updated\" banner. Safe to repeat.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingUpdateRequest"}}}},"responses":{"200":{"description":"The change is saved.","headers":{"Cache-Control":{"description":"Always no-store: the response is for this request only.","schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingUpdateResponse"}}}},"400":{"description":"The body or the booking reference didn't match the contract. `errors` lists each field or parameter that was wrong.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The key is missing, wrong or revoked. Keys start with rsvps_live_ and are made in the venue area.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"404":{"description":"We've never seen this booking reference from your venue: the parent hasn't made invites. Safe to ignore.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests with this key. Wait for the Retry-After seconds and try again.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"headers":{"Retry-After":{"description":"Seconds to wait before trying again.","schema":{"type":"integer","minimum":1}}}},"500":{"description":"Something went wrong on our side. Retry with backoff; the request is safe to repeat.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/v1/venue/booking-types":{"get":{"operationId":"listBookingTypes","summary":"Which party packages offer invites","description":"The venue-wide switch, what happens to a package we haven't seen, and every package we know with whether it offers invites. Show the button for a booking when offerInvites is true and its package offers invites (or, for a package not listed, when newBookingTypes is offer).","responses":{"200":{"description":"The venue's invite settings.","headers":{"Cache-Control":{"description":"Always no-store: the response is for this request only.","schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingTypesResponse"}}}},"401":{"description":"The key is missing, wrong or revoked. Keys start with rsvps_live_ and are made in the venue area.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests with this key. Wait for the Retry-After seconds and try again.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"headers":{"Retry-After":{"description":"Seconds to wait before trying again.","schema":{"type":"integer","minimum":1}}}},"500":{"description":"Something went wrong on our side. Retry with backoff; the request is safe to repeat.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/v1/venue/booking-types/{bookingTypeId}":{"parameters":[{"name":"bookingTypeId","in":"path","required":true,"description":"Your booking system's id for the party package.","schema":{"type":"string","pattern":"^[A-Za-z0-9._-]{1,64}$","description":"Your booking system's id for the party package: letters, digits, dot, dash or underscore."}}],"put":{"operationId":"registerBookingType","summary":"Register or rename a party package","description":"Adds a package the venue can then switch invites on or off for, or renames one already known. Safe to repeat; never changes whether a package offers invites.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingTypeRequest"}}}},"responses":{"200":{"description":"The package as it now stands.","headers":{"Cache-Control":{"description":"Always no-store: the response is for this request only.","schema":{"type":"string","const":"no-store"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BookingTypeResponse"}}}},"400":{"description":"The body or the booking reference didn't match the contract. `errors` lists each field or parameter that was wrong.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"401":{"description":"The key is missing, wrong or revoked. Keys start with rsvps_live_ and are made in the venue area.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}},"429":{"description":"Too many requests with this key. Wait for the Retry-After seconds and try again.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"headers":{"Retry-After":{"description":"Seconds to wait before trying again.","schema":{"type":"integer","minimum":1}}}},"500":{"description":"Something went wrong on our side. Retry with backoff; the request is safe to repeat.","content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}}}}}},"/api/v1/venue/openapi.json":{"get":{"operationId":"getOpenApiDocument","summary":"This document","security":[],"responses":{"200":{"description":"The venue API's OpenAPI 3.1 description.","content":{"application/json":{"schema":{"type":"object"}}}}}}}},"components":{"securitySchemes":{"venueKey":{"type":"http","scheme":"bearer","description":"A venue API key, starting rsvps_live_, sent as `Authorization: Bearer <key>`. Owners and admins make and revoke keys in the venue area; we only ever store a hash."}},"schemas":{"HandoffRequest":{"type":"object","properties":{"occasion":{"description":"What the booking is for. Leave it out for a children's birthday, or to use the occasion the venue set for the booking type in its venue area. Any other occasion in the list may be sent, such as adult_birthday, baby_shower or memorial; only occasions open to hosts are accepted, and \"other\" never is (the host names it themselves, so send get_together instead).","type":"string","enum":["kids_birthday","adult_birthday","baby_shower","bridal_shower","christening","gender_reveal","hen_do","stag_do","halloween","christmas","dinner","get_together","memorial"]},"childFirstName":{"type":"string","minLength":1,"maxLength":40,"description":"The first name of the person the booking is for: the birthday child for a children's party, the person whose birthday or shower it is for a grown-up occasion, or the person being remembered for a memorial. Set once, when the invite is first made; the host can change it afterwards."},"childAge":{"default":null,"description":"The age they turn, if the booking system knows it. Only for birthdays.","anyOf":[{"type":"integer","minimum":0,"maximum":120},{"type":"null"}]},"partyDate":{"type":"string","format":"date","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])))$","description":"The party's date at the venue, in Europe/London."},"startTime":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$","description":"Local start time, 24-hour HH:MM."},"endTime":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$","description":"Local end time, 24-hour HH:MM."},"hostFirstName":{"default":"","description":"The booker's first name, used to greet them.","type":"string","maxLength":40},"capacity":{"default":null,"description":"How many places the booking is for, when the venue limits it: children for a children's occasion, guests for a grown-up one. Guests can't reply coming for more than this; the host sees it as \"booked for up to N\" and can lower it. Optional.","anyOf":[{"type":"integer","minimum":1,"maximum":1000},{"type":"null"}]},"roomName":{"description":"The room or space booked, as the venue names it, such as \"Jungle room\". Shown to the host and their guests with the venue's address. Optional.","type":"string","minLength":1,"maxLength":80},"bookingEmail":{"type":"string","format":"email","pattern":"^(?:[A-Za-z0-9_'+\\-]+\\.)*[A-Za-z0-9_'+\\-]*[A-Za-z0-9_+-]@(?:[A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$","description":"The address the party was booked with. Only this person can save the invite; we keep a hash and a masked hint, never the address."},"bookingPhone":{"description":"The UK mobile number the party was booked with, in any usual form (07700 900123 or +44 7700 900123). A booker who signs in by text with this number can save the invite, as the booking email can; we keep a hash and a masked hint, never the number. Optional.","type":"string","maxLength":32},"bookingType":{"description":"The party package booked. The venue chooses in its venue area which packages offer invites; a package it has switched off is refused with not_offered.","type":"object","properties":{"id":{"type":"string","pattern":"^[A-Za-z0-9._-]{1,64}$","description":"Your booking system's id for the party package: letters, digits, dot, dash or underscore."},"name":{"type":"string","minLength":1,"maxLength":80,"description":"The package's name, as the venue's staff know it."}},"required":["id","name"],"additionalProperties":false}},"required":["childFirstName","partyDate","startTime","endTime","bookingEmail"],"additionalProperties":false},"HandoffResponse":{"type":"object","properties":{"url":{"type":"string","format":"uri","description":"Where to send the parent, with a 303 redirect. Short-lived and single-purpose; never store it or put it in an email."}},"required":["url"],"additionalProperties":false},"BookingUpdateRequest":{"type":"object","properties":{"partyDate":{"type":"string","format":"date","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])))$","description":"The party's date at the venue, in Europe/London."},"startTime":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$","description":"Local start time, 24-hour HH:MM."},"endTime":{"type":"string","pattern":"^([01]\\d|2[0-3]):[0-5]\\d$","description":"Local end time, 24-hour HH:MM."},"status":{"type":"string","enum":["confirmed","cancelled"],"description":"Cancelled tells the parent's guests the party is off. A cancelled booking can't be confirmed again."}},"required":["partyDate","startTime","endTime","status"],"additionalProperties":false},"BookingUpdateResponse":{"type":"object","properties":{"updated":{"type":"boolean","const":true}},"required":["updated"],"additionalProperties":false},"BookingTypeRequest":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":80,"description":"The package's name, as the venue's staff know it."}},"required":["name"],"additionalProperties":false},"BookingTypeResponse":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"offerInvites":{"type":"boolean","description":"Show the \"Make your invites\" button for bookings of this package."}},"required":["id","name","offerInvites"],"additionalProperties":false},"BookingTypesResponse":{"type":"object","properties":{"offerInvites":{"type":"boolean","description":"The venue-wide switch. When false, show the button for no booking, whatever its package."},"newBookingTypes":{"type":"string","enum":["offer","ask"],"description":"What happens to a package we haven't seen yet: offer means its bookings offer invites straight away, ask means they wait until the venue switches it on."},"bookingTypes":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"offerInvites":{"type":"boolean","description":"Show the \"Make your invites\" button for bookings of this package."}},"required":["id","name","offerInvites"],"additionalProperties":false}}},"required":["offerInvites","newBookingTypes","bookingTypes"],"additionalProperties":false},"Problem":{"type":"object","properties":{"type":{"type":"string","const":"about:blank"},"title":{"type":"string","description":"The HTTP status's standard phrase."},"status":{"type":"integer","minimum":400,"maximum":599},"code":{"type":"string","enum":["invalid_request","unauthorised","not_found","method_not_allowed","not_offered","rate_limited","internal_error","unavailable"],"description":"A stable name for the problem, matching the status."},"detail":{"type":"string","description":"What went wrong this time, in plain English. Never contains personal data."},"errors":{"description":"For invalid requests: each field or parameter that was wrong.","type":"array","items":{"anyOf":[{"type":"object","properties":{"detail":{"type":"string"},"pointer":{"type":"string","format":"starts_with","pattern":"^#.*","description":"A JSON Pointer into the request body, as a fragment: \"#\" is the whole body."}},"required":["detail","pointer"],"additionalProperties":false},{"type":"object","properties":{"detail":{"type":"string"},"parameter":{"type":"string","description":"The path parameter that was wrong."}},"required":["detail","parameter"],"additionalProperties":false}]}}},"required":["type","title","status","code","detail"],"additionalProperties":false}}}}