API
Construye sobre Qwizbee
Que tu app sepa al instante cuándo empieza o termina un quiz. Los usuarios conectan con un clic, tú recibes webhooks firmados y nadie comparte nunca una contraseña ni un token de cuenta.
https://qwizbee-api-production.up.railway.app
Cómo funciona la conexión
Tres pasos, sin portal de desarrolladores ni registro previo. Tu app genera un verificador PKCE, el usuario aprueba una vez y tú guardas la clave API que vuelve.
Añade un botón Conectar
Envía al usuario a qwizbee.com/connect con el nombre de tu app, una URI de redirección https, un desafío PKCE y un valor state aleatorio.
El usuario aprueba
Qwizbee muestra una página de consentimiento en el idioma del usuario. Tras Permitir, el navegador vuelve a tu URI de redirección con un código de un solo uso (válido 2 minutos) y tu state.
Canjea el código
Tu servidor cambia el código más el verificador PKCE en POST /api/integrations/token por una clave API qbw_…. Esa clave solo gestiona webhooks; nada más de la cuenta.
El enlace de conexión
Genera el verificador y el desafío en tu servidor y guarda el verificador en la sesión del usuario:
// 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}El valor state vuelve intacto para protegerte del CSRF, y la URI de redirección debe ser https (http://localhost se permite para pruebas).
// 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 safelyLos eventos
Tras conectar, registra un webhook con la clave API. Desde entonces Qwizbee envía por POST un mensaje JSON firmado a tu URL cada vez que un quiz del usuario empieza o termina.
quiz.started
Se dispara cuando el host pulsa Empezar.
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
Se dispara cuando aparecen las posiciones finales. Añade al ganador.
{
"event": "quiz.completed",
"timestamp": "2026-10-08T13:12:40.911Z",
"data": {
...same fields as quiz.started,
"winner": { "nickname": "BeeMercia", "score": 8420 }
}
}Campos del payload
- event
- quiz.started o quiz.completed
- timestamp
- momento del evento en ISO 8601
- data.sessionId
- id interno de sesión, estable por quiz
- data.roomCode
- el código de sala de 6 caracteres con el que entran los jugadores
- data.quizId / quizTitle
- qué quiz era
- data.playerCount / questionCount
- cuántos jugadores y preguntas
- data.winner
- solo en quiz.completed: nickname y puntuación del número 1, o null
Verificar la firma
Cada entrega se firma con el secret propio del webhook (se muestra una vez al registrarlo). Verifica el cuerpo bruto de la petición, no un objeto re-serializado, antes de confiar en una entrega:
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();
});Referencia de la API
URL base de la API de producción. JWT significa el token de sesión del usuario; clave API significa una clave qbw_… del flujo de conexión. Las rutas de webhooks aceptan ambas.
Flujo de conexión
- GET
/api/connect/authorizeJWTValida los parámetros de conexión; devuelve el nombre de la app para la página de consentimiento.
- POST
/api/connect/approveJWTAprobar y recibir la URL de redirección con el código de un solo uso y el state.
- POST
/api/integrations/tokenpublicCanjear código + code_verifier por la clave API aislada (qbw_…).
- GET
/api/integrationsJWTLas apps conectadas del usuario con marcas de último uso.
- DELETE
/api/integrations/:idJWTCortar la conexión. Sus webhooks se borran al instante y la clave deja de funcionar.
Webhooks
- POST
/api/webhooksJWT o clave APIRegistrar un webhook: { url, events }. La respuesta contiene el secret de firma, visible solo una vez.
- GET
/api/webhooksJWT o clave APIListar los webhooks registrados.
- DELETE
/api/webhooks/:idJWT o clave APIEliminar un webhook.
- GET
/api/webhooks/:id/deliveriesJWT o clave APILos últimos 50 intentos de entrega con códigos de estado, para depurar.
Comportamiento de entrega
- Las entregas fallidas se reintentan tras 1, 5 y 25 segundos (4 intentos en total).
- Tu endpoint debe responder en 5 segundos con cualquier estado 2xx.
- Los errores de cliente (4xx) no se reintentan: rechazaste claramente, así que paramos.
- Cada intento queda registrado y visible vía el endpoint de entregas.
- Cortar la conexión en la página Cuenta del usuario detiene todas las entregas al instante.
Probar tu integración
- Registra un webhook hacia una URL de https://webhook.site para inspeccionar entregas reales.
- http://localhost y http://127.0.0.1 se aceptan como destino de webhook y de redirección, así que puedes desarrollar en local.
- El registro de entregas (GET /api/webhooks/:id/deliveries) muestra los códigos de estado exactos que recibimos de ti.
¿Preguntas o falta un evento?
contact@kylevanraaij.com Escribe un correo y cuéntanos qué construyes: los eventos por jugador están en la hoja de ruta.