API

Bouw op Qwizbee

Laat je app op het moment zelf weten dat een quiz start of klaar is. Gebruikers verbinden met één klik, jij ontvangt ondertekende webhooks, en niemand deelt ooit een wachtwoord of account-token.

https://qwizbee-api-production.up.railway.app

Zo werkt de verbinding

Drie stappen, zonder developer-portaal en zonder vooraf registreren. Je app genereert een PKCE-verifier, de gebruiker geeft één keer toestemming, en jij bewaart de API-sleutel die terugkomt.

  1. Zet er een Verbinden-knop op

    Stuur de gebruiker naar qwizbee.com/connect met je app-naam, een https-redirect-URI, een PKCE-challenge en een willekeurige state-waarde.

  2. De gebruiker geeft toestemming

    Qwizbee toont een toestemmingspagina in de taal van de gebruiker. Na Toestaan keert de browser terug naar jouw redirect-URI met een eenmalige code (2 minuten geldig) en jouw state.

  3. Wissel de code in

    Je server wisselt de code plus PKCE-verifier via POST /api/integrations/token in voor een qbw_… API-sleutel. Die sleutel beheert uitsluitend webhooks, verder niets in het account.

De verbindlink

Genereer verifier en challenge op je server en bewaar de verifier in de sessie van de gebruiker:

// on your server
const verifier  = base64url(crypto.randomBytes(32));
const challenge = base64url(sha256(verifier));

// your "Connect to Qwizbee" button points at:
https://www.qwizbee.com/connect
  ?name=Supergood Mail
  &redirect_uri=https://www.supergood-mail.com/callback
  &code_challenge=${challenge}
  &state=${randomState}

// after the user approves you are called back at:
https://www.supergood-mail.com/callback?code=${code}&state=${randomState}

De state-waarde komt ongewijzigd terug zodat je jezelf tegen CSRF kunt beschermen, en de redirect-URI moet https zijn (http://localhost mag voor het testen).

// exchange the code for the scoped API key
const res = await fetch('https://qwizbee-api-production.up.railway.app/api/integrations/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ code, code_verifier: verifier }),
});
const { access_token } = await res.json(); // qbw_… — store it safely

De events

Registreer na het verbinden een webhook met de API-sleutel. Vanaf dat moment POST Qwizbee een ondertekend JSON-bericht naar jouw URL zodra een quiz van de gebruiker start of klaar is.

quiz.started

Vuurt zodra de host op Start drukt.

POST https://your-app.com/webhooks/qwizbee
X-Qwizbee-Event: quiz.started
X-Qwizbee-Signature: <hmac-sha256>

{
  "event": "quiz.started",
  "timestamp": "2026-10-08T13:05:22.410Z",
  "data": {
    "sessionId": "s_ab12cd",
    "roomCode": "KGMZ34",
    "quizId": "qz_9x8y",
    "quizTitle": "Friday afternoon quiz",
    "playerCount": 14,
    "questionCount": 10
  }
}

quiz.completed

Vuurt wanneer de eindstanden verschijnen. Voegt de winnaar toe.

{
  "event": "quiz.completed",
  "timestamp": "2026-10-08T13:12:40.911Z",
  "data": {
    ...same fields as quiz.started,
    "winner": { "nickname": "BeeMercia", "score": 8420 }
  }
}

Payload-velden

event
quiz.started of quiz.completed
timestamp
ISO 8601-tijdstip van het event
data.sessionId
interne sessie-id, stabiel per quiz
data.roomCode
de 6-tekens roomcode waarmee spelers meedoen
data.quizId / quizTitle
welke quiz het was
data.playerCount / questionCount
hoeveel spelers en vragen
data.winner
alleen bij quiz.completed: nickname en score van nummer 1, of null

De handtekening verifiëren

Elke levering is ondertekend met het eigen secret van de webhook (één keer zichtbaar bij registratie). Verifieer de rauwe request-body, dus niet een opnieuw geserialiseerd object, voordat je een levering vertrouwt:

import { createHmac, timingSafeEqual } from 'node:crypto';

app.post('/webhooks/qwizbee', express.raw({ type: 'application/json' }), (req, res) => {
  const expected = createHmac('sha256', WEBHOOK_SECRET).update(req.body).digest('hex');
  const got = req.headers['x-qwizbee-signature'] || '';
  const valid = expected.length === got.length &&
    timingSafeEqual(Buffer.from(expected), Buffer.from(got));
  if (!valid) return res.status(401).end();

  const event = JSON.parse(req.body);
  if (event.event === 'quiz.started') { /* … */ }
  res.status(200).end();
});

API-referentie

Base-URL van de productie-API. JWT betekent het login-token van de gebruiker; API-sleutel betekent een qbw_…-sleutel uit de connect-flow. Webhook-routes accepteren beide.

Connect-flow

  • GET/api/connect/authorizeJWT

    De verbindparameters valideren; geeft de app-naam terug voor de toestemmingspagina.

  • POST/api/connect/approveJWT

    Toestemmen en de redirect-URL met eenmalige code en state terugkrijgen.

  • POST/api/integrations/tokenpublic

    Code + code_verifier inwisselen voor de afgeschermde API-sleutel (qbw_…).

  • GET/api/integrationsJWT

    De verbonden apps van de gebruiker met tijdstempels van laatste gebruik.

  • DELETE/api/integrations/:idJWT

    Verbinding verbreken. De webhooks worden direct verwijderd en de sleutel werkt niet meer.

Webhooks

  • POST/api/webhooksJWT of API-sleutel

    Webhook registreren: { url, events }. Het antwoord bevat het signing-secret, dat maar één keer zichtbaar is.

  • GET/api/webhooksJWT of API-sleutel

    Geregistreerde webhooks tonen.

  • DELETE/api/webhooks/:idJWT of API-sleutel

    Webhook verwijderen.

  • GET/api/webhooks/:id/deliveriesJWT of API-sleutel

    De laatste 50 leverpogingen met statuscodes, voor het debuggen.

Levergedrag

  • Mislukte leveringen worden herhaald na 1, 5 en 25 seconden (in totaal 4 pogingen).
  • Je endpoint moet binnen 5 seconden antwoorden met een 2xx-status.
  • Client-fouten (4xx) worden niet herhaald: jij weigerde duidelijk, dus wij stoppen.
  • Elke poging wordt gelogd en zichtbaar via het deliveries-endpoint.
  • Verbinding verbreken op de Account-pagina van de gebruiker stopt alle leveringen direct.

Je integratie testen

  • Registreer een webhook op een https://webhook.site-URL om echte leveringen te inspecteren.
  • http://localhost en http://127.0.0.1 zijn toegestaan als webhook- en redirect-doel, dus je kunt lokaal ontwikkelen.
  • Het leverlogboek (GET /api/webhooks/:id/deliveries) toont de exacte statuscodes die we van jou ontvingen.

Vragen of mis je een event?

contact@kylevanraaij.com Mail ons en vertel wat je bouwt: events op quizniveau staan op de roadmap.