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.

Mailing-list signup

POST/v1/events/{slug}/subscribe

Post an email address straight into the organiser's mailing list, so your site can host its own signup block rather than embedding a hosted form.

Consent is the organiser's to collect. Only submit an address when the visitor has actively asked to hear from them — this endpoint records a signup, it does not make one lawful.

Double opt-in. The signup is saved at once, but the address is not on the list until the visitor opens the confirmation email we send them, so tell them to check their inbox (verification.email is sent). Three follow-up bodies use the same endpoint, keyed by the returned submissionId: { submissionId, phone, channel? } attaches a mobile number and, where phone verification is on, sends it a code (verification.phone is sent) by text, or by WhatsApp when channel is "whatsapp"; a WhatsApp request the number cannot take falls back to a text, and verification.phoneChannel says which one went, so print that rather than what was asked for. { submissionId, code } types the code back and answers with an outcome of verified, wrong_code or expired; { submissionId, resend: "email" | "code", channel? } sends either again, three times per ten minutes, the code by the same choice of channel (the reply's channel is the one it went by). Follow-ups work for an hour after the signup.

There is no bot check on this endpoint today, so you do not need to render a Turnstile widget. What gates it is your publishable key and the organiser's domain allowlist, plus a rate limit.

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.
No bot check todayThis write accepts a turnstileToken, but it is not currently verified, so you do not need to render a widget to call it. What gates it is your publishable key and the organiser’s domain allowlist, plus a per-address rate limit. We will announce it in the changelog before a token starts being required.

Request

curl -sS \
  -X POST \
  -H 'X-Grofomo-Key: pk_live_YOUR_PUBLISHABLE_KEY' \
  -H 'Content-Type: application/json' \
  -d '{"email":"fan@example.com","name":"Alex Doe"}' \
  'https://api.grofomo.com/v1/events/summer-fest/subscribe?surface=web'

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,
    "submissionId": "a7b8c9d0-e1f2-4345-8678-9abcdef01234",
    "verification": {
      "email": "sent",
      "phone": "not_needed",
      "phoneHint": null
    }
  }
}
okbooleanmay be absent
Always `true` on success.
submissionIduuidmay be absent
Identifies this signup for the follow-up bodies (attach a number, type back the code, resend) within the hour.
verificationobjectmay be absent
Where the double opt-in stands: `email` is `sent` when a confirmation link is on its way (`not_needed` if the address was already confirmed, `failed` if the send failed and a resend is worth offering); `phone` is `not_needed` until a number is attached; `phoneHint` is the last digits a code went to; `phoneChannel` is `sms` or `whatsapp` when a code was sent on this call, whichever actually went, else null.

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 email address was not valid.

Notes

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