GetAff mit signierten Webhooks an Ihr CRM oder Slack anbinden
Auf die Antwort eines Creators sollten Sie innerhalb einer Stunde reagieren, nicht erst beim nächsten Wochenreview. Mit Webhooks meldet GetAff Ihren Systemen sofort, wenn etwas passiert — eine Suche ist abgeschlossen, ein Kontakt hat geantwortet, ein Creator hat sich beworben, eine Partnerschaft wurde genehmigt. So erhält Ihr CRM die Aktualisierung und Ihr Team die Slack-Nachricht, ohne dass jemand abfragen muss.
Dieser Artikel behandelt die vier Ereignisse, das Registrieren eines Endpunkts, die Signaturprüfung in Node und Python, den Wiederholungsplan und zwei konkrete Integrationen.
Aktualisiert am
Die vier Ereignisse
Jede Zustellung ist ein JSON-Körper { id, type, created_at, data }. data enthält Kennungen und Zählwerte, nie eine E-Mail-Adresse oder eine Nachricht: Wenn Sie das vollständige Objekt brauchen, lesen Sie es mit der Kennung über die API.
search.finished— eine Partnersuche für eine Ihrer Websites ist abgeschlossen, gleich ob vom Bildschirm, aus dem Chat, über die API oder MCP gestartet.data:siteId,searchId,results,onTopic,sourcesje Quelle,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.dataenthält unter anderemapplicationId,programSlug,affiliateName,scoreundautoApproved.partner.approved— eine Partnerschaft wurde genehmigt: durch Ihre Entscheidung, Ihre Regel zur automatischen Genehmigung oder eine angenommene Einladung.dataenthält unter anderempartnershipId,programSlug,affiliateNameundorigin.
Einen Endpunkt registrieren
Unter Kontoeinstellungen › API › Webhooks oder mit einem Schlüssel, der den Scope write besitzt:
curl -s -X POST https://www.getaff.org/api/v1/webhooks \
-H "Authorization: Bearer $GETAFF_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://hooks.example.com/getaff",
"events": ["prospect.replied", "application.received"],
"description": "CRM sync"}'Die Antwort enthält das Signaturgeheimnis (whsec_…) ein einziges Mal: Bewahren Sie es serverseitig bei Ihren übrigen Geheimnissen auf. Ein Konto umfasst bis zu zehn Endpunkte, jeder mit eigenen Ereignissen und eigenem Geheimnis. Die URL muss https:// auf einem öffentlichen Host sein; private, Loopback- und Link-local-Adressen werden bei der Registrierung und erneut bei jeder Zustellung abgelehnt.
Jede Zustellung prüfen
Zustellungen folgen der Standard-Webhooks-Spezifikation. Drei Header begleiten den Körper: webhook-id, 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. Jede Standard-Webhooks-Bibliothek prüft sie allein mit dem Geheimnis; ohne Bibliothek genügt ein Dutzend Zeilen.
- Lesen Sie den ROHEN Körper so, wie er ankommt. Ein neu serialisiertes Objekt ändert die Bytes, und die Signatur passt nicht mehr.
- Lehnen Sie einen Zeitstempel ab, der mehr als fünf Minuten von Ihrer Uhr abweicht: Das ist das Replay-Fenster.
- Berechnen Sie den HMAC und vergleichen Sie ihn in konstanter Zeit mit jedem durch Leerzeichen getrennten
v1,…-Wert. Ein Treffer genügt — während einer Rotation reisen zwei Werte mit. - Speichern Sie die verarbeitete
webhook-id: Eine Wiederholung trägt dieselbe.
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verify(rawBody, headers, secret) {
const id = headers['webhook-id'];
const ts = Number(headers['webhook-timestamp']);
const sigs = (headers['webhook-signature'] ?? '').split(' ');
if (!id || !ts || Math.abs(Date.now() / 1000 - ts) > 300) return false;
const key = Buffer.from(secret.slice('whsec_'.length), 'base64');
const expected = Buffer.from(
'v1,' + createHmac('sha256', key).update(`${id}.${ts}.${rawBody}`).digest('base64'),
);
return sigs.some((s) => {
const got = Buffer.from(s);
return got.length === expected.length && timingSafeEqual(got, expected);
});
}import base64, hashlib, hmac, time
def verify(raw_body: bytes, headers, secret: str) -> bool:
msg_id = headers.get("webhook-id", "")
ts = headers.get("webhook-timestamp", "")
if not msg_id or not ts.isdigit() or abs(time.time() - int(ts)) > 300:
return False
key = base64.b64decode(secret.removeprefix("whsec_"))
signed = f"{msg_id}.{ts}.".encode() + raw_body
expected = "v1," + base64.b64encode(hmac.new(key, signed, hashlib.sha256).digest()).decode()
return any(hmac.compare_digest(s, expected)
for s in headers.get("webhook-signature", "").split(" "))Eine unsignierte oder falsch signierte Anfrage an Ihren Endpunkt kam nicht von GetAff. Zustellungen tragen nie Ihren API-Schlüssel.
Schnell antworten, mit Wiederholungen rechnen
Antworten Sie innerhalb von zehn Sekunden mit einem beliebigen 2xx; der Körper Ihrer Antwort wird ignoriert. Alles andere — ein anderer Status, eine Zeitüberschreitung, ein Verbindungsfehler — ist ein fehlgeschlagener Versuch und wird nach 1 Minute, 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 in den Kontoeinstellungen oder über GET /v1/webhooks/{id}, nie wiederholt. Ein 404 oder 410 Ihrerseits ist sofort endgültig.
- Erst bestätigen, dann arbeiten: Legen Sie das Ereignis in eine Warteschlange und geben Sie
200zurück. - Deduplizieren Sie anhand der
webhook-id, damit eine Wiederholung nach einer langsamen Antwort nur einmal verarbeitet wird. - Senden Sie mit der Aktion „Testen“ oder
POST /v1/webhooks/{id}/testein signierteswebhook.test-Ereignis und sehen Sie sich die Antwort Ihres Endpunkts an.
Beispiel: Ihr CRM synchron halten
Abonnieren Sie mit einem Endpunkt prospect.replied und partner.approved. Bei einer Antwort rufen Sie den Kontakt mit GET /v1/prospects/{prospectId} ab (Status, Kontaktkanal, Herkunft), legen den Kontakt in Ihrem CRM an oder aktualisieren ihn und eröffnen eine Aufgabe für den Account-Verantwortlichen. Bei einer Genehmigung verschieben Sie den Deal auf „Partner“ und speichern die partnershipId, damit die Buchhaltung ihr später die Provisionen zuordnen kann.
Da data Kennungen statt personenbezogener Daten enthält, kann das Ereignis selbst bedenkenlos protokolliert werden; die Details fließen nur über Ihren authentifizierten API-Aufruf.
Beispiel: eine Slack-Nachricht für jede Bewerbung
Die Incoming Webhooks von Slack akzeptieren einen einfachen JSON-POST. Leiten Sie nach der Signaturprüfung eine kurze Zusammenfassung von application.received weiter:
if (event.type === 'application.received') {
const d = event.data;
await fetch(process.env.SLACK_WEBHOOK_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
text: `New application to ${d.programName} from ${d.affiliateName} (score ${d.score})`,
}),
});
}Auch search.finished eignet sich gut für einen Überblick: Posten Sie die Zahl der themenrelevanten Ergebnisse und einen Link zur Seite „Finden“ der Website, damit das Team eine neue Suche noch am selben Tag sichtet.
Ein Endpunkt pro Aufgabe
Jeder Endpunkt hat eigene Ereignisse und ein eigenes Geheimnis – trennen Sie sie nach Zweck, statt alles an eine einzige URL zu schicken. Eine CRM-Synchronisierung, die nur prospect.replied und partner.approved braucht, muss search.finished nicht empfangen; eine Slack-Brücke für das Partnerteam kommt mit application.received aus. Kleinere Handler lassen sich leichter prüfen, überwachen und pausieren.
Jeder Endpunkt zeigt außerdem den Status seiner letzten Zustellung und seine letzten zwanzig Versuche samt der Antwort Ihres Servers. Fällt ein Empfänger aus, verrät dieser Verlauf, ob es an Ihrem Code, Ihrem Zertifikat oder Ihrer Firewall liegt – und die Dead Letter bewahrt jedes verpasste Ereignis dreißig Tage lang auf: Während Sie den Fehler beheben, geht nichts verloren.
Ein Geheimnis ohne Ausfallzeit rotieren
Rotieren Sie in den Kontoeinstellungen oder mit POST /v1/webhooks/{id}/rotate. In den folgenden 24 Stunden trägt jede Zustellung zwei Signaturen — die neue und die alte —, sodass ein Empfänger, den Sie erst später am Tag aktualisieren, weiterhin prüft. Ein Endpunkt lässt sich außerdem pausieren, ohne sein Geheimnis zu verlieren; pausiert empfängt er nichts, und nichts wird eingereiht. Die Webhooks-Dokumentation beschreibt den vollständigen Vertrag, die Referenz jeden Endpunkt.
Dokumentation
Der genaue Vertrag, erzeugt aus den Schemas, die die Routen durchsetzen:
Weiterlesen
Vom API-Schlüssel zum Interessenten in der Sequenz: Partner suchen, vormerken, E-Mails mit bestätigten Kosten (402), sichere Wiederholungen und Ratenbegrenzung.
Kontaktieren Sie ausgewählte Creator aus Ihrem eigenen Gmail-Postfach, lassen Sie Follow-ups automatisch laufen und begleiten Sie jede Person bis zum aktiven Partner.
Verbinden Sie Ihren KI-Assistenten per MCP und OAuth mit GetAff: Creator suchen, bewerten und ansprechen – jede Ausgabe und jeder Versand von Ihnen bestätigt.