Authentication
Publishable and secret keys, where each one belongs, and the two failure codes.
Examples use a fictional festival, summer-fest. Sign in at events.grofomo.com and they switch to your own events.
Requests are authenticated with a key in the X-Grofomo-Key header.
curl -H 'X-Grofomo-Key: pk_live_YOUR_PUBLISHABLE_KEY' \
'https://api.grofomo.com/v1/events/summer-fest/lineup?surface=web'Two kinds of key
Publishable — pk_live_…
Safe to ship in browser code. Buys you attribution, a rate-limit budget of its own, optional scoping to a single event, and instant revocation. It buys you no secrecy at all: it is visible in page source, and that is the design, not a leak. It can only read what the organiser has already published.
Secret — sk_live_…
For a server or a build step. Stored as a hash, so it is shown once at creation and never again. If it ends up in a browser bundle, treat it as compromised and rotate it.
Which to use is decided by where the code runs, not by how sensitive the data feels. Front-end fetch → publishable. Server-side fetch, static build, or a scheduled job → secret.
Passing the key
X-Grofomo-Key: <key>— the header. Always works, for both kinds.?key=<key>— the query string. Publishable keys only. A secret key sent this way is refused outright, because query strings end up in access logs,Refererheaders and browser history. The refusal is deliberate: silently accepting it would leak the key while looking like it worked.
When a key is required
Each organisation is in one of two modes:
open— requests without a key are answered. Organisations that predate keys are still here.key_required— a valid key is mandatory. Every new organisation starts here.
You cannot tell which mode an organisation is in from the outside, and you should not try. Always send a key. In open mode it is accepted and ignored; the day the organiser switches, your integration is already correct.
Failures
401 unauthorized — no key was sent and one is required, or the key is invalid, revoked, belongs to a different organisation, or is scoped to a different event. All of these return the same code on purpose: distinguishing them would let anyone probe which keys exist.
403 origin_not_allowed — the browser's Origin is not on the organiser's allowed-domains list. Only browsers can hit this; a server sends no Origin header. See CORS and allowed domains.
Neither is retryable. Both mean a configuration conversation with the organiser, not a backoff loop.
Rotation
Keys do not expire. When one needs replacing: mint the new key, deploy it, confirm traffic has moved, then revoke the old one. Revocation takes effect on the next request, so revoking first means downtime.