Webhooks

Recevez des notifications en temps réel sur les événements FluenzR.

Webhooks

Les webhooks vous permettent de recevoir des notifications en temps réel lorsque des événements se produisent dans FluenzR.

ℹ️ Info Les webhooks sont disponibles uniquement sur le plan Pro.

Comment ça fonctionne

  1. Vous configurez une URL de webhook dans FluenzR
  2. Quand un événement se produit, FluenzR envoie une requête HTTP POST à votre URL
  3. Votre serveur traite l'événement et répond avec un code 2xx

Événements disponibles

Contacts

ÉvénementDescription
contact.createdNouveau contact créé
contact.updatedContact modifié
contact.deletedContact supprimé

Campagnes

ÉvénementDescription
campaign.startedCampagne démarrée
campaign.pausedCampagne mise en pause
campaign.completedCampagne terminée

Emails

ÉvénementDescription
email.sentEmail envoyé
email.deliveredEmail délivré
email.openedEmail ouvert
email.clickedLien cliqué
email.repliedRéponse reçue
email.bouncedBounce détecté
email.unsubscribedDésinscription

Créer un webhook

Via l'interface

  1. Allez dans Paramètres > Webhooks
  2. Cliquez sur + Nouveau webhook
  3. Configurez l'URL et les événements
  4. Enregistrez

Via l'API

POST /webhooks
{
  "url": "https://your-server.com/webhooks/fluenzr",
  "events": [
    "email.sent",
    "email.opened",
    "email.replied"
  ],
  "secret": "your_webhook_secret"
}

Exemple

curl -X POST "https://api.fluenzr.co/v1/webhooks" \
  -H "Authorization: Bearer flz_sk_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://your-server.com/webhooks/fluenzr",
    "events": ["email.opened", "email.replied"],
    "secret": "whsec_abc123"
  }'

Réponse

{
  "success": true,
  "data": {
    "id": "whk_abc123",
    "url": "https://your-server.com/webhooks/fluenzr",
    "events": ["email.opened", "email.replied"],
    "status": "active",
    "createdAt": "2025-01-15T10:30:00Z"
  }
}

Format des payloads

Structure générale

{
  "id": "evt_abc123",
  "type": "email.opened",
  "timestamp": "2025-01-15T14:32:00Z",
  "data": {
    // Données spécifiques à l'événement
  }
}

email.sent

{
  "id": "evt_abc123",
  "type": "email.sent",
  "timestamp": "2025-01-15T10:00:00Z",
  "data": {
    "campaignId": "cmp_xyz789",
    "campaignName": "Prospection Q1",
    "contactId": "con_def456",
    "contactEmail": "jean@example.com",
    "emailNodeId": "node_2",
    "emailSubject": "Jean, question rapide"
  }
}

email.opened

{
  "id": "evt_abc124",
  "type": "email.opened",
  "timestamp": "2025-01-15T14:32:00Z",
  "data": {
    "campaignId": "cmp_xyz789",
    "contactId": "con_def456",
    "contactEmail": "jean@example.com",
    "emailNodeId": "node_2",
    "openCount": 1,
    "firstOpen": true,
    "userAgent": "Mozilla/5.0...",
    "ip": "192.168.1.1"
  }
}

email.clicked

{
  "id": "evt_abc125",
  "type": "email.clicked",
  "timestamp": "2025-01-15T14:35:00Z",
  "data": {
    "campaignId": "cmp_xyz789",
    "contactId": "con_def456",
    "contactEmail": "jean@example.com",
    "emailNodeId": "node_2",
    "linkUrl": "https://fluenzr.co/demo",
    "clickCount": 1
  }
}

email.replied

{
  "id": "evt_abc126",
  "type": "email.replied",
  "timestamp": "2025-01-15T15:10:00Z",
  "data": {
    "campaignId": "cmp_xyz789",
    "contactId": "con_def456",
    "contactEmail": "jean@example.com",
    "emailNodeId": "node_2",
    "replySubject": "Re: Jean, question rapide",
    "replyPreview": "Bonjour, je serais intéressé par..."
  }
}

email.bounced

