API REST v1WebhooksSABI Hub

API & Intégrations

Documentation pratique pour intégrer Sabi Workspace à vos systèmes. Connectez vos formulaires, sondages et billetterie à n'importe quel outil via notre API REST et nos webhooks.

Version
REST v1
Webhook
HMAC
Scopes
Projet
Base
https://sabi.tools

Authentification

Toutes les requêtes à l'API v1 nécessitent une clé API transmise dans le header Authorization.

Obtenir une clé API

  1. Connectez-vous à SABI Workspace
  2. Allez dans Paramètres → Clés API
  3. Cliquez sur Nouvelle clé
  4. Choisissez un nom et les permissions (scopes) nécessaires
  5. Copiez la clé — elle ne sera affichée qu'une seule fois
Format d'authentification
Authorization: Bearer sk_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Scopes disponibles

ScopeAccès
forms:readLire les formulaires, sondages et événements
responses:readLire les réponses et soumissions
contacts:readLire les contacts et tags
contacts:writeCréer et modifier les contacts
tickets:readLire les tickets et types
webhooks:readLire les webhooks configurés
webhooks:writeCréer et modifier les webhooks
*Accès complet (toutes les permissions)
Sécurité : Les clés API sont hashées (SHA-256) dans notre base de données. Ne partagez jamais votre clé dans du code côté client ou des dépôts publics. Utilisez des variables d'environnement.

Démarrage en 3 minutes

  1. Créez une clé dans Paramètres → Clés API avec les scopes minimum nécessaires.
  2. Testez l'accès avec GET /api/v1/forms.
  3. Configurez un webhook dans Paramètres → Webhooks pour recevoir les événements métier.
Premier appel
curl "https://sabi.tools/api/v1/forms?limit=10" \
  -H "Authorization: Bearer sk_votre_cle_api"
Les clés sont affichées une seule fois. Stockez-les dans une variable d'environnement, jamais dans le navigateur ou un dépôt Git.

Formulaires

GET/api/v1/formsforms:read

Liste paginée de tous les formulaires, sondages et événements du projet.

Paramètres

NomTypeRequisDescription
pagenumbernonNuméro de page (défaut: 1)
limitnumbernonRésultats par page (défaut: 50, max: 100)
typestringnonFiltrer par type : FORM, SURVEY ou TICKETING
Exemple de requête
curl -X GET "https://sabi.tools/api/v1/forms?type=FORM&page=1&limit=10" \
  -H "Authorization: Bearer sk_votre_cle_api"
Réponse
{
  "data": [
    {
      "id": "clxyz123...",
      "title": "Formulaire d'inscription",
      "type": "FORM",
      "slug": "inscription-abc123",
      "isPublished": true,
      "isArchived": false,
      "createdAt": "2026-01-15T10:00:00.000Z",
      "updatedAt": "2026-01-20T14:30:00.000Z",
      "_count": {
        "responses": 42,
        "questions": 8
      }
    }
  ],
  "total": 12,
  "page": 1,
  "limit": 10
}

Réponses

GET/api/v1/responsesresponses:read

Liste paginée des réponses soumises. Filtrable par formulaire et statut.

Paramètres

NomTypeRequisDescription
pagenumbernonNuméro de page (défaut: 1)
limitnumbernonRésultats par page (défaut: 50, max: 100)
formIdstringnonFiltrer par ID de formulaire
statusstringnonFiltrer par statut : NEW, HOT, WARM, COLD, PROCESSED
Exemple de requête
curl -X GET "https://sabi.tools/api/v1/responses?formId=clxyz123&status=NEW&limit=20" \
  -H "Authorization: Bearer sk_votre_cle_api"
Réponse
{
  "data": [
    {
      "id": "resp_abc123...",
      "formId": "clxyz123...",
      "status": "NEW",
      "score": 85,
      "submittedAt": "2026-08-15T09:30:00.000Z",
      "answers": [
        { "questionId": "q1...", "value": "Maeva Fosso" },
        { "questionId": "q2...", "value": "maeva@example.com" }
      ],
      "ticket": null
    }
  ],
  "total": 42,
  "page": 1,
  "limit": 20
}

