API für das Sub-Affiliate-Recruiting
Endpunkte, Aufbau der Override-Zeilen, Webhook-Payloads und Verhalten des Bewerbungsformulars für das begrenzte 2-stufige Sub-Affiliate-Modell.
Diese Seite ist die Entwicklerseite zum Sub-Affiliate-Recruiting: Die Konzeptseite für Marken erklärt das „Was“ und das „Warum“, diese Seite zeigt die Datenformate, die tatsächlich über die API laufen.
Es gibt fünf Endpunkte, einen neuen Aufbau für Conversion-Zeilen, eine Variante eines Webhooks und zwei Felder im Bewerbungsformular. Alles ist auf eine einzige Rekrutierungsebene begrenzt, mit fester Obergrenze; es gibt keine Weitergabe über eine Kette.
Endpunkte
Alle fünf liegen unter /api/v1/*. Die drei Endpunkte für Marken akzeptieren die übliche Authentifizierung per API-Schlüssel mit Authorization: Bearer rk_live_* und zusätzlich denselben JWT-Weg wie das Dashboard im Produkt. Die beiden Endpunkte unter /me/sub-affiliate/* gehören zur Seite der Affiliates und akzeptieren nur das JWT des Affiliates, nie einen API-Schlüssel.
GET /api/v1/programs/{id}/sub-affiliate-config
Scope (Berechtigungsumfang): read. Liefert die aktuelle Sub-Affiliate-Einstellung einer Kampagne.
curl -sS https://api.rekomi.com/api/v1/programs/0789abcd-.../sub-affiliate-config \
-H "Authorization: Bearer $REKOMI_API_KEY"Antwort:
{
"enabled": true,
"percent": 10.0
}enabled: ob das Werben in dieser Kampagne eingeschaltet ist.percent: der Override-Prozentsatz auf die Provision des Geworbenen. Wertebereich 0 bis 100. Istenabledfalse, wird das Feld trotzdem zurückgegeben (es greift dann nur bei neuen Conversions nicht).
PUT /api/v1/programs/{id}/sub-affiliate-config
Scope: read_write. Rolle: Owner (beim JWT). Plan: in jedem Plan enthalten. Eine bestätigte E-Mail-Adresse ist erforderlich.
Wird im Audit-Log als program.sub_affiliate_config_changed festgehalten.
curl -sS -X PUT https://api.rekomi.com/api/v1/programs/0789abcd-.../sub-affiliate-config \
-H "Authorization: Bearer $REKOMI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"enabled": true,
"percent": 10
}'Antwort (200):
{
"enabled": true,
"percent": 10.0
}Fehler:
| HTTP | error | Ursache |
|---|---|---|
| 400 | body_required | Der JSON-Anfrageinhalt fehlt oder ist leer |
| 400 | unsupported_value | Prozentwert außerhalb von [0, 100] oder keine Zahl |
| 403 | email_verification_required | Der Aufrufer im Dashboard hat seine E-Mail-Adresse nicht bestätigt |
| 403 | (leer) | Der Aufrufer ist nicht Owner |
| 404 | (leer) | Die Kampagne gehört nicht zu deiner Organisation |
GET /api/v1/me/sub-affiliate/link?programId={id}
Scope: read. Seite der Affiliates. Liefert den persönlichen Rekrutierungslink des aufrufenden Affiliates für eine Kampagne. Authentifiziert wird mit dem /a-JWT des Affiliates, nicht mit einem API-Schlüssel der Marke.
curl -sS "https://api.rekomi.com/api/v1/me/sub-affiliate/link?programId=0789abcd-..." \
-H "Authorization: Bearer $AFFILIATE_JWT"Antwort:
{
"affiliateId": "01234567-...",
"programId": "0789abcd-...",
"programSlug": "default",
"url": "https://your-brand.example.com/default?recruited_by=v6media"
}Für das Token recruited_by gilt: Slug zuerst. Es ist der kontoweite ?via=-Slug des Affiliates, falls er einen hat (oben v6media), sonst sein Link-Slug in der Kampagne, und nur wenn es keinen der beiden Slugs gibt, die GUID der Affiliate-ID.
Fehler:
| HTTP | error | Ursache |
|---|---|---|
| 403 | email_verification_required | Der aufrufende Affiliate hat seine E-Mail-Adresse nicht bestätigt (wird vor allem anderen geprüft) |
| 400 | program_id_required | Der Query-Parameter programId fehlt |
| 400 | sub_affiliate_recruiting_disabled | Die Kampagne hat enabled = false |
| 403 | (leer) | Der aufrufende Affiliate ist in der Kampagne nicht Approved |
| 404 | (leer) | Der aufrufende Affiliate kann die Kampagne nicht sehen (er ist kein Mitglied) |
| 409 | portal_not_configured | Die Marke hat keinen Host für ein öffentliches Portal, auf dem die URL aufgebaut werden kann |
GET /api/v1/me/sub-affiliate/recruits
Scope: read. Seite der Affiliates. Listet die Geworbenen des aufrufenden Affiliates auf. Mit Cursor-Paginierung.
curl -sS "https://api.rekomi.com/api/v1/me/sub-affiliate/recruits?take=50" \
-H "Authorization: Bearer $AFFILIATE_JWT"Antwort:
{
"items": [
{
"affiliateId": "fed00b00-...",
"email": "recruit@example.com",
"fullName": "Recruited Person",
"status": "Approved",
"totalCommissionGeneratedCents": 12400,
"overrideEarnedCents": 1240,
"currency": "USD",
"joinedAt": "2026-05-12T10:00:00Z"
}
],
"nextCursor": null
}totalCommissionGeneratedCents: die eigenen Provisionseinnahmen dieses Geworbenen (ohne Override-Zeilen, damit nichts doppelt gezählt wird, wenn der Geworbene später selbst jemanden wirbt).overrideEarnedCents: die gesamte Override-Provision, die der aufrufende Affiliate mit diesem Geworbenen verdient hat, incurrency.- Ausstehende Bewerber und abgelehnte Bewerbungen erscheinen nicht in der Liste; ein Geworbener taucht auf, sobald die Marke ihn freigibt (Geworbene, die später pausiert oder gesperrt werden, bleiben mit ihrem Verlauf in der Liste). Nur freigegebene (
Approved) Geworbene bringen Override-Provision ein.
GET /api/v1/affiliates/{id}/sub-affiliates
Scope: read. Detailansicht für die Marke: Damit sieht sich die handelnde Person bei der Marke die Geworbenen eines beliebigen Affiliates an. Liefert ein flaches Array (ohne Paginierungs-Wrapper) mit leicht anderen Feldnamen als der Endpunkt auf der Seite der Affiliates.
curl -sS "https://api.rekomi.com/api/v1/affiliates/01234567-.../sub-affiliates" \
-H "Authorization: Bearer $REKOMI_API_KEY"[
{
"id": "fed00b00-...",
"email": "recruit@example.com",
"fullName": "Recruited Person",
"status": "Approved",
"totalCommissionCents": 12400,
"overrideEarnedCents": 1240,
"currency": "USD"
}
]Override-Zeile einer Conversion
Wirbt Affiliate A den Affiliate B und schließt B einen Verkauf ab, werden zwei Conversion-Zeilen geschrieben: zuerst die Provisionszeile des Geworbenen, danach die Override-Zeile in einem eigenen, idempotenten Schreibvorgang. Schlägt das Schreiben der Override-Zeile einmal fehl, wird sie automatisch nachgetragen, sobald dasselbe Verkaufsereignis erneut zugestellt wird. So geht keine Override-Provision verloren.
- Provisionszeile für B. Sieht aus wie eine normale Conversion.
AmountCentsist der Verkauf,CommissionCentsdie Provision von B,AttributionMethodder Weg, über den der Verkauf zugeordnet wurde (zum Beispielcouponodermetadata_or_customer).ParentAffiliateConversionIdistnull. - Override-Zeile für A.
AmountCents = 0,CommissionCents = (B.commission × Program.SubAffiliateCommissionPercent / 100), auf die kleinste Währungseinheit gerundet, und zwar mit Banker's Rounding (bei genau einer Hälfte wird zur geraden Zahl gerundet,MidpointRounding.ToEven).AttributionMethod = "sub_affiliate_override".ParentAffiliateConversionIdzeigt auf die ID der Provisionszeile.
Beispielabfrage der Conversion-Liste mit beiden Zeilen:
curl -sS "https://api.rekomi.com/api/v1/conversions?programId=0789abcd-...&take=50" \
-H "Authorization: Bearer $REKOMI_API_KEY"Antwort (nur die relevanten Felder):
{
"items": [
{
"id": "c0nv0parent-...",
"affiliateId": "01234567-B-...",
"programId": "0789abcd-...",
"amountCents": 10000,
"currency": "USD",
"commissionCents": 2000,
"attributionMethod": "coupon",
"parentAffiliateConversionId": null,
"status": "Approved"
},
{
"id": "c0nv0override-...",
"affiliateId": "01234567-A-...",
"programId": "0789abcd-...",
"amountCents": 0,
"currency": "USD",
"commissionCents": 200,
"attributionMethod": "sub_affiliate_override",
"parentAffiliateConversionId": "c0nv0parent-...",
"status": "Approved"
}
],
"nextCursor": null,
"hasMore": false
}Hinweise:
- Override-Zeilen haben immer
amountCents = 0. Der Verkaufsbetrag steht in der Provisionszeile; ihn in der Override-Zeile zu wiederholen, würde den Umsatz in Dashboards doppelt zählen. - Override-Zeilen haben immer
commissionCents > 0. Rundet die berechnete Override-Provision auf null (sehr kleine Provision mal sehr kleiner Prozentsatz), wird keine Zeile angelegt. parentAffiliateConversionIdist ein Fremdschlüssel auf dieselbe TabelleConversion. Damit verknüpfst du Override-Zeilen mit dem Verkauf, aus dem sie entstanden sind.
Erstattungen wirken auf die Override-Zeile
Wird die zugrunde liegende Conversion erstattet (teilweise oder ganz), egal über welchen Erstattungsweg (Webhooks des Zahlungsanbieters, der Endpunkt für Erstattungs-Tracking oder der S2S-Endpunkt für Erstattungen), berechnet Rekomi, welcher Anteil der Provision zurückgeholt wurde, und wendet denselben Anteil auf die Override-Provision an.
- Volle Erstattung des Verkaufs: Die Override-Provision wird vollständig storniert.
- Teilerstattung (z. B. 40% des Verkaufsbetrags): Provision und Override-Provision sinken beide um 40%.
Beide Zeilen lösen Webhook-Ereignisse conversion.refunded aus. Wer Einnahmen über die gesamte Laufzeit mitführt, zieht bei beiden Zeilen commissionReversedCents von der laufenden Summe ab.
Ablehnung und Ungültigkeit wirken auf die Override-Zeile
Statusänderungen an der zugrunde liegenden Conversion wirken auf ihre unbezahlte Override-Zeile genauso wie Erstattungen. Die Override-Zeile wird nur angepasst, solange sie noch nicht bezahlt ist; ist eine Override-Provision einmal ausgezahlt, bleibt sie unverändert.
- Ablehnen. Wird die zugrunde liegende Conversion abgelehnt, wird auch ihre unbezahlte Override-Zeile abgelehnt. Ein abgelehnter Verkauf hinterlässt also keine Einnahmen beim Werber.
- Für ungültig erklären. Kommt die zugrunde liegende Conversion wegen Betrugsverdachts in Quarantäne, landet auch ihre unbezahlte Override-Zeile in Quarantäne.
- Validieren. Wird eine zuvor für ungültig erklärte Conversion wiederhergestellt, wird auch die Override-Zeile mit ihrem ursprünglichen Betrag wiederhergestellt.
Webhook-Payloads
conversion.created (Override-Zeile)
Derselbe Envelope wie beim normalen Ereignis conversion.created. Override-Zeilen erkennst du am Feld attributionMethod:
{
"id": "evt_9f3c1a7b2e4d6f80a1b3c5d7e9f0a2b4",
"type": "conversion.created",
"createdAt": "2026-05-12T03:14:25.000Z",
"organizationId": "b7e2c9a4-3f1d-4e8a-9c2b-6d5f0a1e3c7b",
"data": {
"conversionId": "c0nv0override-...",
"affiliateId": "01234567-A-...",
"programId": "0789abcd-...",
"amountCents": 0,
"currency": "USD",
"commissionCents": 200,
"status": "Pending",
"attributionMethod": "sub_affiliate_override",
"parentAffiliateConversionId": "c0nv0parent-...",
"externalEventId": "in_1abc23DEF456:override:3f9c2a1b8e4d6f70a1b3c5d7e9f0a2b4",
"type": "OneTime"
}
}Die externalEventId der Override-Zeile ist die externe Ereignis-ID des zugrunde liegenden Verkaufs mit einem Override-Suffix, das außerdem die ID des Werbers als 32 Hex-Zeichen enthält (:override:<32-hex-recruiter-id>); sehr lange IDs des Verkaufs werden durch ein Hash-Präfix ersetzt. Der Wert bleibt bei erneuten Zustellungen desselben Verkaufsereignisses stabil, du kannst ihn also als Idempotenzschlüssel (idempotency key) nutzen, wenn du Override-Einnahmen erfasst.
conversion.refunded (Override-Zeile)
Wird bei jedem Durchlauf der Weitergabe ausgelöst, in dem RefundedAmountCents der Override-Zeile erhöht wurde. refundedAmountCents und commissionReversedCents sind die Rückholung GENAU DIESES Ereignisses; cumulativeRefundedAmountCents ist die Gesamtsumme der Zeile; isFullRefund ist nur bei dem Durchlauf true, der die Override-Zeile in den Zustand „vollständig erstattet“ gebracht hat. Das Ereignis trägt die Verknüpfung zur Provisionszeile und die Attributionsmethode, damit du Ereignisse von Override-Zeilen gesondert weiterleiten kannst.
{
"id": "evt_2b4d6f80a1b3c5d7e9f0a2b41c3e5d70",
"type": "conversion.refunded",
"createdAt": "2026-05-15T11:00:00Z",
"organizationId": "b7e2c9a4-3f1d-4e8a-9c2b-6d5f0a1e3c7b",
"data": {
"conversionId": "c0nv0override-...",
"affiliateId": "01234567-A-...",
"programId": "0789abcd-...",
"refundedAmountCents": 200,
"cumulativeRefundedAmountCents": 200,
"commissionReversedCents": 200,
"currency": "USD",
"isFullRefund": true,
"refundedAt": "2026-05-15T11:00:00Z",
"parentAffiliateConversionId": "c0nv0parent-...",
"attributionMethod": "sub_affiliate_override"
}
}Warnung für Webhook-Empfänger
Filtert dein Handler Ereignisse conversion.created nach amountCents > 0, um Ereignisse „ohne Umsatz“ auszusortieren, verlierst du stillschweigend alle Override-Ereignisse. Override-Zeilen sind echte Provisionsverpflichtungen mit commissionCents > 0, haben aber immer amountCents = 0.
Bessere Filter:
- Filtere nach
status(z. B. nur beiApprovedoderPaidreagieren). - Filtere nach
commissionCents > 0, wenn du alles willst, wofür der Affiliate Geld bekommt. - Prüfe
attributionMethod, wenn du Override-Zeilen in eine andere nachgelagerte Verarbeitung leiten willst (z. B. ein eigenes Buchhaltungskonto für Einnahmen der Werber im Unterschied zu Provisionen aus direkten Verkäufen).
Bewerbungsformular: recruitedBy und recruitedByAffiliateId
Der öffentliche Bewerbungsendpunkt (POST /api/p/id/{programId}/apply) nimmt optional einen Verweis auf den Werber an, der am neuen Affiliate-Datensatz die Verknüpfung über eine Rekrutierungsebene herstellt. Es gibt zwei Felder:
recruitedBy(String): das aktuelle Feld. Akzeptiert die Affiliate-ID des Werbers als GUID-String, seinen Link-Slug in der Kampagne oder seinen kontoweiten?via=-Slug.recruitedByAffiliateId(GUID): das ältere Feld. Wird für ältere Integrationen weiterhin akzeptiert.
curl -sS https://api.rekomi.com/api/p/id/0789abcd-.../apply \
-H "Content-Type: application/json" \
-d '{
"email": "newaffiliate@example.com",
"fullName": "New Affiliate",
"recruitedBy": "v6media"
}'Regeln:
- Der Verweis muss auf einen freigegebenen (
Approved) Affiliate in genau der Kampagne führen, für die die Bewerbung gilt. Nicht auflösbare Werte und Abweichungen werden stillschweigend verworfen (die Bewerbung geht durch, aberRecruitedByAffiliateIdbleibt null). - Wirbt sich ein Bewerber selbst, wird der Verweis stillschweigend verworfen, nicht abgelehnt: Ist der Bewerber derselbe Clerk-Nutzer wie der Werber oder bewirbt er sich mit derselben normalisierten E-Mail-Adresse, geht die Bewerbung trotzdem durch, nur ohne Verknüpfung zum Werber.
- Hat die Kampagne
enabled = false, wird gar kein Werber verknüpft: Die Bewerbung geht durch, aber es wird kein Werber gespeichert. - Die Felder sind aus SEO-Gründen nicht im öffentlichen Schema des Bewerbungsformulars sichtbar; nur Aufrufer, die einen Verweis auf einen Werber haben (meist aus einer markierten URL), sollten sie übergeben.
In der Praxis baust du das nicht von Hand. Die Rekrutierungs-URL (die GET /api/v1/me/sub-affiliate/link liefert) trägt recruited_by={token} als Query-Parameter. Das Token ist der kontoweite ?via=-Slug des Werbers, falls er einen hat, sonst sein Link-Slug in der Kampagne, sonst die GUID seiner Affiliate-ID. Die öffentliche Bewerbungsseite liest diesen Parameter und schickt ihn beim Absenden des Formulars als recruitedBy mit.
MCP-Tools
Für Integrationen mit KI-Agenten gibt es drei Tools, die die Endpunkte für Marken kapseln:
| Tool | Entspricht |
|---|---|
get_program_sub_affiliate_config | GET /api/v1/programs/{id}/sub-affiliate-config |
set_program_sub_affiliate_config | PUT /api/v1/programs/{id}/sub-affiliate-config |
list_affiliate_sub_affiliates | GET /api/v1/affiliates/{id}/sub-affiliates |
Alle drei akzeptieren entweder rk_live_* (direkter API-Schlüssel) oder rk_mcp_* (weitergeleitet über mcp.rekomi.com). Die Endpunkte /me/sub-affiliate/* auf der Seite der Affiliates funktionieren nur mit Clerk-JWT und sind bewusst nicht über MCP erreichbar (die Weiterleitung trägt die Identität der Markenorganisation, nicht die eines einzelnen Affiliates). Das Authentifizierungsmodell beschreibt die Seite MCP-Server.
Siehe auch
- Sub-Affiliate-Recruiting (Marken): Konzept, Rechenbeispiel, Weitergabe von Erstattungen, FAQ.
- Conversion-Währung: Override-Zeilen tragen dasselbe Feld
currency; die Override-Provision lautet auf die Währung des zugrunde liegenden Verkaufs. - Webhooks: Envelope der Ereignisse, Prüfung der Signatur, Verhalten bei Wiederholungen.
- API-Referenz: knappe Endpunkt-Signaturen neben dem Rest von
/api/v1/*.