NjanggoNjanggoDocumentation API
Plus

Webhooks calendrier

Recevez les événements de tous vos events sur une seule URL — avec signature HMAC.

POST/calendars/:slug/webhooks
DivergentcreateCalendarWebhook

Try It! → /api/events/:eventId/webhooks

Chemin actuel : /api/events/:eventId/webhooks

Paramètres

Modifiez les valeurs puis cliquez Try It! à droite.

slugpathrequis

Segment d'URL :slug

eventIdpathrequis

Requis pour Try It! (backend actuel — /events/:eventId/webhooks)

Authorizationheaderrequis

Token Bearer Clerk (session dashboard)

Corps (JSON)

Pourquoi un webhook calendrier ?

Avant Plus, les webhooks se configuraient événement par événement. Avec le webhook calendrier, une seule URL reçoit les notifications de tous les events du calendrier — idéal pour Zapier, Make ou un micro-service central.

Statut : Divergent

Aujourd’hui le backend expose /api/events/:eventId/webhooks (auth Clerk, scope événement). La cible Plus est /api/calendars/:slug/webhooks. Les en-têtes et payloads convergeront — voir ci-dessous.

Actuel vs cible

AspectActuel (dashboard)Cible (Plus)
ScopePar événementPar calendrier
CréationPOST /api/events/:id/webhooksPOST /api/calendars/:slug/webhooks
Auth configBearer ClerkBearer dashboard
Header signatureX-Njanggo-Webhook-Signature: t=…,v1=…X-Njanggo-Signature: sha256=…
Header typeX-Njanggo-Webhook-EventX-Njanggo-Event
Header deliveryX-Njanggo-Delivery-Id (idempotence)
Payload calendarSlugAbsentPrésent sur chaque delivery
Logs + retry APIGET /api/v1/webhook-deliveries (Pro)

Types d'événements

TypeDéclenché quandCas d'usage
guest.registeredInscription (confirmée, en attente ou waitlist)CRM, Slack, Google Sheet
checkin.createdCheck-in à l'entréeCompteur live, écran géant
event.updatedModification titre, date, lieu…Sync site externe
event.cancelledAnnulation par l'organisateurAlerte équipe, remboursements auto
announcement.sentAnnonce e-mail envoyéeAudit communications
reminder.sentRappel automatique ou manuel envoyéSuivi campagnes

En-têtes HTTP (livraison entrante)

Njanggo envoie une requête POST vers votre URL avec les en-têtes suivants (cible Plus) :

En-têtes cible

Content-Typestringrequis

application/json

X-Njanggo-Eventstringrequis

Type — ex. guest.registered

X-Njanggo-Delivery-Idstringrequis

del_* — idempotence, ignorer les doublons

X-Njanggo-Signaturestringrequis

sha256=<hex> — HMAC-SHA256 du corps brut UTF-8

User-Agentstringoptionnel

Njanggo-Webhook/1.0

Actuel : le backend utilise X-Njanggo-Webhook-Signature: t=<unix>,v1=<hex> (HMAC du string {timestamp}.{body}) et X-Njanggo-Webhook-Event. Migration documentée dans le changelog — les deux formats coexisteront brièvement.

Signature HMAC

Cible Plus — calculez HMAC-SHA256(secret, rawBody) en hex, comparez en temps constant avec X-Njanggo-Signature (prefixe sha256=).

Node.js — vérification cible
import crypto from 'crypto';

function verifyWebhook(rawBody: string, signature: string, secret: string) {
  const expected = crypto.createHmac('sha256', secret).update(rawBody, 'utf8').digest('hex');
  const received = signature.replace(/^sha256=/, '');
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received));
}
Node.js — format actuel (event webhooks)
function verifyLegacy(rawBody: string, header: string, secret: string) {
  const parts = Object.fromEntries(header.split(',').map((p) => p.trim().split('=')));
  const payload = `${parts.t}.${rawBody}`;
  const expected = crypto.createHmac('sha256', secret).update(payload, 'utf8').digest('hex');
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1 ?? ''));
}

Créer un webhook

Aujourd’hui (live)

Créez le webhook depuis Gérer l'événement → Webhooks ou via POST /api/events/:eventId/webhooks (Bearer). La cible calendrier ci-dessous est en déploiement.

Corps POST /events/:eventId/webhooks (actuel)

urlurirequis

URL HTTPS (Zapier, Make, serveur)

eventsstring[]optionnel

guest.registered, checkin.created, event.updated, …

secretstringoptionnel

