Zum Inhalt springen

Webhooks

Reagieren Sie sofort auf Statusänderungen Ihrer Angebote, ohne die API per Polling abfragen zu müssen.

Warum Webhooks?

Statt dass Ihr System (ein ERP, ein Ablauf in Zapier/Make oder Ihr eigenes Backend) Cord alle 5 Minuten fragt, ob der Kunde schon bezahlt hat, benachrichtigen die Webhooks von Cord Ihr System in dem Moment, in dem das Ereignis eintritt, mit einem signierten HTTP-POST an Ihren Server.

Häufige Anwendungsfälle:

  • Automatisch eine Softwarelizenz bereitstellen, wenn ein Angebot den Status paid erreicht.
  • Eine interne Nachricht in Slack senden, wenn der Kunde ein Angebot zum ersten Mal ansieht (viewed).
  • Den Abrechnungsprozess in Ihrem ERP auslösen, wenn der CFDI gestempelt wird.

Einen Endpoint einrichten

  1. Aktivieren Sie den Entwicklermodus unter Ihr Konto › Präferenzen – dadurch erscheint die Schaltfläche „Entwickler“ unten rechts, in der gesamten App sichtbar. Öffnen Sie sie per Klick oder mit der Taste W. Die vollständige Tour finden Sie unter Cord Workbench.
  2. Öffnen Sie den Tab Webhooks des Panels.
  3. Klicken Sie auf Endpoint hinzufügen – es öffnet sich ein Assistent im Vollbild.
  4. Wählen Sie die Ereignisse, die Sie empfangen möchten: Sie sind nach Familien gruppiert, mit Suche und einem Kontrollkästchen pro Familie. Alle Ereignisse (oder gar keine Auswahl) sendet Ihnen alle, auch Ereignistypen, die Cord künftig hinzufügt. Siehe den vollständigen Katalog weiter unten.
  5. Geben Sie Ihre URL ein – sie muss mit https:// beginnen; Cord lehnt bereits beim Anlegen Ziele mit http://, localhost, privaten/internen IP-Adressen und Hosts .internal/.local ab.
  6. Bewahren Sie eine Kopie Ihres Secrets (whsec_…) auf – es wird nur einmal im Klartext angezeigt. Danach ist es nur noch maskiert sichtbar. Auf derselben Seite können Sie Testereignis senden.

Ihr Team kann im kostenlosen Tarif sowie in Starter und Professional 16 Endpoints konfigurieren, in Scale 32 und in Developer 100. Abonnements, die Integrationen per API erstellen, haben in allen Tarifen ein eigenes Kontingent von 100 pro Organisation. Webhooks sind NICHT an einen kostenpflichtigen Tarif gebunden.

Der Payload

Jede Zustellung ist ein POST mit Content-Type: application/json und diesem Body:

{
  "id": "evt_5f2a1c9e8b3d4a7f6e1c0d9b8a7f6e5c",
  "event": "quote.paid",
  "created_at": "2026-07-27T18:32:04.000Z",
  "data": {
    "id": "b3f1c2d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
    "folio": "COT-0148",
    "status": "paid",
    "moneda": "MXN",
    "total": 45200.00,
    "cliente": "Distribuidora El Zarco",
    "cliente_id": "9a1c3e5f-7b2d-4f6a-8c0e-1d3b5f7a9c2e",
    "link_publico": "https://cordhq.app/q/6f2a9c4e1b7d3f8a0c5e2b9d4f1a6c3e"
  }
}

id ist die Identität des Ereignisses, nicht des Angebots – derselbe Wert bei Wiederholungen und bei einem manuellen Replay (siehe Identität des Ereignisses und Idempotenz). data.id ist die interne ID des Angebots.

Signaturen und Sicherheit

Cord signiert jede Zustellung mit zwei Headern. Verwenden Sie constructEvent aus @flouviahq/node (siehe das Node-SDK), damit Sie die Verifizierung nicht selbst implementieren müssen. Die Funktion verlangt X-Cord-Signature-V1 mit Zeitstempel, vergleicht in konstanter Zeit und lehnt eine Zustellung ab, die älter als 5 Minuten ist:

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

const event = await constructEvent(
  await req.text(),
  req.headers,
  process.env.CORD_WEBHOOK_SECRET!,
);

Jede Zustellung trägt diese Header:

