GetAff
API-Dokumentation

Webhooks

GetAff sendet einen POST an Ihren HTTPS-Endpunkt, wenn in Ihrem Konto etwas geschieht. Jede Zustellung ist signiert; Ihr Code prüft sie mit einem HMAC und muss nie wieder abfragen.

Ereignisse

Vier Ereignisse, jedes ein JSON-Körper { id, type, created_at, data }. data enthält Kennungen und Zählwerte, nie eine E-Mail-Adresse oder eine Nachricht: lesen Sie das vollständige Objekt bei Bedarf über die API. id beginnt mit msg_ und bleibt bei jedem erneuten Versuch desselben Ereignisses gleich.

search.finished
Eine Partnersuche einer Ihrer Websites ist abgeschlossen — vom Bildschirm, dem Chat, der API oder MCP. data: siteId, searchId, results, onTopic, sources (je Quelle: id, status, count), startedAt, finishedAt.
prospect.replied
Ein Kontakt hat auf eine Outreach-Sequenz geantwortet (vom Zehn-Minuten-Takt in Ihrem Postfach erkannt). data: prospectId, sequenceId, mailboxEmail, repliedAt. Eine von Hand verschobene Stufe löst es nicht aus.
application.received
Ein Creator hat sich für eines Ihrer Programme beworben. data: applicationId, programId, programSlug, programName, affiliateName, affiliateSlug, score, autoApproved.
partner.approved
Eine Partnerschaft in einem Ihrer Programme wurde genehmigt — Ihre Entscheidung, Ihre Regel zur automatischen Genehmigung oder eine angenommene Einladung. data: partnershipId, programId, programSlug, programName, affiliateName, origin.

Einen Endpunkt registrieren

Unter Kontoeinstellungen › API › Webhooks oder per API: POST /v1/webhooks mit dem Scope write. Die Antwort enthält das Signaturgeheimnis (whsec_…) EINMALIG; bewahren Sie es serverseitig auf. Bis zu zehn Endpunkte je Konto, jeder mit eigenen Ereignissen und eigenem Geheimnis. Die URL muss https:// auf einem öffentlichen Host sein: eine private, Loopback- oder Link-local-Adresse, localhost oder ein .internal-Name wird bei der Registrierung und erneut bei jeder Zustellung abgelehnt.

curl
curl -s https://www.getaff.org/api/v1/webhooks \
  -H "Authorization: Bearer $GETAFF_API_KEY" \
  -H 'Content-Type: application/json' \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"url":"https://hooks.example.com/getaff",
       "events":["prospect.replied","application.received"],
       "description":"CRM sync"}'

# → { "data": { "endpoint": { "id": "whe_…", … }, "secret": "whsec_…" } }
#   The secret is in THIS response only. Store it server-side.
Webhooks · API-Schlüssel

Was ankommt

Ein POST mit drei Headern — webhook-id (die Nachrichtenkennung), webhook-timestamp (Unix-Sekunden) und webhook-signature (v1, gefolgt vom base64-kodierten HMAC-SHA256 über {id}.{timestamp}.{body}, mit den Bytes hinter whsec_ als Schlüssel) — und dem JSON-Körper. Antworten Sie innerhalb von 10 Sekunden mit einem beliebigen 2xx; der Körper Ihrer Antwort wird ignoriert. Jeder andere Status, ein Zeitüberschreitung oder ein Verbindungsfehler zählt als fehlgeschlagener Versuch.

HTTP
POST /getaff HTTP/1.1
Host: hooks.example.com
Content-Type: application/json
webhook-id: msg_3f1c2b9a7d6e5f4c3b2a19087f6e5d4c
webhook-timestamp: 1762337524
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pfCnzRIJnS2bzE=

{"id":"msg_3f1c2b9a7d6e5f4c3b2a19087f6e5d4c","type":"prospect.replied",
 "created_at":"2026-11-05T10:12:00.000Z",
 "data":{"prospectId":"…","sequenceId":"…","mailboxEmail":"[email protected]",
         "repliedAt":"2026-11-05T10:11:58.000Z"}}

Eine Zustellung prüfen