Statuts de réponse

NEW — Nouvelle
HOT — Score ≥70%
WARM — Score ≥40%
COLD — Score <40%
PROCESSED — Traitée

Contacts

GET/api/v1/contactscontacts:read

Liste paginée des contacts du projet. Recherche par nom, email ou téléphone.

Paramètres

NomTypeRequisDescription
pagenumbernonNuméro de page
limitnumbernonRésultats par page (max: 100)
qstringnonRecherche dans nom, email, téléphone
tagIdstringnonFiltrer par tag
Exemple de requête
curl -X GET "https://sabi.tools/api/v1/contacts?q=william&limit=10" \
  -H "Authorization: Bearer sk_votre_cle_api"
Réponse
{
  "data": [
    {
      "id": "contact_abc...",
      "name": "William Akono",
      "email": "william@example.com",
      "phone": "+237699123456",
      "tags": [
        { "id": "tag1", "name": "VIP", "color": "#8B5CF6" }
      ],
      "_count": { "responses": 3, "tickets": 1 },
      "createdAt": "2026-03-10T08:00:00.000Z"
    }
  ],
  "total": 156,
  "page": 1,
  "limit": 10
}
GET/api/v1/contacts/:idcontacts:read

Détail d'un contact avec ses tags, dernières réponses et tickets.

Paramètres

NomTypeRequisDescription
idstringouiID du contact
Exemple de requête
curl -X GET "https://sabi.tools/api/v1/contacts/contact_abc123" \
  -H "Authorization: Bearer sk_votre_cle_api"
Réponse
{
  "id": "contact_abc123",
  "name": "Greg Hayatou",
  "email": "greg@example.com",
  "phone": "+237699123456",
  "tags": [{ "id": "tag1", "name": "VIP", "color": "#8B5CF6" }],
  "responses": [
    {
      "id": "resp_1",
      "formId": "form_1",
      "status": "HOT",
      "score": 92,
      "submittedAt": "2026-08-10T14:00:00.000Z"
    }
  ],
  "tickets": [
    {
      "id": "tkt_1",
      "status": "VALID",
      "createdAt": "2026-08-12T09:00:00.000Z",
      "ticketType": { "name": "VIP Pass" }
    }
  ]
}

Tickets

GET/api/v1/ticketstickets:read

Liste paginée des tickets vendus. Filtrable par statut.

Paramètres

NomTypeRequisDescription
pagenumbernonNuméro de page
limitnumbernonRésultats par page (max: 100)
statusstringnonFiltrer par statut : VALID, SCANNED, CANCELLED
Exemple de requête
curl -X GET "https://sabi.tools/api/v1/tickets?status=VALID&limit=20" \
  -H "Authorization: Bearer sk_votre_cle_api"
Réponse
{
  "data": [
    {
      "id": "tkt_abc123",
      "qrCode": "TKT-ABCDE12345",
      "status": "VALID",
      "createdAt": "2026-08-10T12:00:00.000Z",
      "ticketType": {
        "id": "type_1",
        "name": "Standard",
        "price": 5000,
        "currency": "XAF"
      },
      "response": {
        "id": "resp_1",
        "submittedAt": "2026-08-10T12:00:00.000Z"
      }
    }
  ],
  "total": 340,
  "page": 1,
  "limit": 20
}

Webhooks

Les webhooks envoient automatiquement des données à votre URL chaque fois qu'un événement se produit dans votre projet. Configurez-les dans Paramètres → Webhooks.

Configuration

  1. Allez dans Paramètres → Webhooks
  2. Cliquez sur Nouveau webhook
  3. Entrez votre URL de réception (HTTPS recommandé)
  4. Sélectionnez les événements à écouter
  5. Un secret est généré automatiquement — conservez-le pour vérifier les signatures

Vérification de signature (HMAC-SHA256)

