Sell entries
Hosted checkout
Sell entries from your own product. Your backend starts a checkout session for a buyer; we price it, take the payment through the competition's payment provider, and issue the tickets.
The flow
Ask the entry question, if one is required
Call GET /entry-question. If
requiredis true, show the question, send the answer toPOST /entry-question/verify, and keep thepassit returns.Start a session
Send the competition, quantity and buyer. The buyer is identified by email or wallet address; they become (or map to) an account, so their tickets, emails and results share one identity.
curl -X POST https://www.bitraffle.io/api/v1/checkout/sessions \ -H "Authorization: Bearer $BITRAFFLE_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{ "competitionId": 71, "quantity": 2, "buyer": { "email": "buyer@example.com" }, "skillPass": "eyJxIjoxNywi…", "returnUrl": "https://your-site.com/raffle/return" }'const res = await fetch("https://www.bitraffle.io/api/v1/checkout/sessions", { method: "POST", headers: { Authorization: `Bearer ${process.env.BITRAFFLE_SECRET_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ competitionId: 71, quantity: 2, buyer: { email: "buyer@example.com" }, skillPass, // from /entry-question/verify returnUrl: "https://your-site.com/raffle/return", }), }); const { data: session } = await res.json();import os, requests res = requests.post( "https://www.bitraffle.io/api/v1/checkout/sessions", headers={"Authorization": f"Bearer {os.environ['BITRAFFLE_SECRET_KEY']}"}, json={ "competitionId": 71, "quantity": 2, "buyer": {"email": "buyer@example.com"}, "skillPass": skill_pass, # from /entry-question/verify "returnUrl": "https://your-site.com/raffle/return", }, ) session = res.json()["data"]201 Created{ "data": { "id": "RAF1789046392849TpO2yXOd", "status": "pending", "provider": "checkout", "amount": 400, "currency": "USD", "payment": { "type": "PAY_URL", "redirectUrl": "https://pay.checkout.com/…" } } }Hand the buyer the payment step
If
payment.redirectUrlis present, send the buyer there. Otherwisepayment.datais an in-page payment form: pass the session to BitRaffle.checkout and it renders the right one.Wait for the tickets
A session is done when
ticketsIssuedis true, not when a form is submitted. PollGET /checkout/sessions/{id}every few seconds while the buyer waits, and listen for theentry.confirmedwebhook as the source of truth.GET /checkout/sessions/{id}{ "data": { "id": "RAF1789046392849TpO2yXOd", "status": "paid", "competitionId": 71, "quantity": 2, "amount": 400, "currency": "USD", "ticketsIssued": true, "error": null } }
Session status
status | Meaning |
|---|---|
pending | Waiting for the buyer or the payment provider. |
paid | Payment settled. Tickets are issued when ticketsIssued is true. |
failed | The payment failed; error carries the provider's reason. Start a new session to try again. |
Returning to your site
Pass returnUrl and redirect-based payments bring the buyer back to it, with ?ref=<session id>&status=success|failed appended. It must be HTTPS, and its host must be listed under Settings → Webhooks → Partner return hosts in your admin, so a session can never redirect a buyer somewhere you did not choose. In-page payments (Stripe, Worldpay, CVPay QR) never leave your page.
What can refuse a session
| Response | Why |
|---|---|
400 bad_request | Entries are closed, the competition is sold out, the quantity is outside 1 to 100, or the entry question was required and no valid skillPass was sent. |
403 forbidden | Card payments are switched off for this competition. |
404 not_found | The competition is not your organisation's. |
412 precondition_failed | Card payments are not set up for your organisation yet. |