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.

Funnel beacon

POST/v1/events/{slug}/tickets/funnel

Optional. The organiser's sales report shows a funnel from viewing tickets to buying them; the reserve and the payment write their own steps, but the top of the funnel only exists if your page reports it.

Mint an opaque token per visit (a UUID is fine), send event_viewed when the ticket list renders and ticket_selected the first time a quantity goes above zero, and pass the same token as funnelSessionToken on reserve so the steps join up. Send both or neither: beacons without the reserve token, or the reverse, show a funnel that never converts.

Fire and forget: the response is always { ok: true }, and a failure must never block a checkout. navigator.sendBeacon or fetch with keepalive both work.

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 POST \
  -H 'X-Grofomo-Key: pk_live_YOUR_PUBLISHABLE_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"step":"event_viewed","sessionToken":"9d1f7c2a-3b6e-4a8d-9e0f-1c2b3a4d5e6f"}' \
  'https://api.grofomo.com/v1/events/summer-fest/tickets/funnel?surface=web'

Request body

step"event_viewed" | "ticket_selected" | "checkout_started" | "purchased"required
Send `event_viewed` when the ticket list renders and `ticket_selected` the first time a quantity goes above zero. `checkout_started` and `purchased` are recorded server-side from the reserve and the payment; do not send them.
sessionTokenstringrequired
An opaque per-visit token you mint (a UUID is fine), kept for the visit and sent again as `funnelSessionToken` on reserve. Not a credential and never shown to anyone.
orderPublicIdstring
The order's `publicId` once one exists. Optional.
deviceHandoffstring
Internal: a signed hand-off from the Grofomo mobile app. Leave it out.
Generated from FunnelBeacon

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`.

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`, even when nothing was recorded.

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 event with that slug is published on this surface. Check the slug, and check you sent `?surface=web`.
bad_requestHTTP 400
The body did not match the schema.

Notes

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