Chaque requête webhook inclut un header X-Sabi-Signaturecontenant le HMAC-SHA256 du body avec votre secret. Vérifiez-le pour confirmer l'authenticité.

Vérification en Node.js
const crypto = require("crypto");

function verifyWebhook(body, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(body, "utf8")
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

// Dans votre handler Express/Fastify :
app.post("/webhook/sabi", (req, res) => {
  const signature = req.headers["x-sabi-signature"];
  const isValid = verifyWebhook(
    JSON.stringify(req.body),
    signature,
    process.env.SABI_WEBHOOK_SECRET
  );

  if (!isValid) return res.status(401).send("Invalid signature");

  const { event, data } = req.body;
  console.log("Événement reçu :", event, data);

  res.status(200).json({ ok: true });
});
Vérification en Python
import hmac, hashlib, json
from flask import Flask, request, abort

app = Flask(__name__)
WEBHOOK_SECRET = "whsec_votre_secret"

@app.route("/webhook/sabi", methods=["POST"])
def handle_webhook():
    signature = request.headers.get("X-Sabi-Signature", "")
    body = request.get_data(as_text=True)

    expected = hmac.new(
        WEBHOOK_SECRET.encode(),
        body.encode(),
        hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(signature, expected):
        abort(401)

    payload = json.loads(body)
    print(f"Événement: {payload['event']}", payload["data"])
    return {"ok": True}

Format de la requête webhook

Payload webhook
POST https://votre-site.com/webhook/sabi
Content-Type: application/json
X-Sabi-Signature: a1b2c3d4e5f6...

{
  "event": "response.created",
  "timestamp": "2026-08-15T10:30:00.000Z",
  "data": {
    "id": "resp_abc123",
    "formId": "form_xyz",
    "formTitle": "Inscription événement",
    "status": "NEW",
    "score": 0,
    "submittedAt": "2026-08-15T10:30:00.000Z",
    "answers": [
      { "question": "Nom complet", "value": "Marie Ngo" },
      { "question": "Email", "value": "marie@example.com" },
      { "question": "Téléphone", "value": "+237670123456" }
    ]
  }
}
Retry : Si votre serveur ne répond pas avec un code 2xx, SABI réessaie 3 fois avec backoff exponentiel (5s, 25s, 125s).

Types d'événements

Voici tous les événements disponibles pour les webhooks. Sélectionnez uniquement ceux dont vous avez besoin.

form.submitted— Formulaire soumis par un visiteur
form.published— Formulaire publié
form.archived— Formulaire archivé
survey.completed— Sondage complété
response.created— Nouvelle réponse enregistrée
response.updated— Statut de réponse modifié
contact.created— Nouveau contact créé
contact.updated— Contact mis à jour
contact.merged— Contacts fusionnés
ticket.purchased— Ticket acheté
ticket.scanned— Ticket scanné (QR)
ticket.cancelled— Ticket annulé
ticket.transferred— Ticket transféré
member.invited— Membre invité au projet
member.removed— Membre retiré du projet
invoice.created— Facture créée
invoice.paid— Facture payée
invoice.overdue— Facture en retard

Exemples d'intégration

📊

Google Sheets (automatique)

Chaque réponse ajoutée en temps réel

Utilisez un webhook + Google Apps Script pour synchroniser automatiquement les réponses.Voir le guide complet →

⚡

n8n / Make / Zapier

Automatisation no-code

Configurez un noeud Webhook dans votre outil d'automatisation, puis ajoutez l'URL générée dans Paramètres → Webhooks.

Exemple workflow n8n
1. Noeud "Webhook" → URL: https://votre-n8n.com/webhook/sabi
2. Noeud "IF" → Filtrer par event === "response.created"
3. Noeud "Google Sheets" → Ajouter ligne avec les réponses
4. Noeud "Slack" → Envoyer notification au canal #leads
🔧

API personnalisée (CRM, ERP, etc.)

Intégration directe par code

Exemple : synchroniser les contacts vers votre CRM
// Script Node.js — s'exécute via CRON toutes les heures
const API_KEY = process.env.SABI_API_KEY;
const BASE = "https://sabi.tools/api/v1";

async function syncContacts() {
  let page = 1;
  let hasMore = true;

  while (hasMore) {
    const res = await fetch(
      `${BASE}/contacts?page=${page}&limit=100`,
      { headers: { Authorization: `Bearer ${API_KEY}` } }
    );
    const { data, total } = await res.json();

    for (const contact of data) {
      await upsertToCRM({
        name: contact.name,
        email: contact.email,
        phone: contact.phone,
        tags: contact.tags.map(t => t.name),
        totalResponses: contact._count.responses,
      });
    }

    hasMore = page * 100 < total;
    page++;
  }

  console.log("Synchronisation terminée !");
}

syncContacts();
💬

WhatsApp Business (notification)

Envoyer un message à chaque nouvelle réponse

Webhook → WhatsApp via 360dialog
// Handler webhook SABI
app.post("/webhook/sabi", async (req, res) => {
  const { event, data } = req.body;

  if (event === "ticket.purchased") {
    const phone = data.answers.find(
      a => a.question.includes("Téléphone")
    )?.value;

    if (phone) {
      await fetch("https://waba.360dialog.io/v1/messages", {
        method: "POST",
        headers: {
          "D360-API-KEY": process.env.DIALOG_API_KEY,
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          messaging_product: "whatsapp",
          to: phone,
          type: "template",
          template: {
            name: "confirmation_billet",
            language: { code: "fr" },
            components: [{
              type: "body",
              parameters: [
                { type: "text", text: data.ticketType },
                { type: "text", text: data.qrCode },
              ],
            }],
          },
        }),
      });
    }
  }

  res.json({ ok: true });
});

SABI Hub (inter-apps)

SABI Hub permet l'interconnexion entre les applications de la suite SABI (Forms, SMS, Invoices, etc.). Les connexions utilisent l'authentification HMAC-SHA256 avec un secret partagé.

Créer une connexion

  1. Allez dans Paramètres → Connexions SABI
  2. Cliquez sur Nouvelle connexion
  3. Entrez l'App ID et l'URL de l'application cible
  4. Sélectionnez les scopes de données partagées
  5. Conservez le secret HMAC pour signer vos requêtes

Headers d'authentification Hub

Headers requis
X-Sabi-App-Id: votre_app_id
X-Sabi-Signature: hmac_sha256_du_body
X-Sabi-Timestamp: 2026-08-15T10:30:00.000Z

Scopes Hub disponibles

prospects:write— Créer des prospects
contacts:read— Lire les contacts
contacts:write— Créer/modifier les contacts
email:send— Envoyer des emails
email:templates— Accéder aux templates email
responses:read— Lire les réponses
forms:read— Lire les formulaires

Limites & quotas

RessourceGratuitPayant
Formulaires actifsIllimitéIllimité
Événements billetterie1 actifIllimité
Tickets par événement100 (gratuits uniquement)Illimité (payants OK)
Scan QR caméraNonOui
API v1 — paginationMax 100 résultats par page
Webhooks3 retries, backoff exponentiel
Collaboration : Invitez votre équipe sans plafonner les outils actifs du projet. Les limites métier restent sur les événements et les QR dynamiques.

Codes d'erreur

CodeSignificationSolution
401UnauthorizedClé API manquante, invalide ou expirée
403Insufficient scopeLa clé API n'a pas le scope requis pour cette action
404Not foundLa ressource demandée n'existe pas ou n'appartient pas à votre projet
429Rate limitedTrop de requêtes — attendez avant de réessayer
500Server errorErreur interne — contactez le support si persistant
Format d'erreur standard
{
  "error": "Insufficient scope",
  "message": "La clé API nécessite le scope 'forms:read' pour cette action"
}

Besoin d'aide ? Contactez-nous à assistance@sabi.tools

Conforme à la loi n° 2024/017 — SABI Workspace — Improved Agency, Douala, Cameroun