Header Inhalt Hinweise
Content-Type application/json
User-Agent Cord-Webhooks/1.0
X-Cord-Event quote.paid, ping… Der Ereignistyp, identisch mit dem Feld event im Body. Praktisch zum Routing, ohne zu parsen.
X-Cord-Timestamp Unix-Sekunden Dasselbe t wie in X-Cord-Signature-V1: der Moment, in dem dieser Versuch signiert wurde.
X-Cord-Signature-V1 t=<unix>,v1=<hmac_sha256(secret, "<t>.<body>")> Empfohlen. Enthält einen Zeitstempel – echter Schutz vor Replay. Kann während einer Secret-Rotation zwei v1= enthalten (siehe unten); akzeptieren Sie, wenn einer davon passt.
X-Cord-Signature sha256=<hmac_sha256(secret, body)> Legacy, ohne Zeitstempel. Wird aus Kompatibilitätsgründen weiterhin bei jeder Zustellung gesendet; verwenden Sie nach Möglichkeit V1.
X-Cord-Event-Id evt_… Identisch mit dem Feld id im Body.
X-Cord-Delivery-Id UUID ID dieses Versuchs – ändert sich bei jeder Wiederholung und jedem Replay, anders als X-Cord-Event-Id.
X-Cord-Attempt Zahl Beginnt bei 1.
Idempotency-Key evt_… Wiederholt X-Cord-Event-Id – mehrere Server-Frameworks lesen ihn direkt von dort.

Signatur von Hand prüfen

Wenn Sie das SDK nicht verwenden, ist dies der vollständige Algorithmus. Er entspricht dem, was constructEvent tut:

  1. Lesen Sie den rohen Body, genau so, wie er angekommen ist. Geparstes und wieder serialisiertes JSON passt nicht.
  2. Entnehmen Sie dem Header X-Cord-Signature-V1 den Wert t und alle v1-Werte (während einer Rotation kommen zwei an).
  3. Lehnen Sie die Zustellung ab, wenn t mehr als 300 Sekunden von Ihrer Uhrzeit abweicht: Es handelt sich um einen Replay.
  4. Berechnen Sie HMAC-SHA256(secret, "<t>.<roher Body>") in Hexadezimal.
  5. Akzeptieren Sie, wenn das Ergebnis mit einem beliebigen der v1-Werte übereinstimmt, und vergleichen Sie dabei in konstanter Zeit.
import { createHmac, timingSafeEqual } from 'node:crypto';

export function verifyCordSignature(rawBody, header, secret, toleranceSec = 300) {
  if (!header) return false;
  let t = null;
  const signatures = [];
  for (const part of header.split(',')) {
    const i = part.indexOf('=');
    if (i < 0) continue;
    const k = part.slice(0, i).trim();
    const v = part.slice(i + 1).trim();
    if (k === 't' && /^\d+$/.test(v)) t = Number(v);
    else if (k === 'v1' && /^[0-9a-f]{64}$/i.test(v)) signatures.push(Buffer.from(v, 'hex'));
  }
  if (t === null || signatures.length === 0) return false;
  if (Math.abs(Math.floor(Date.now() / 1000) - t) > toleranceSec) return false;
  const expected = createHmac('sha256', secret).update(`${t}.`).update(rawBody).digest();
  return signatures.some((s) => s.length === expected.length && timingSafeEqual(s, expected));
}

Mit Express fordern Sie den rohen Body nur auf dieser Route an:

import express from 'express';
import { verifyCordSignature } from './verify-cord.js';

const app = express();

app.post('/webhooks/cord', express.raw({ type: 'application/json' }), (req, res) => {
  if (!verifyCordSignature(req.body, req.get('X-Cord-Signature-V1'), process.env.CORD_WEBHOOK_SECRET)) {
    return res.sendStatus(400);
  }
  const event = JSON.parse(req.body);
  // Vor jedem Nebeneffekt nach event.id deduplizieren.
  res.sendStatus(200);
});

app.listen(3000);

Vergleichen Sie Signaturen in der Produktion nie von Hand mit === – verwenden Sie einen Vergleich in konstanter Zeit (das SDK tut das bereits). Ein normales !== kann über einen Timing-Angriff Informationen preisgeben.

Das Testereignis ping