Das ist das Standard-Webhooks-Schema: jede Bibliothek dieses Namens prüft eine GetAff-Zustellung allein mit dem Geheimnis. Ohne Bibliothek vier Schritte:

  1. Lesen Sie den ROHEN Anfragekörper wie empfangen — kein neu serialisiertes Objekt: ein umsortierter Schlüssel ändert die Bytes, und die Signatur passt nicht mehr.
  2. Lehnen Sie einen webhook-timestamp ab, der mehr als fünf Minuten von Ihrer Uhr abweicht: das ist das Replay-Fenster.
  3. Berechnen Sie HMAC-SHA256 über {webhook-id}.{webhook-timestamp}.{body} mit dem base64-dekodierten Geheimnis, kodieren Sie es base64 und vergleichen Sie in konstanter Zeit mit jedem durch Leerzeichen getrennten v1,…-Wert in webhook-signature. Ein Treffer genügt (während einer Rotation reisen zwei Werte).
  4. Behandeln Sie id als Idempotenzschlüssel: ein erneuter Versuch trägt dieselbe, und ein Empfänger, der gesehene Kennungen speichert, verarbeitet jedes Ereignis einmal.
Node.js — no dependency
import { createHmac, timingSafeEqual } from 'node:crypto';

// Express: app.post("/getaff", express.raw({ type: "application/json" }), handler)
// The body MUST be the raw bytes — never a parsed-then-stringified object.
export function verifyGetAffWebhook(headers, rawBody, secret, toleranceSec = 300) {
  const id = headers['webhook-id'];
  const ts = headers['webhook-timestamp'];
  const sig = headers['webhook-signature'];
  if (!id || !ts || !sig) return false;
  if (Math.abs(Date.now() / 1000 - Number(ts)) > toleranceSec) return false; // replay window

  const key = Buffer.from(secret.replace(/^whsec_/, ''), 'base64');
  const expected = createHmac('sha256', key).update(`${id}.${ts}.${rawBody}`).digest();
  return sig.split(' ').some((part) => {
    const [version, value] = part.split(',');
    if (version !== 'v1' || !value) return false;
    const given = Buffer.from(value, 'base64');
    return given.length === expected.length && timingSafeEqual(given, expected);
  });
}

// Then: const event = JSON.parse(rawBody); dedupe on event.id; answer 200 at once.
Node.js — standardwebhooks
import { Webhook } from 'standardwebhooks';

const wh = new Webhook(process.env.GETAFF_WEBHOOK_SECRET.replace(/^whsec_/, ''));
const event = wh.verify(rawBody, headers); // throws when the signature or timestamp is wrong

Wiederholungen und Dead Letter

Ein fehlgeschlagener Versuch wird nach 1 Minute, dann 5 Minuten, 30 Minuten, 2 Stunden und 12 Stunden wiederholt — sechs Versuche über rund fünfzehn Stunden — stets mit derselben webhook-id und demselben Körper. Nach dem sechsten wird die Zustellung zur Dead Letter: aufbewahrt und sichtbar (Kontoeinstellungen oder GET /v1/webhooks/{id}), nie wiederholt. Ein abgelehnter Host oder ein 404/410 Ihrerseits ist sofort endgültig. Zustellungen bleiben dreißig Tage sichtbar. Die Aktion „Testen“ (oder POST /v1/webhooks/{id}/test) sendet sofort ein signiertes webhook.test-Ereignis und zeigt die Antwort Ihres Endpunkts.

Ein Geheimnis rotieren

Kontoeinstellungen › Rotieren oder POST /v1/webhooks/{id}/rotate. In den folgenden 24 Stunden trägt jede Zustellung zwei Signaturen in webhook-signature — neu und alt, durch Leerzeichen getrennt —, sodass ein später am Tag aktualisierter Empfänger weiterhin prüft. Danach ist das alte Geheimnis weg.

Was Sie von uns erwarten können — und wir von Ihnen
  • Ein Geheimnis wird einmal gezeigt und ruhend versiegelt; niemand bei GetAff kann es zurücklesen. Verloren? Rotieren.
  • Zustellungen gehen von GetAffs eigenen Servern aus, nie aus einem Browser, und tragen nie Ihren API-Schlüssel.
  • Ein Endpunkt kann pausiert werden (disabled), ohne sein Geheimnis zu verlieren; pausiert empfängt er nichts, und seine Ereignisse werden nicht eingereiht.
  • Prüfen Sie jede Zustellung, bevor Sie handeln. Eine unsignierte oder falsch signierte Anfrage an Ihren Endpunkt kam nicht von GetAff.
  • Antworten Sie schnell mit 2xx und arbeiten Sie danach: ein langsamer Handler ist eine Zeitüberschreitung, und eine Zeitüberschreitung ist eine Wiederholung, die Sie doppelt erhalten.

Referenz