Grofomo API
Browse the docs

Examples use a fictional festival, summer-fest. Sign in at events.grofomo.com and they switch to your own events.

Buyer details and consent

PATCH/v1/events/{slug}/tickets/orders/{checkoutToken}

Attach the buyer to the order: where the tickets go, and what they agreed to hear about. Required before pay or complete-free.

Print the wording we give you. The checkout block on ticket availability carries three consent labels. Show organiserEmail beside a box that is ticked by default (the buyer is becoming the organiser's customer, so this is a soft opt-in) and send the result as orgConsent.email with orgEmailBasis: "soft_opt_in". Show organiserMessaging beside an unticked box that needs a phone number, and send the one result as both orgConsent.sms and orgConsent.whatsapp. Show platformEmail beside its own box, ticked or not as platformEmailDefaultTicked says, and send the result as platformConsent.email with platformEmailBasis copied across. Then send the three strings back, verbatim, as consentTextSnapshot under the keys organiser, messaging and platform, with formVersion copied from the same block: that snapshot is the record of what was agreed to.

A channel you did not ask about is omitted, not sent as false. Omitted leaves a returning buyer's standing choice alone; false withdraws it.

Calling again replaces the details; it is safe to retry.

Send ?surface=webOmit it and the request is treated as coming from the mobile app, which is gated on a different visibility setting — so a perfectly live event can return 404 not_found for no visible reason.

Request

curl -sS \
  -X PATCH \
  -H 'X-Grofomo-Key: pk_live_YOUR_PUBLISHABLE_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"customer":{"email":"alex@example.com","firstName":"Alex","lastName":"Doe","phoneE164":"+447700900123"},"orgConsent":{"email":true,"sms":true,"whatsapp":true},"platformConsent":{"email":false},"orgEmailBasis":"soft_opt_in","platformEmailBasis":"consent","consentTextSnapshot":{"organiser":"We'll email you about future Riot Promotions shows, presales and lineup drops. Untick if you'd rather not - unsubscribe anytime.","messaging":"Be first in line: on-sale alerts, presale codes and last-minute ticket drops from Riot Promotions by SMS or WhatsApp. Reply STOP anytime to opt out.","platform":"Grofomo runs this checkout and delivers your tickets. Tick to get occasional email picks of similar events on Grofomo. Unsubscribe anytime."},"formVersion":"storefront-web-3"}' \
  'https://api.grofomo.com/v1/events/summer-fest/tickets/orders/YzNhMWU1ZDctOWIyZi00ZTZhLThjMGQtMmY0YjZhOGMwZTEyOjE3ODk2NDM2MDAwMDA6dGt0LWNoZWNrb3V0.ZXhhbXBsZS1zaWduYXR1cmU?surface=web'

Request body

customerobjectrequired
The buyer.
customer.emailemailrequired
customer.firstNamestring
customer.lastNamestring
customer.phoneE164string
orgConsentobjectrequired
What the buyer agreed to from the ORGANISER. Print `checkout.consent.organiserEmail` from the ticket feed beside a pre-ticked box (soft opt-in) and `organiserMessaging` beside an unticked one; send `sms` and `whatsapp` together from the messaging box.
orgConsent.emailboolean
orgConsent.smsboolean
orgConsent.pushboolean
orgConsent.postboolean
orgConsent.whatsappboolean
platformConsentobjectrequired
What the buyer agreed to from GROFOMO, asked separately. Print `checkout.consent.platformEmail` from the ticket feed beside its own box and send only `email`.
platformConsent.emailboolean
platformConsent.smsboolean
platformConsent.pushboolean
platformConsent.postboolean
platformConsent.whatsappboolean
orgEmailBasis"consent" | "soft_opt_in"
How the organiser email box was shown: `soft_opt_in` when it was pre-ticked (the wording we give you assumes this), `consent` when the buyer had to tick it.
platformEmailBasis"consent" | "soft_opt_in"
How the Grofomo email box was shown. Send `checkout.platformEmailBasis` from the ticket feed.
consentTextSnapshotobject
The exact wording the buyer saw, keyed `organiser`, `messaging` and `platform`. Send the strings you printed, verbatim: they are stored as the proof of consent.
formVersionstring
Send `checkout.formVersion` from the ticket feed, so the stored consent names the wording it was given under.
answersobject[]
Answers to the organiser's registration questions. The questions are not published on this API yet, so leave this out; the hosted ticket page collects them.
answers[].questionIduuidrequired
answers[].ticketIduuid
answers[].valuestring | number | boolean | string[]required
localestring
The buyer's BCP 47 locale, e.g. `en-GB`, for the confirmation email. Optional.
Generated from CollectDetailsRequest

Path parameters

slugstringrequired
The event's URL slug. Globally unique. It can change if the organiser renames the event, but an old slug keeps reaching the same event, so a stored one never breaks. Example: `summer-fest`.
checkoutTokenstringrequired
The order's `checkoutToken` from the reserve response. It authorises this checkout only and expires a day after the reserve; keep it out of logs and analytics. Example: `YzNhMWU1ZDctOWIyZi00ZTZhLThjMGQtMmY0YjZhOGMwZTEyOjE3ODk2NDM2MDAwMDA6dGt0LWNoZWNrb3V0.ZXhhbXBsZS1zaWduYXR1cmU`.

Query parameters

surface'web'required
Must be `web` for a website. Omit it and you get the mobile-app surface, which is gated on a different visibility setting — so a perfectly live event can return 404. Example: `web`.

Response

200 OK
{
  "apiVersion": 1,
  "data": {
    "ok": true
  }
}
okboolean
Always `true` on success.

Errors

unauthorizedHTTP 401
No key was sent and the organiser requires one — or the key is invalid, revoked, belongs to another organisation, or is scoped to a different event.
origin_not_allowedHTTP 403
Your site's domain is not on the organiser's allowed list. Browser calls only: a server sends no Origin header and is unaffected.
not_foundHTTP 404
No order for that `checkoutToken` on this event (or the token has expired), or no event with that slug on this surface.
bad_requestHTTP 400
The body did not match the schema, or messaging consent was sent without a phone number.
expiredHTTP 409
The 15-minute hold has ended. Start again from reserve.
order_not_openHTTP 409
The order is no longer `reserved`: it has been paid, completed or cancelled.
promo_not_yoursHTTP 409
The order's promo code was issued to a different email address.
consent_text_mismatchHTTP 400
The `consentTextSnapshot` differs from the wording the ticket feed publishes for this `formVersion`. Print the feed's text verbatim.
promo_customer_limitHTTP 409
This email address has used the promo code as many times as it may.

Notes

  • Rate limited to roughly 30 requests per minute per caller. Guidance, not a contract — see caching and rate limits.
  • Cache-Control: no-store