Die Schaltfläche Testen eines Endpoints in der Workbench sendet bei ausgewähltem ping (die Standardoption) eine Zustellung mit event: "ping" über denselben Weg wie ein echtes Ereignis (Signatur, Header und Protokoll). Sie verwendet Beispieldaten in der Form eines Angebots und ein Feld mensaje. Jeder Test trägt "test": true auf der obersten Ebene des Payloads:

{
  "id": "evt_0b7e3d9a1c5f2e8b4a6d0c9f3e1b7a5d",
  "event": "ping",
  "created_at": "2026-10-07T17:12:09.441Z",
  "test": true,
  "data": {
    "id": "00000000-0000-0000-0000-000000000000",
    "folio": "COT-PRUEBA",
    "status": "sent",
    "moneda": "MXN",
    "total": 12500,
    "cliente": "Cliente de prueba S.A. de C.V.",
    "cliente_id": null,
    "link_publico": "https://cordhq.app/q/demo",
    "mensaje": "Esta es una entrega de prueba enviada desde el Workbench de Cord."
  }
}

Statt ping können Sie jedes Ereignis wählen, das der Endpoint abonniert hat: Es kommt mit seinem echten Typ an, mit Beispieldaten in der API-Version des Endpoints und ebenfalls mit "test": true. Ein Test wird nur einmal versucht: Schlägt er fehl, kommt er nicht in den Wiederholungsplan. Tests erscheinen nicht im Ereignisverlauf und lösen keine Workflows aus. Antworten Sie mit 2xx und verwerfen Sie sie. Um mit einem der 41 echten Ereignisse und den Daten eines Testobjekts zu testen, verwenden Sie den Webhook-Simulator oder cord trigger aus der CLI.

Identität des Ereignisses und Idempotenz

Jedes logische Ereignis hat eine stabile id (evt_…), identisch im Body und in X-Cord-Event-Id/Idempotency-Key. Es ist derselbe Wert:

  • Bei jeder automatischen Wiederholung desselben Ereignisses (siehe nächster Abschnitt).
  • Bei einem manuellen Replay aus der Workbench („Erneut versuchen“ im Zustellprotokoll) – der Payload wird Byte für Byte identisch erneut gesendet, einschließlich der id.

Da Wiederholungen und Replays dazu führen können, dass Ihr Endpoint dasselbe Ereignis mehr als einmal erhält, muss Ihr Handler idempotent sein: Speichern Sie die bereits verarbeiteten id (und sei es in einer einfachen Tabelle mit unique) und verwerfen Sie Wiederholungen stillschweigend, bevor Sie Nebeneffekte ausführen (eine Zahlung einziehen, eine E-Mail senden usw.).

Wiederholungen und garantierte Zustellung

Cord stellt jedes Ereignis vor dem ersten Zustellversuch in eine Warteschlange – ist Ihr Endpoint nicht erreichbar, geht das Ereignis nicht verloren: Es folgt einem Wiederholungsplan mit exponentiellem Backoff von bis zu 11 Versuchen über ca. 3,6 Tage:

Versuch Wartezeit seit dem vorherigen
1 sofort
2 ca. 10 s
3 ca. 1 min
4 ca. 5 min
5 ca. 30 min
6 ca. 2 h
7 ca. 5 h
8 ca. 10 h
9–11 ca. 1 Tag

(Mit einem Jitter von ±20 %, damit viele gleichzeitig ausgefallene Endpoints die Wiederholung nicht exakt in derselben Sekunde erhalten.) Jede Antwort außer 2xx, ein Netzwerkfehler oder keine Antwort innerhalb von 5 Sekunden gilt als Fehlschlag und plant den nächsten Versuch. Antworten Sie schnell und verarbeiten Sie später: Speichern Sie das Ereignis und antworten Sie mit 200, bevor Sie aufwendige Arbeit erledigen. Einem 3xx (Weiterleitung) wird nie gefolgt – es gilt als Fehlschlag; hat sich die URL Ihres Endpoints geändert, aktualisieren Sie sie in der Workbench, statt eine Weiterleitung einzurichten.

Die Zeiten in der Tabelle sind Mindestwerte

