BitRaffleDocs

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

  1. Ask the entry question, if one is required

    Call GET /entry-question. If required is true, show the question, send the answer to POST /entry-question/verify, and keep the pass it returns.

  2. 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/…"
        }
      }
    }
  3. Hand the buyer the payment step

    If payment.redirectUrl is present, send the buyer there. Otherwise payment.data is an in-page payment form: pass the session to BitRaffle.checkout and it renders the right one.

  4. Wait for the tickets

    A session is done when ticketsIssued is true, not when a form is submitted. Poll GET /checkout/sessions/{id} every few seconds while the buyer waits, and listen for the entry.confirmed webhook 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

statusMeaning
pendingWaiting for the buyer or the payment provider.
paidPayment settled. Tickets are issued when ticketsIssued is true.
failedThe 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

ResponseWhy
400 bad_requestEntries 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 forbiddenCard payments are switched off for this competition.
404 not_foundThe competition is not your organisation's.
412 precondition_failedCard payments are not set up for your organisation yet.