{
  "id": "evt_abc127",
  "type": "email.bounced",
  "timestamp": "2025-01-15T10:05:00Z",
  "data": {
    "campaignId": "cmp_xyz789",
    "contactId": "con_def456",
    "contactEmail": "jean@example.com",
    "bounceType": "hard",
    "bounceReason": "User unknown"
  }
}

contact.created

{
  "id": "evt_abc128",
  "type": "contact.created",
  "timestamp": "2025-01-15T09:00:00Z",
  "data": {
    "contact": {
      "id": "con_def456",
      "email": "jean@example.com",
      "firstName": "Jean",
      "lastName": "Dupont",
      "company": "Acme Corp"
    }
  }
}

Vérification de signature

Chaque webhook inclut une signature pour vérifier son authenticité.

Headers

X-FluenzR-Signature: sha256=abc123...
X-FluenzR-Timestamp: 1640000000

Vérification (Node.js)

const crypto = require('crypto');

function verifyWebhookSignature(payload, signature, timestamp, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${payload}`)
    .digest('hex');

  return `sha256=${expectedSignature}` === signature;
}

// Dans votre endpoint
app.post('/webhooks/fluenzr', (req, res) => {
  const signature = req.headers['x-fluenzr-signature'];
  const timestamp = req.headers['x-fluenzr-timestamp'];
  const payload = JSON.stringify(req.body);

  if (!verifyWebhookSignature(payload, signature, timestamp, WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }

  // Traiter l'événement
  const event = req.body;
  console.log(`Event received: ${event.type}`);

  res.status(200).send('OK');
});

Vérification (Python)

import hmac
import hashlib

def verify_webhook_signature(payload, signature, timestamp, secret):
    expected = hmac.new(
        secret.encode(),
        f"{timestamp}.{payload}".encode(),
        hashlib.sha256
    ).hexdigest()

    return f"sha256={expected}" == signature

# Dans votre endpoint Flask
@app.route('/webhooks/fluenzr', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('X-FluenzR-Signature')
    timestamp = request.headers.get('X-FluenzR-Timestamp')
    payload = request.get_data(as_text=True)

    if not verify_webhook_signature(payload, signature, timestamp, WEBHOOK_SECRET):
        return 'Invalid signature', 401

    event = request.get_json()
    print(f"Event received: {event['type']}")

    return 'OK', 200

Gestion des retries

FluenzR réessaie automatiquement les webhooks en échec :

TentativeDélai
1Immédiat
21 minute
35 minutes
430 minutes
52 heures

Après 5 échecs, le webhook est marqué comme "failed".

Codes de succès

Votre serveur doit répondre avec un code HTTP 2xx dans les 30 secondes.

CodeRésultat
200-299Succès
3xxRetry
4xxÉchec permanent
5xxRetry
TimeoutRetry

Gérer les webhooks

Lister les webhooks

GET /webhooks

Modifier un webhook

PUT /webhooks/:id
{
  "events": ["email.opened", "email.replied", "email.clicked"]
}

Supprimer un webhook

DELETE /webhooks/:id

Tester un webhook

POST /webhooks/:id/test

Envoie un événement de test à votre URL.

Voir les logs

GET /webhooks/:id/logs

Récupère l'historique des tentatives de livraison.

{
  "success": true,
  "data": [
    {
      "id": "log_abc123",
      "eventId": "evt_xyz789",
      "eventType": "email.opened",
      "status": "delivered",
      "statusCode": 200,
      "responseTime": 145,
      "timestamp": "2025-01-15T14:32:00Z"
    }
  ]
}

Bonnes pratiques

Répondre rapidement

Votre endpoint doit répondre en moins de 30 secondes. Effectuez le traitement en arrière-plan si nécessaire.

Idempotence

Votre code doit gérer les événements dupliqués. Utilisez l'ID de l'événement pour dédupliquer.

Monitoring

Surveillez vos webhooks :

  • Taux de succès
  • Temps de réponse
  • Erreurs fréquentes

Sécurité

  • Vérifiez toujours la signature
  • Utilisez HTTPS uniquement
  • Validez le timestamp (rejeter si > 5 minutes)