Der erste Versuch erfolgt im Moment des Ereignisses. Die Wiederholungen führt ein Durchlauf der Zustelllogik aus, der alle 5 Minuten läuft; jede Wiederholung erfolgt also beim ersten Durchlauf nach der Zeit, die die Tabelle angibt. War Ihr Endpoint nicht erreichbar, warten Sie nicht auf die nächste Wiederholung: Holen Sie Verpasstes über den Ereignisverlauf nach.

Die Zustellung erfolgt mindestens einmal (at-least-once), nicht genau einmal – deshalb ist die Deduplizierung nach id so wichtig (siehe oben).

Zustand des Endpoints

Cord überwacht die Serie von Fehlschlägen jedes Endpoints (gezählt werden nur Ereignisse, die ihren Wiederholungsplan ausgeschöpft haben, oder fehlgeschlagene manuelle Tests/Replays – nie ein Versuch, dem noch Wiederholungen bevorstehen):

  • 3 Fehlschläge in Folge → Sie erhalten eine Warn-E-Mail (höchstens eine alle 24 h).
  • 5 Fehlschläge in Folge → der Endpoint wird automatisch deaktiviert (er erhält keine neuen Ereignisse mehr), und Sie erhalten eine E-Mail mit der Begründung. Ereignisse, die für diesen Endpoint noch auf eine Wiederholung warteten, werden abgebrochen – sie stauen sich nicht endlos gegen ein nicht erreichbares Ziel.

Beide E-Mails gehen an den Inhaber des Kontos, in der Sprache Ihres Kontos (Einstellungen › Allgemein).

Sobald Sie Ihren Endpoint repariert haben, können Sie im Tab Webhooks der Workbench:

  • Reaktivieren – ab sofort wieder neue Ereignisse empfangen.
  • Reaktivieren und wiederholen – zusätzlich wird erneut eingereiht, was in den letzten 24 Stunden fehlgeschlagen ist (nie der gesamte Rückstand, damit Sie nicht auf einen Schlag mit alten Ereignissen überflutet werden).

Secret-Rotation

Sie können das Secret eines Endpoints rotieren, ohne eine einzige Zustellung zu verlieren. Klicken Sie in der Workbench auf „Secret rotieren“, wählen Sie ein Überlappungsfenster (1 h / 24 h / 72 h) und bestätigen Sie – Sie sehen das neue Secret einmalig im Klartext.

Während des Fensters signiert Cord jede Zustellung gleichzeitig mit beiden Secrets:

X-Cord-Signature-V1: t=1732650000,v1=<hmac with the NEW secret>,v1=<hmac with the OLD secret>

Die Reihenfolge ist fest: Das v1= des neuen Secrets steht zuerst, das des alten am Ende. So können Sie Ihr CORD_WEBHOOK_SECRET zu jedem Zeitpunkt innerhalb des Fensters aktualisieren, ohne dass eine Zustellung abgelehnt wird – vorausgesetzt, Ihre Verifizierung vergleicht alle v1= im Header. Das SDK akzeptiert bereits jedes der beiden, das passt. Wenn Sie von Hand verifizieren, behalten Sie nicht nur einen einzigen Wert: Wer nur den ersten liest, muss das neue Secret bereits haben, und ein Parser, der das letzte v1= behält (zum Beispiel einer, der eine Schlüssel-Wert-Map aufbaut), validiert während des Fensters nur mit dem alten Secret und schlägt daher fehl, sobald Sie vor Ende des Fensters auf das neue wechseln.

Die Legacy-Signatur X-Cord-Signature fasst nur einen Wert, daher wird sie während des Fensters mit dem alten Secret berechnet; nach dem Ende des Fensters wird sie mit dem neuen signiert. Sobald das Fenster endet, signiert Cord in beiden Headern nicht mehr mit dem alten Secret.

Ereigniskatalog