Optionnel — whsec_… généré si omis (affiché une fois)

Corps POST /calendars/:slug/webhooks (cible Plus)

urlurirequis

URL HTTPS de votre endpoint (Zapier, Make, serveur)

eventsstring[]optionnel

Sous-ensemble des 6 types. Défaut : tous.

secretstringoptionnel

Secret HMAC custom. Si omis, Njanggo génère whsec_… (affiché une fois).

Requête

application/json
{
  "url": "https://hooks.zapier.com/hooks/catch/123456/abcdef/",
  "events": ["guest.registered", "checkin.created"]
}

Réponse

201
application/json
{
  "data": {
    "id": "wh_cal_01",
    "url": "https://hooks.zapier.com/hooks/catch/123456/abcdef/",
    "events": ["guest.registered", "checkin.created"],
    "isActive": true,
    "plainSecret": "whsec_a1b2c3d4e5f6...",
    "createdAt": "2026-07-04T10:00:00.000Z"
  }
}

Endpoints

Actuel (live) — par événement

GETPlus
/events/:eventId/webhooks

Lister

POSTPlus
/events/:eventId/webhooks

Créer

PATCHPlus
/events/:eventId/webhooks/:id

Modifier

DELETEPlus
/events/:eventId/webhooks/:id

Supprimer

POSTPlus
/events/:eventId/webhooks/:id/test

Test

Cible Plus — par calendrier

GETPlus
/calendars/:slug/webhooks

Lister les webhooks du calendrier

POSTPlus
/calendars/:slug/webhooks

Créer

PATCHPlus
/calendars/:slug/webhooks/:id

Modifier url, events, isActive

DELETEPlus
/calendars/:slug/webhooks/:id

Supprimer

POSTPlus
/calendars/:slug/webhooks/:id/test

Envoyer un événement test

Exemples de payloads

guest.registered

Webhook entrant

200
application/json
{
  "id": "del_8f2a1b3c",
  "type": "guest.registered",
  "createdAt": "2026-07-04T18:30:00.000Z",
  "calendarSlug": "mon-collectif",
  "data": {
    "eventId": "evt_abc123",
    "eventSlug": "soiree-jazz",
    "eventTitle": "Soirée Jazz",
    "guestId": "gst_xyz789",
    "userId": "usr_456",
    "guestEmail": "alice@example.com",
    "guestName": "Alice Martin",
    "status": "CONFIRMED",
    "ticketId": "tkt_standard",
    "ticketName": "Entrée standard",
    "registrationAnswers": [
      { "question": "Organisation", "answer": "Association Lumière" }
    ]
  }
}

checkin.created

Webhook entrant

200
application/json
{
  "id": "del_9a3b2c1d",
  "type": "checkin.created",
  "createdAt": "2026-07-04T20:15:00.000Z",
  "calendarSlug": "mon-collectif",
  "data": {
    "eventId": "evt_abc123",
    "eventSlug": "soiree-jazz",
    "guestId": "gst_xyz789",
    "guestName": "Alice Martin",
    "guestEmail": "alice@example.com",
    "qrCode": "NJ-ABC123XYZ",
    "checkedInAt": "2026-07-04T20:15:00.000Z",
    "checkedInBy": "usr_staff_01"
  }
}
Vérifiez la signature — voir Authentification.

WebhookDelivery (logs Pro)

Chaque tentative de livraison crée un objet WebhookDelivery consultable via l’API Pro (Preview). Voir schéma WebhookDelivery.

GET /v1/webhook-deliveries/:id

200
application/json
{
  "data": {
    "id": "del_8f2a1b3c",
    "webhookId": "wh_cal_01",
    "eventType": "guest.registered",
    "status": "retrying",
    "attempts": 2,
    "responseCode": 503,
    "nextRetryAt": "2026-07-04T18:35:00.000Z",
    "payload": { "type": "guest.registered", "data": { "..." : "..." } }
  }
}
GETPro
/v1/webhook-deliveries

Filtrer par webhookId, status, eventType

POSTPro
/v1/webhook-deliveries/:id/retry

Relance manuelle

Retry & idempotence

Si votre endpoint ne répond pas HTTP 2xx sous 10 secondes, Njanggo retente jusqu'à 3 fois (délais : 1 min, 5 min, 30 min). Utilisez X-Njanggo-Delivery-Id pour ignorer les doublons.

Code réponseComportement
2xxSuccès — pas de retry
410 GoneWebhook désactivé automatiquement
429 / 5xxRetry planifié
Timeout 10sRetry planifié