Aller au contenu

Démarrage rapide

De zéro à un webhook vérifié en cinq minutes, entièrement dans l’environnement de test.

Dans ce guide, vous créez une clé de test, vérifiez à quel compte elle appartient, ajoutez un client, lui préparez un devis, le lui envoyez et recevez les notifications de Cord sur votre localhost avec la signature vérifiée. Rien de ce que vous faites ici ne touche vos données réelles : une clé sk_test_ travaille sur l’environnement de test de votre compte.

L’e-mail de test part bel et bien

Envoyer un devis de test adresse un vrai e-mail au client, avec l’objet marqué comme test. Utilisez une adresse e-mail à vous à l’étape 4.

  1. Créez une clé de test. Dans l’application, activez le Mode développeur dans Votre compte › Préférences et ouvrez la pastille Développeurs dans le coin inférieur droit. Dans l’onglet Clés API, créez une clé Secrète en lecture et écriture en mode test (si l’application est déjà en mode test, la clé est créée comme clé de test). Elle commence par sk_test_ et ne s’affiche qu’une seule fois : copiez-la.

    L’application dans l’environnement de test avec le Workbench ouvert sur l’onglet Clés API : la clé secrète en lecture et écriture qui vient d’être créée s’affiche en entier une seule fois, sk_test_ suivi de 48 caractères, avec son bouton Copier ; derrière, la nouvelle ligne figure déjà dans le tableau comme Test et Secrète.

    Enregistrez-la dans une variable de votre terminal. La CLI de Cord la lit aussi à cet endroit :

    export CORD_API_KEY="sk_test_..."
  2. Vérifiez la clé. GET /api/v1/me renvoie l’organisation, le droit et le mode de la clé :

    curl https://cordhq.app/api/v1/me \
      -H "Authorization: Bearer $CORD_API_KEY"
    {
      "data": {
        "org": { "id": "5b1f7d9e-3c2a-4e8b-9f61-0a7c4d2e8b13", "nombre": "Aceros del Norte", "plan": "pro" },
        "scope": "write",
        "mode": "test"
      }
    }

    Si vous voyez "mode": "test", tout ce qui suit se passe dans votre environnement de test. Un 401 avec code: "invalid_key" signifie que la clé a été mal copiée ou qu’elle a été révoquée.

  3. Écoutez les webhooks sur votre machine. Cord ne peut pas appeler votre localhost : la CLI ouvre donc une session d’écoute et vous retransmet chaque événement avec les vrais en-têtes de signature. D’abord, le serveur qui les reçoit. Installez le SDK dans un dossier vide :

    npm init -y && npm install @flouviahq/node

    Enregistrez ceci sous le nom server.mjs :

    import { createServer } from 'node:http';
    import { constructEvent, CordWebhookSignatureError } from '@flouviahq/node';
    
    createServer(async (req, res) => {
      if (req.method !== 'POST' || req.url !== '/webhooks/cord') {
        res.writeHead(404).end();
        return;
      }
      // La signature est calculée sur le corps BRUT : ne l’analysez pas avant de la vérifier.
      const chunks = [];
      for await (const chunk of req) chunks.push(chunk);
      try {
        const event = await constructEvent(Buffer.concat(chunks), req.headers, process.env.CORD_WEBHOOK_SECRET);
        console.log(event.event, event.id, event.data.id);
        res.writeHead(200).end('ok');
      } catch (err) {
        if (err instanceof CordWebhookSignatureError) {
          console.error('Signature rejected:', err.code);
          res.writeHead(400).end(err.code);
          return;
        }
        throw err;
      }
    }).listen(3000, () => console.log('Listening on http://localhost:3000/webhooks/cord'));

    Dans un second terminal (avec la même CORD_API_KEY), ouvrez la session d’écoute :

    npx @flouviahq/cli listen --forward-to http://localhost:3000/webhooks/cord

    La CLI affiche le secret de la session (whsec_…). Démarrez votre serveur avec ce secret dans le premier terminal :

    CORD_WEBHOOK_SECRET="whsec_..." node server.mjs

    Laissez les deux tourner. La session expire au bout de 24 heures et Ctrl+C la ferme.

  4. Créez un client. Avec une adresse e-mail à vous :

    curl https://cordhq.app/api/v1/clientes \
      -H "Authorization: Bearer $CORD_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "empresa": "Ferretería La Prueba", "contacto": "Ana Torres", "email": "your-email@example.com" }'
    { "data": { "id": "0e6f2b8a-91c4-4d7e-b3a5-6c2d8f1e4a90" } }

    Votre serveur affiche le premier événement :

    client.created evt_4c1e9a7b2d5f8e0a3b6c9d2e5f8a1b4c 0e6f2b8a-91c4-4d7e-b3a5-6c2d8f1e4a90
  5. Créez le devis. Utilisez l’id du client. Sans tax_rate, chaque ligne prend le taux par défaut du catalogue de taxes de votre compte ; sans base_currency, le devis est établi dans la devise de votre compte.

    curl https://cordhq.app/api/v1/cotizaciones \
      -H "Authorization: Bearer $CORD_API_KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: 6a1d3f5e-8b2c-4e7a-9d0f-1c3e5a7b9d2f" \
      -d '{
        "cliente_id": "0e6f2b8a-91c4-4d7e-b3a5-6c2d8f1e4a90",
        "items": [
          { "descripcion": "Window frame installation", "cantidad": 1, "precio_unitario": 12000 }
        ],
        "vigencia_dias": 15
      }'
    {
      "data": {
        "id": "8b0c6f0e-2d55-4c1e-9a43-6a2f1c7d9e10",
        "token": "3f9a1c2e7b4d6f8a0b1c2d3e4f5a6b7c",
        "folio": "COT-0001",
        "link_publico": "https://cordhq.app/q/3f9a1c2e7b4d6f8a0b1c2d3e4f5a6b7c",
        "needs_approval": false,
        "motivo": null
      }
    }

    Le devis naît à l’état de brouillon et votre serveur reçoit quote.created. L’en-tête Idempotency-Key rend les nouvelles tentatives sûres : avec la même clé et le même corps, Cord renvoie cette même réponse au lieu de créer un second devis. Générez une nouvelle clé (par exemple avec uuidgen) pour chaque devis différent.

  6. Envoyez-le. L’envoi génère le lien et adresse l’e-mail au client :

    curl https://cordhq.app/api/v1/cotizaciones/8b0c6f0e-2d55-4c1e-9a43-6a2f1c7d9e10 \
      -H "Authorization: Bearer $CORD_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{ "action": "send" }'
    { "data": { "ok": true, "status": "sent", "email": { "sent": true } } }

    Si le client n’avait pas d’adresse e-mail, email arriverait sous la forme { "sent": false, "skipped": "el cliente no tiene correo" } et le devis serait tout de même envoyé. Votre serveur reçoit :

    quote.sent evt_9d2a6c4e1f8b3a5d7c0e2f4a6b8d1c3e 8b0c6f0e-2d55-4c1e-9a43-6a2f1c7d9e10
  7. Simulez le client. Ouvrez le lien depuis l’e-mail ou déclenchez la consultation avec un simulateur. Dans un autre terminal :

    npx @flouviahq/cli simulate quote 8b0c6f0e-2d55-4c1e-9a43-6a2f1c7d9e10 vista

    quote.viewed arrive. Pour boucler le cycle, approuvez-le depuis l’API ({ "action": "approve" }) et quote.approved arrivera.