Ereignis Wird ausgelöst, wenn…
quote.sent Der Verkäufer das Angebot an den Kunden sendet.
quote.viewed Der Kunde den öffentlichen Link zum ersten Mal öffnet.
quote.approved Der Kunde das Angebot (ganz oder teilweise) bestätigt.
quote.rejected Der Kunde es ablehnt.
quote.updated Der Verkäufer es ändert und erneut sendet („Ändern und erneut senden“ in der Detailansicht).
quote.expired Seine Gültigkeit abgelaufen ist, ohne dass der Kunde entschieden hat (es war sent/viewed).
quote.deleted Ein Entwurf gelöscht wird (gilt nur für Angebote, die nie gesendet wurden).
quote.paid Die vollständige Zahlung eingeht (eine einfache Zahlung oder die letzte ausstehende Zahlung eines Plans mit Anzahlung oder Raten).
payment.partial EINE Teilzahlung eingeht (Anzahlung, Restzahlung oder eine Rate), ohne dass das Angebot bereits vollständig bezahlt ist.
payment.failed Eine wiederkehrende Zahlung einer Monatspauschale fehlschlägt.
invoice.stamped Der CFDI gestempelt wird (stammt aus einem Angebot, siehe Hinweis unten).
invoice.issued Außerhalb Mexikos eine Rechnung ausgestellt wird (stammt aus einem Angebot).
invoice.finalized Eine Rechnung von Cord Invoicing ausgestellt wird – das eigenständige Rechnungsobjekt, nicht an ein Angebot gebunden.
invoice.sent Der Verkäufer diese Rechnung an den Kunden sendet.
invoice.paid Die Rechnung vollständig bezahlt ist.
invoice.payment_failed Eine Zahlung auf diese Rechnung fehlschlägt (abgelehnte Karte, fehlgeschlagene Lastschrift).
invoice.voided Die Rechnung storniert wird.
invoice.marked_uncollectible Die Rechnung als uneinbringlich markiert wird.
invoice.overdue Ihr Fälligkeitsdatum verstrichen ist, ohne dass sie beglichen wurde (ausgelöst durch den Cron der Zahlungserinnerungen).
quote.created Ein Angebot erstellt wird, in der App, über die API, über MCP oder über Cord Elements.
quote.approval_requested Ein neues Angebot auf eine interne Freigabe wartet (Rabatt, Betrag oder Marge außerhalb des Schwellenwerts). Enthält motivo.
quote.approval_decided Die Geschäftsleitung diese Anfrage freigibt oder ablehnt. Enthält decision (approved | rejected).
quote.comment_added Der Kunde oder der Verkäufer in der Unterhaltung des Angebots schreibt. Enthält autor (cliente | vendedor), mensaje und, wenn es sich auf eine Position bezieht, item_id; ein Gegenangebot enthält tipo: "contraoferta" und propuesta.
client.created / client.updated / client.deleted Ein Kunde im Verzeichnis angelegt, bearbeitet oder gelöscht wird.
product.created / product.updated / product.deleted Ein Produkt im Katalog angelegt, bearbeitet oder gelöscht wird.
task.created / task.completed Eine Aufgabe erstellt wird (in der App, über die API, über MCP, durch einen Workflow oder durch Cord wegen einer Rückbuchung oder einer SPEI-Rückerstattung) oder als erledigt markiert wird.
promise.created / promise.kept / promise.broken Eine Zahlungszusage erfasst oder als eingehalten bzw. nicht eingehalten markiert wird.
dispute.created / dispute.closed Ein Kunde eine Rückbuchung zu einer Zahlung über Cord Payments einleitet oder sie mit ihrem Ergebnis in estado abgeschlossen wird (won | lost | warning_closed). Enthält monto, moneda, motivo und fecha_limite für die Antwort.
refund.succeeded / refund.failed Eine Rückerstattung abgeschlossen wird oder fehlschlägt. Enthält monto, moneda, motivo und bei einem Fehlschlag motivo_falla.
payout.paid / payout.failed Eine Auszahlung auf Ihr Bankkonto ausgeführt wird oder fehlschlägt. Enthält monto, moneda, llegada und bei einem Fehlschlag motivo_falla.
account.updated Sich ändert, was Ihr Zahlungskonto kann: puede_cobrar, puede_depositar, pendientes (noch zu verifizierende Daten) und motivo_bloqueo. Wird nur gesendet, wenn sich einer dieser Werte ändert.
ping Erscheint nur bei „Testen“ in der Workbench – wird nie durch ein echtes Ereignis ausgelöst.

Die Ereignisse zu Rückbuchungen, Rückerstattungen und Auszahlungen enthalten referencia, die Kennung des Vorgangs beim Zahlungsdienstleister, und werden pro Vorgang und Ergebnis nur einmal gesendet. Stammt die Zahlung aus einem Angebot, enthalten sie außerdem cotizacion_id, folio und cliente.

