API

Baue auf Qwizbee

Lass deine App sofort wissen, wenn ein Quiz startet oder endet. Nutzer verbinden sich mit einem Klick, du empfängst signierte Webhooks, und niemand teilt jemals ein Passwort oder Konto-Token.

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

So funktioniert die Verbindung

Drei Schritte, ohne Entwicklerportal und ohne Vorab-Registrierung. Deine App erzeugt einen PKCE-Verifier, der Nutzer stimmt einmal zu, und du speicherst den zurückkommenden API-Schlüssel.

  1. Connect-Button einbauen

    Schicke den Nutzer zu qwizbee.com/connect mit deinem App-Namen, einer https-Redirect-URI, einer PKCE-Challenge und einem zufälligen state-Wert.

  2. Der Nutzer stimmt zu

    Qwizbee zeigt eine Zustimmungsseite in der Sprache des Nutzers. Nach Erlauben kehrt der Browser zu deiner Redirect-URI zurück — mit einem einmaligen Code (2 Minuten gültig) und deinem state.

  3. Code eintauschen

    Dein Server tauscht Code plus PKCE-Verifier an POST /api/integrations/token gegen einen qbw_… API-Schlüssel. Der Schlüssel verwaltet nur Webhooks — sonst nichts im Konto.

Der Connect-Link

Erzeuge Verifier und Challenge auf deinem Server und bewahre den Verifier in der Session des Nutzers auf:

// 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}

Der state-Wert kommt unverändert zurück (CSRF-Schutz), und die Redirect-URI muss https sein (http://localhost ist zum Testen erlaubt).

// 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

Die Events

Registriere nach dem Verbinden einen Webhook mit dem API-Schlüssel. Qwizbee POSTet danach eine signierte JSON-Nachricht an deine URL, sobald ein Quiz des Nutzers startet oder endet.

quiz.started

Feuert, sobald der Host auf Start drückt.

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

Feuert, wenn die Endstände erscheinen. Ergänzt den Gewinner.

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

Payload-Felder

event
quiz.started oder quiz.completed
timestamp
ISO-8601-Zeitpunkt des Events
data.sessionId
interne Session-ID, stabil pro Spiel
data.roomCode
der 6-stellige Raumcode, mit dem Spieler beitreten
data.quizId / quizTitle
welches Quiz es war
data.playerCount / questionCount
wie viele Spieler und Fragen
data.winner
nur bei quiz.completed: Nickname und Punktzahl von Platz 1, oder null

Signatur prüfen

Jede Zustellung ist mit dem eigenen Secret des Webhooks signiert (einmalig bei der Registrierung angezeigt). Prüfe den rohen Request-Body — nicht ein neu serialisiertes Objekt — bevor du einer Zustellung vertraust:

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-Referenz

Base-URL der Produktions-API. JWT bedeutet das Login-Token des Nutzers; API-Schlüssel bedeutet einen qbw_…-Schlüssel aus dem Connect-Flow. Webhook-Routen akzeptieren beides.

Connect-Flow

  • GET/api/connect/authorizeJWT

    Connect-Parameter validieren; liefert den App-Namen für die Zustimmungsseite.

  • POST/api/connect/approveJWT

    Zustimmen und die Redirect-URL mit einmaligem Code und state erhalten.

  • POST/api/integrations/tokenpublic

    Code + code_verifier gegen den abgetrennten API-Schlüssel (qbw_…) tauschen.

  • GET/api/integrationsJWT

    Verbundene Apps des Nutzers mit Zeitstempeln der letzten Nutzung.

  • DELETE/api/integrations/:idJWT

    Verbindung trennen. Ihre Webhooks werden sofort gelöscht, der Schlüssel stoppt.

Webhooks

  • POST/api/webhooksJWT oder API-Schlüssel

    Webhook registrieren: { url, events }. Die Antwort enthält das Signing-Secret — nur einmal sichtbar.

  • GET/api/webhooksJWT oder API-Schlüssel

    Registrierte Webhooks auflisten.

  • DELETE/api/webhooks/:idJWT oder API-Schlüssel

    Webhook entfernen.

  • GET/api/webhooks/:id/deliveriesJWT oder API-Schlüssel

    Die letzten 50 Zustellversuche mit Statuscodes, zum Debuggen.

Zustellverhalten

  • Fehlgeschlagene Zustellungen werden nach 1, 5 und 25 Sekunden wiederholt (insgesamt 4 Versuche).
  • Dein Endpunkt muss innerhalb von 5 Sekunden mit einem 2xx-Status antworten.
  • Client-Fehler (4xx) werden nicht wiederholt — du hast abgelehnt, also hören wir auf.
  • Jeder Versuch wird protokolliert und über den Deliveries-Endpunkt sichtbar.
  • Wird die Verbindung auf der Kontoseite des Nutzers getrennt, stoppen alle Zustellungen sofort.

Integration testen

  • Registriere einen Webhook auf eine https://webhook.site-URL, um echte Zustellungen zu inspizieren.
  • http://localhost und http://127.0.0.1 sind als Webhook- und Redirect-Ziele erlaubt, du kannst also lokal entwickeln.
  • Das Zustellprotokoll (GET /api/webhooks/:id/deliveries) zeigt die exakten Statuscodes, die wir von dir erhalten haben.

Fragen oder fehlt ein Event?

contact@kylevanraaij.com Schreib eine Mail und erzähl uns, was du baust — Events auf Spieler-Ebene stehen auf der Roadmap.