Ce que contient chaque notification

Chaque envoi est un POST avec ce corps. id est l’identité de l’événement (identique à chaque nouvelle tentative : dédupliquez avec lui) et data est le résumé du devis :

{
  "id": "evt_9d2a6c4e1f8b3a5d7c0e2f4a6b8d1c3e",
  "event": "quote.sent",
  "created_at": "2026-10-07T17:04:12.318Z",
  "data": {
    "id": "8b0c6f0e-2d55-4c1e-9a43-6a2f1c7d9e10",
    "folio": "COT-0001",
    "status": "sent",
    "moneda": "MXN",
    "total": 13920,
    "cliente": "Ferretería La Prueba",
    "cliente_id": "0e6f2b8a-91c4-4d7e-b3a5-6c2d8f1e4a90",
    "link_publico": "https://cordhq.app/q/3f9a1c2e7b4d6f8a0b1c2d3e4f5a6b7c"
  }
}

Le total de cet exemple inclut l’IVA de 16 % d’un compte mexicain ; sur votre compte, il sera calculé avec vos taux. Tout montant voyage avec sa moneda (devise).

La même chose avec le SDK Node

import { Cord } from '@flouviahq/node';

const cord = new Cord(process.env.CORD_API_KEY);

const me = await cord.me();
const cliente = await cord.clients.create({ empresa: 'Ferretería La Prueba', email: 'your-email@example.com' });
const cotizacion = await cord.quotes.create({
  cliente_id: cliente.id,
  items: [{ descripcion: 'Window frame installation', cantidad: 1, precio_unitario: 12000 }],
});
await cord.quotes.send(cotizacion.id);
console.log(me.mode, cotizacion.folio, cotizacion.link_publico);

Le SDK envoie une Idempotency-Key à chaque opération d’écriture et la répète lors de ses nouvelles tentatives. Voir SDK Node.

Étapes suivantes

  • Webhooks : enregistrez un vrai endpoint ; nouvelles tentatives, renouvellement du secret et vérification manuelle de la signature.
  • Événements : les 41 types d’événements et l’historique par API.
  • API des devis : approuver, refuser, marquer comme payé et supprimer.
  • Gestion des erreurs : tous les codes et quand réessayer.
  • Avant de passer en production, créez une clé sk_live_ et enregistrez votre endpoint dans le Workbench : le secret de cord listen ne sert que pour sa session.