Start payment
/v1/events/{slug}/tickets/orders/{checkoutToken}/payCreate the payment for a detailed order and return what Stripe.js needs to take the card on your page: the PaymentIntent's clientSecret and the publishable key to initialise Stripe with. Mount a Payment Element with the secret and confirm it; the selling tickets guide has the snippet.
The card form is Stripe's, rendered in an iframe on your page, so card numbers never touch your site or ours. The publishable key is Grofomo's platform key and is meant to be public; the client secret pays this one order and nothing else.
Calling again returns the same secret while the payment is still open, so a page reload does not create a second charge. Once the buyer has confirmed, tickets are issued when the payment is confirmed server-side, usually within a minute: poll order status until it reads completed.
On a payment plan the amount is the deposit plus the booking fee; the card is saved for the instalments. Free orders are refused here (nothing_to_pay): use complete free.
404 not_found for no visible reason.Request
curl -sS \
-X POST \
-H 'X-Grofomo-Key: pk_live_YOUR_PUBLISHABLE_KEY' \
'https://api.grofomo.com/v1/events/summer-fest/tickets/orders/YzNhMWU1ZDctOWIyZi00ZTZhLThjMGQtMmY0YjZhOGMwZTEyOjE3ODk2NDM2MDAwMDA6dGt0LWNoZWNrb3V0.ZXhhbXBsZS1zaWduYXR1cmU/pay?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`.
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
{
"apiVersion": 1,
"data": {
"clientSecret": "pi_3QbXk2Hn7v1Zq9Lm0aB1cD2e_secret_Fg3Hi4Jk5Lm6No7Pq8Rs9Tu0",
"publishableKey": "pk_live_51Hn7v1Zq9Lm0aB1cD2eFg3Hi4Jk5Lm6No7Pq8Rs9Tu0",
"paymentIntentId": "pi_3QbXk2Hn7v1Zq9Lm0aB1cD2e"
}
}clientSecretstring- The PaymentIntent client secret. Pass it to `stripe.elements({ clientSecret })` and confirm with `stripe.confirmPayment`. It only ever pays this one order.
publishableKeystring- The Stripe publishable key to initialise Stripe.js with. It is Grofomo's, not the organiser's, and it is meant to be public.
paymentIntentIdstring- The Stripe PaymentIntent id, for your own records.
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.
details_requiredHTTP 409- The buyer's details have not been sent yet. Call the details endpoint first.
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.
nothing_to_payHTTP 409- The order total is zero. Use complete-free.
already_processingHTTP 409- A payment on this order is mid-flight. Poll order status rather than starting another.
unavailableHTTP 409- The organiser has not finished connecting Stripe.
Notes
- Rate limited to roughly 20 requests per minute per caller. Guidance, not a contract — see caching and rate limits.
Cache-Control: no-store