Hinweis: payment.partial enthält in data zusätzliche Felder, die die übrigen Angebots-Ereignisse nicht haben: tipo (anticipo|saldo|cuota), monto, numero_cuota, saldo_pendiente und payment_method, zusätzlich zur normalen Zusammenfassung des Angebots (Nummer, Kunde, Gesamtbetrag, Link).

Zwei Ereignisfamilien, zwei Formen von data. Die Ereignisse quote.* und payment.* (einschließlich invoice.stamped/invoice.issued, die aus dem Angebot ausgelöst werden) teilen die Form von data aus dem Beispiel oben (folio, status, total, cliente, link_publico…). Die sieben Ereignisse invoice.finalized/sent/paid/payment_failed/voided/marked_uncollectible/overdue gehören zu Cord Invoicing – die Rechnung als eigenes Objekt, mit eigenem Lebenszyklus und eigenem öffentlichen Link, nicht als Ableitung eines Angebots – und haben eine ANDERE Form von data:

{
  "id": "evt_7c3e9a1f5b2d8e4a0c6f3b9d1e7a5c2f",
  "event": "invoice.paid",
  "created_at": "2026-08-20T14:00:00.000Z",
  "data": {
    "id": "d4a1b3c5-7e9f-4a2b-8c4d-6e8f0a2b4c6d",
    "object": "invoice",
    "numero": "FAC-0032",
    "folio_fiscal": "3F2A9C1E-7B4D-4E6A-8C2F-1D5B9E3A7C40",
    "estado": "paid",
    "estado_fiscal": "issued",
    "pais": "MX",
    "tipo": "cfdi_40",
    "moneda": "MXN",
    "total": 12500.00,
    "pagado": 12500.00,
    "saldo": 0,
    "vence": "2026-08-30",
    "cliente": "Distribuidora El Zarco",
    "cotizacion_id": null,
    "link_publico": "https://cordhq.app/i/9c1f4e7a2b5d8c0f3a6e9b2d5f8a1c4e"
  }
}

estado ist der Lebenszyklus der Rechnung (draft, open, paid, void oder uncollectible). estado_fiscal ist die Registrierung beim steuerlichen Anbieter: pending, issued, cancelled oder error. tipo ist der Dokumenttyp: cfdi_40, cfdi_egreso, proforma, commercial_invoice, commercial_credit_note, verifactu_invoice oder verifactu_credit_note.

Achten Sie auf den öffentlichen Link: /i/{token} für eine Rechnung von Cord Invoicing, dagegen /q/{token} für ein Angebot. Wenn Sie @flouviahq/node mit TypeScript verwenden, gibt constructEvent den Typ CordWebhookEvent zurück, eine nach event unterschiedene Union, die bereits alle 41 Ereignisse des Katalogs typisiert, einschließlich der sieben invoice.* von Cord Invoicing mit der Form oben.

Form von data bei Kunden, Produkten, Aufgaben und Zahlungszusagen

Jedes Objekt enthält object, damit Sie es unterscheiden können. Das Produktereignis enthält nie Kosten oder Marge.

object Felder
client id, empresa, contacto, email, telefono, rfc, terminos, country_code, language (seine Dokumentsprache; null = die der Organisation) (bei client.deleted: nur id und empresa)
product id, sku, nombre, unidad, precio_lista, activo (bei product.deleted: nur id und nombre)
task id, titulo, due_date, done, cotizacion_id (ohne zuständige Person, Priorität und Notizen)
promise id, cotizacion_id, fecha_promesa, monto, moneda, estado

Endpoints über die API verwalten

Plattformen wie Zapier oder Make erstellen und löschen ihre Abonnements selbst. Dafür gibt es diese Routen:

Methode und Route Scope Was sie tut
GET /api/v1/webhooks read Listet die Endpoints auf, die genau dieser Schlüssel erstellt hat, ohne Secrets.
POST /api/v1/webhooks write Erstellt einen Endpoint. Body: { "url": "https://…", "eventos": ["quote.approved"] }. Gibt id, url, eventos und secret zurück (nur einmal im Klartext).
DELETE /api/v1/webhooks/{id} write Löscht einen Endpoint, den genau dieser Schlüssel erstellt hat.

Es gelten dieselben URL-Regeln wie in der Workbench: nur https:// zu öffentlichen Hosts. Diese Abonnements zählen nicht gegen die Endpoints Ihres Teams: Sie haben ein eigenes Kontingent von 100 pro Organisation (integration_limit_reached).

