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.

Apply to be a partner

POST/v1/events/{slug}/ambassadors/applications

For people with a following who want a cash rate for every ticket their crew brings, rather than the rewards. The application lands in the organiser's queue in Grofomo beside ones from the affiliate portal. When they approve it, the applicant becomes a commission partner and is emailed their link and a way to get paid.

Check partnerApplications.open on the programme first and hide the form when it is false: a submission is then refused with applications_closed.

Nothing is emailed to the address given until the organiser acts on the application, so there is no confirmation step to build. Sending again from the same email updates a pending application rather than adding another, and the answer is always received, whatever happened before.

Include an empty, visually hidden website field in your form and send whatever is in it. People never fill it; a submission that does is dropped as a bot. There is no Turnstile widget to render.

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 '{"name":"Sam Rivera","email":"sam@example.com","phone":"07400 123456","instagram":"@samscrew","estimatedTickets":"25_50","audienceReach":"5k_20k","website":""}' \
  'https://api.grofomo.com/v1/events/summer-fest/ambassadors/applications?surface=web'

Request body

namestringrequired
Their name.
emailstringrequired
Where the organiser replies. Approval is sent here.
phonestring | null
Mobile number, optional.
instagramstring | null
Instagram handle or profile URL, optional.
estimatedTickets"under_10" | "10_25" | "25_50" | "50_plus" | null
One of `estimatedTicketsOptions[].value`, optional.
audienceReach"under_1k" | "1k_5k" | "5k_20k" | "20k_plus" | null
One of `audienceReachOptions[].value`, optional.
messagestring | null
Anything else they want the organiser to know, optional.
websitestring
Leave this out. A hidden form field people never fill; a submission that fills it is dropped as a bot.
Generated from AmbassadorPartnerApplicationRequest

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": {
    "status": "received"
  }
}
status"received"
Always `received`, whether this is a new application or an update to one already sent, so the form cannot be used to find out who has applied.
Generated from AmbassadorPartnerApplicationResultResponse

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: a name and a valid email are required, and the two bands take only the listed values.
applications_closedHTTP 409
The organiser is not taking partner applications for this event.
rate_limitedHTTP 429
Too many applications from this address, or too many waiting for the organiser to review. Try again later.

Notes

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