Zum Inhalt springen

Schnellstart

Von null bis zum verifizierten Webhook in fünf Minuten, alles in der Testumgebung.

In dieser Anleitung erstellen Sie einen Testschlüssel, prüfen, zu welchem Konto er gehört, legen einen Kunden an, erstellen ein Angebot für ihn, senden es und empfangen die Benachrichtigungen von Cord auf Ihrem localhost mit verifizierter Signatur. Nichts davon berührt Ihre echten Daten: Ein sk_test_-Schlüssel arbeitet in der Testumgebung Ihres Kontos.

Die Test-E-Mail wird tatsächlich versendet

Das Senden eines Testangebots schickt dem Kunden eine echte E-Mail, deren Betreff als Test gekennzeichnet ist. Verwenden Sie in Schritt 4 eine eigene E-Mail-Adresse.

  1. Erstellen Sie einen Testschlüssel. Aktivieren Sie in der App den Entwicklermodus unter Ihr Konto › Präferenzen und öffnen Sie die Schaltfläche Entwickler in der rechten unteren Ecke. Erstellen Sie im Tab Schlüssel einen geheimen Schlüssel mit Lese- und Schreibzugriff im Testmodus (ist die App bereits im Testmodus, wird der Schlüssel als Testschlüssel erstellt). Er beginnt mit sk_test_ und wird nur einmal angezeigt: Kopieren Sie ihn.

    Die App in der Testumgebung mit der Workbench im Tab Schlüssel: Der soeben erstellte geheime Schlüssel mit Lese- und Schreibzugriff wird ein einziges Mal vollständig angezeigt, sk_test_ gefolgt von 48 Zeichen, mit der Schaltfläche Kopieren; dahinter steht die neue Zeile bereits als Test und Geheim in der Tabelle.

    Speichern Sie ihn in einer Variablen Ihres Terminals. Die Cord-CLI liest ihn ebenfalls von dort:

    export CORD_API_KEY="sk_test_..."
  2. Prüfen Sie den Schlüssel. GET /api/v1/me antwortet mit Organisation, Berechtigung und Modus des Schlüssels:

    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"
      }
    }

    Wenn Sie "mode": "test" sehen, findet alles Weitere in Ihrer Testumgebung statt. Ein 401 mit code: "invalid_key" bedeutet, dass der Schlüssel falsch kopiert oder widerrufen wurde.

  3. Empfangen Sie Webhooks auf Ihrem Rechner. Cord kann Ihren localhost nicht aufrufen, deshalb öffnet die CLI eine Empfangssitzung und leitet jedes Ereignis mit den echten Signatur-Headern an Sie weiter. Zuerst der Server, der sie empfängt. Installieren Sie das SDK in einem leeren Ordner:

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

    Speichern Sie dies als 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;
      }
      // Die Signatur wird über den ROHEN Body berechnet: Parsen Sie ihn nicht vor der Prüfung.
      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'));

    Öffnen Sie in einem zweiten Terminal (mit demselben CORD_API_KEY) die Empfangssitzung:

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

    Die CLI gibt das Secret der Sitzung aus (whsec_…). Starten Sie damit Ihren Server im ersten Terminal:

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

    Lassen Sie beide laufen. Die Sitzung läuft nach 24 Stunden ab, Strg+C beendet sie.

  4. Legen Sie einen Kunden an. Mit einer eigenen E-Mail-Adresse:

    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" } }

    Ihr Server gibt das erste Ereignis aus:

    client.created evt_4c1e9a7b2d5f8e0a3b6c9d2e5f8a1b4c 0e6f2b8a-91c4-4d7e-b3a5-6c2d8f1e4a90
  5. Erstellen Sie das Angebot. Verwenden Sie die id des Kunden. Ohne tax_rate übernimmt jede Position den Standardsatz aus dem Steuerkatalog Ihres Kontos; ohne base_currency wird das Angebot in der Währung Ihres Kontos erstellt.

    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
      }
    }

    Das Angebot entsteht als Entwurf, und Ihr Server empfängt quote.created. Der Header Idempotency-Key macht Wiederholungen sicher: Mit demselben Schlüssel und demselben Body gibt Cord dieselbe Antwort zurück, statt ein zweites Angebot anzulegen. Erzeugen Sie für jedes neue Angebot einen neuen Schlüssel (zum Beispiel mit uuidgen).

  6. Senden Sie es. Der Versand erzeugt den Link und schickt die E-Mail an den Kunden:

    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 } } }

    Hätte der Kunde keine E-Mail-Adresse, käme email als { "sent": false, "skipped": "el cliente no tiene correo" } zurück, und das Angebot würde trotzdem als gesendet gelten. Ihr Server empfängt:

    quote.sent evt_9d2a6c4e1f8b3a5d7c0e2f4a6b8d1c3e 8b0c6f0e-2d55-4c1e-9a43-6a2f1c7d9e10
  7. Simulieren Sie den Kunden. Öffnen Sie den Link aus der E-Mail oder lösen Sie das Öffnen mit einem Simulator aus. In einem weiteren Terminal:

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

    Es kommt quote.viewed an. Um den Zyklus abzuschließen, bestätigen Sie das Angebot über die API ({ "action": "approve" }), dann kommt quote.approved an.

Was jede Benachrichtigung enthält

Jede Zustellung ist ein POST mit diesem Body. id ist die Identität des Ereignisses (bei jedem Wiederholungsversuch gleich: Deduplizieren Sie damit), und data ist die Zusammenfassung des Angebots:

{
  "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"
  }
}

Der total in diesem Beispiel enthält die IVA von 16 % eines mexikanischen Kontos; in Ihrem Konto ergibt er sich aus Ihren Sätzen. Jeder Betrag wird mit seiner moneda (Währung) übertragen.

Dasselbe mit dem Node-SDK

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);

Das SDK sendet bei jedem schreibenden Vorgang einen Idempotency-Key und wiederholt ihn bei seinen Wiederholungsversuchen. Siehe Node-SDK.

Nächste Schritte

  • Webhooks: einen echten Endpoint registrieren, Wiederholungsversuche, Rotation des Secrets und manuelle Prüfung der Signatur.
  • Ereignisse: die 41 Ereignistypen und der Verlauf per API.
  • Angebots-API: bestätigen, ablehnen, als bezahlt markieren und löschen.
  • Fehlerbehandlung: alle Codes und wann Sie es erneut versuchen sollten.
  • Erstellen Sie vor dem Livegang einen sk_live_-Schlüssel und registrieren Sie Ihren Endpoint in der Workbench: Das Secret von cord listen gilt nur für seine Sitzung.