Ein Schlüssel kann die in der Workbench oder von einem anderen Schlüssel erstellten Endpoints weder sehen noch löschen. Wenn Sie einen Schlüssel widerrufen, werden seine Endpoints deaktiviert: Wenn Sie eine Integration trennen, erhält sie keine Daten mehr.

Ereignisverlauf über die API

War Ihr System länger nicht erreichbar, als die Wiederholungen abdecken, oder fragen Sie lieber ab, statt zu empfangen, gibt GET /api/v1/events (Scope read) die Ereignisse Ihrer Organisation vom neuesten zum ältesten zurück, unabhängig davon, ob Endpoints abonniert sind:

curl "https://cordhq.app/api/v1/events?type=quote.approved&limit=50" \
     -H "Authorization: Bearer sk_live_tU..."
{
  "data": [
    {
      "id": "6b0e2f4a-8c1d-4e3b-9a5f-7d2c0e8b1a46",
      "type": "quote.approved",
      "object": "quote",
      "object_id": "b3f1c2d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
      "data": { "id": "b3f1c2d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "folio": "COT-0148", "status": "approved", "moneda": "MXN", "total": 45200 },
      "actor": "client",
      "created_at": "2026-09-14T18:32:04.123Z"
    }
  ],
  "meta": { "next_cursor": "MjAyNi0wOS0xNFQxODozMjowNC4xMjMwMDBafDZiMGUyZjRhLThjMWQtNGUzYi05YTVmLTdkMmMwZThiMWE0Ng" }
}
  • Optionale Filter: type (ein Ereignis aus dem Katalog) und object_id.
  • next_cursor ist ein undurchsichtiges Token: Wiederholen Sie die Anfrage mit ?cursor=<next_cursor>. Es ist null, wenn es keine weiteren Seiten gibt.
  • actor gibt an, wer das Ereignis ausgelöst hat: user:<id>, api:<key>, mcp:<key>, client (der Kunde im öffentlichen Link), workflow:<id>, system und weitere. Siehe die vollständige Liste.
  • Die id im Verlauf ist die UUID des Datensatzes, nicht dasselbe wie das evt_… des Webhooks.
  • Anders als beim Webhook enthält data kein link_publico: Der Link ist ein Zugangsnachweis und wird nicht im Verlauf gespeichert.

Zustellprotokoll und Aufbewahrung

Der Tab Webhooks der Workbench im Vollbild: der ERP-Endpoint mit 93,1 % erfolgreichen Zustellungen in 7 Tagen, aufgeklappt mit seinen Zustellungen; die neueste, quote.approved im zweiten Versuch, ist mit 500 fehlgeschlagen und zeigt den genauen Payload, den Cord gesendet hat, die Antwort des Servers sowie die Schaltflächen Erneut versuchen und Ereignis ansehen.

Im Tab Webhooks der Workbench hat jeder Endpoint ein aufklappbares Protokoll mit seinen letzten 50 Zustellungen (Ereignis, Versuch, Status, Dauer und Datum). Jede lässt sich öffnen und zeigt, was Cord gesendet hat (den exakten Payload), was Ihr Server geantwortet hat und die Schaltfläche Erneut versuchen. Der Tab Ereignisse der Workbench zeigt dasselbe aus einem anderen Blickwinkel: ein logisches Ereignis mit seiner Zustellung an jeden Endpoint statt eines Protokolls pro Endpoint, und die Schaltfläche Erneut senden. Das Protokoll wird 30 Tage aufbewahrt; endgültig fehlgeschlagene Ereignisse 90 Tage (damit „Erneut versuchen“ in diesem Fall länger funktioniert). Nutzen Sie das Protokoll von Cord nicht als Langzeitspeicher für Gesendetes – speichern Sie, was Sie aufbewahren müssen, bei der Verarbeitung in Ihrem eigenen System.

Das Zustellprotokoll einsehen, eine Zustellung erneut versuchen, ein Ereignis erneut senden und einen Endpoint reaktivieren und wiederholen erfordern den Professional-Tarif oder höher. Endpoints erstellen, Ereignisse empfangen, Testen, Secret rotieren und Reaktivieren sind in allen Tarifen verfügbar.