Rekomi Docs
Für Entwickler
Developers

API-Referenz

Jeder v1-Endpunkt mit Anfrageschema, Beispiel-JSON der Antwort, Scope und Hinweisen, geprüft gegen den Live-Code.

Diese Seite beschreibt die zentralen Endpunkte unter /api/v1/* für Aufrufer, die sich mit einem API-Schlüssel authentifizieren. Jeder Eintrag zeigt den Aufbau der Anfrage, ein realistisches JSON der Antwort, den nötigen Scope und alle Einschränkungen nach Plan oder Rolle. Vollständig ist die Liste nicht: Die Gutscheincodes sind separat unter Tracking über Gutscheincodes dokumentiert, und eine Handvoll Hilfsendpunkte (Slug-Prüfungen, Tracking-Status pro Kampagne, Provisionsübersichten, Auszahlungsjournal und Belege) fehlen hier. Antworten können außerdem zusätzliche Komfortfelder über die gezeigten hinaus enthalten (Anzeigenamen, Kundendaten, Avatar-URLs, Budget- und Schwellenfelder); betrachte die Schemas unten als Kernaufbau, nicht als feste Obergrenze.

Du baust keinen eigenen Client? Rekomi stellt dieselben Funktionen über zwei Wege ohne oder mit wenig Code bereit:

  • Zapier, 9 Trigger, 9 Aktionen, 4 Suchen. Mit einem Klick aus dem Zapier-Marktplatz verbunden. Nimm diesen Weg, wenn du Rekomi mit Slack, Gmail, HubSpot, Notion, Google Sheets, Typeform oder einer anderen App verknüpfen willst, die mit Zapier funktioniert.
  • MCP-Server, 61 Tools für Claude, Cursor, ChatGPT und andere KI-Clients über das Model Context Protocol. Nimm diesen Weg, wenn ein KI-Assistent Rekomi für dich bedienen soll.

Für beide Wege gelten dieselben Plangrenzen und dasselbe Audit-Log wie für die unten dokumentierte REST-Schnittstelle. Sie sind nur andere Zugangswege zum selben Datenmodell, keine eigenen APIs.

Auth-Header bei jedem authentifizierten Endpunkt:

Authorization: Bearer rk_live_xxxxxxxxxxxxxxxxxxxxx

Die Angabe Scope (Berechtigungsumfang) sagt dir, welcher Scope eines API-Schlüssels für den Endpunkt genügt:

  • read: Schlüssel mit dem Scope read werden angenommen (read_write genügt ebenfalls)
  • read_write: Nur Schlüssel mit read_write werden angenommen

Standardregel: GET/HEAD/OPTIONS → read; POST/PUT/PATCH/DELETE → read_write. Die Endpunkte für Embed-Tokens verlangen immer read_write.

Planstufe, die pro Ressourcengruppe nötig ist (die vollständige Tabelle steht unter Überblick → Planvoraussetzungen):

  • Starter+: Server-zu-Server-Tracking (S2S), Kampagnen (Programs), Affiliates, Conversions, Auszahlungen, Dashboard-Kennzahlen, Kunden-Webhooks, Zapier
  • Growth+: Embed-iframe, KI-Co-Pilot
  • Pro+: Export des Audit-Logs per API

Plangrenzen greifen als 402 plan_tier_required mit required, current und trialing im Antwortinhalt. Das Dashboard in der App unter app.rekomi.com umgeht die Plangrenzen (Authentifizierung per JWT).

Gemeinsame Envelopes der Antworten (definiert im Überblick):

// Cursor-paginated list (the core resource lists: programs, affiliates, conversions, payouts)
{
  "items": [ /* ... */ ],
  "nextCursor": "eyJpZCI6IjAxNzg5..." ,
  "hasMore": true
}

// Business-rule error
{ "error": "plan_tier_required", "message": "Feature requires a higher plan tier" }

Scheitert die Validierung der Anfrage, kommt statt des Envelopes für Geschäftsregeln ein Standardobjekt mit Problemdetails ({ type, title, status, errors }) zurück, und 401er sowie viele 404er haben keinen Antwortinhalt; siehe Überblick → Fehler.

Kampagnen (Programs)

Planstufe: Starter+ für Aufrufer mit API-Schlüssel. Im Dashboard per JWT ohne Einschränkung.

Ressourcen vom Typ AffiliateProgram. Marken haben meist 1 bis 3 Kampagnen (zum Beispiel eine Standardkampagne und eine mit einem besonderen Aktionssatz).

GET /api/v1/programs

Scope: read. Listet die Kampagnen auf. Mit Cursor-Paginierung.

Query-Parameter: take (Standard 50, höchstens 200), cursor (opaker Wert aus dem vorherigen nextCursor).

{
  "items": [
    {
      "id": "0789abcd-...",
      "name": "Default program",
      "slug": "default",
      "description": "Earn 20% recurring on every annual plan.",
      "termsUrl": "https://yourbrand.com/affiliate-terms",
      "isActive": true,
      "defaultCommissionType": "Percentage",
      "defaultCommissionValue": 20.0,
      "defaultCommissionModel": "Cps",
      "cookieWindowDays": 60,
      "recurringMonths": 12,
      "recurringDelayInvoices": 0,
      "attributionModel": "LastTouch",
      "autoApprove": false,
      "customDomain": null,
      "customDomainVerified": false,
      "networkVisibility": "Private",
      "isPubliclyDiscoverable": true,
      "createdAt": "2026-05-01T12:00:00Z",
      "updatedAt": "2026-05-12T08:30:00Z",
      "archivedAt": null
    }
  ],
  "nextCursor": null,
  "hasMore": false
}

isPubliclyDiscoverable steuert, ob die öffentliche Landingpage {handle}.rekomi.com/{campaign} für Suchmaschinen sichtbar ist. Bei false gibt die Seite <meta name="robots" content="noindex"> aus und fehlt in der Sitemap; stell den Wert für Kampagnen nur auf Einladung oder mit vertraulichen Sätzen auf false.

GET /api/v1/programs/{id}

Scope: read. Liefert eine einzelne ProgramResponse (gleicher Aufbau wie ein Listeneintrag oben).

GET /api/v1/programs/stats

Scope: read. Die Zusammenfassung für die Kampagnenliste: ein Array mit einem Eintrag pro Kampagne (jeweils mit programId), das affiliatesCount, clicks30d, conversions30d (Verkäufe, Leads und bezahlte Klicks zusammen), sales30d, leads30d, paidClicks30d, pendingCommissionCents, availableCommissionCents, settlingCommissionCents, owedEligibleInHomeCents und settlingInHomeCents enthält. Die Geldwerte sind kleinste Einheiten der Heimatwährung der Organisation, jede Conversion bewertet zum gespeicherten Referenzkurs ihres eigenen Datums. Zeilen, für deren Verkaufswährung kein brauchbarer Kurs vorliegt, fließen nicht in den Wert ein, statt zum Nennwert gezählt zu werden. Sie werden pro Währung in nativen kleinsten Einheiten in den zusätzlichen Maps pendingUnconvertibleByCurrency, availableUnconvertibleByCurrency und settlingUnconvertibleByCurrency gemeldet (jeweils null, wenn alles umgerechnet wurde); diese Maps sind die maßgebliche Aufstellung dessen, was zurückgehalten wurde. Eine Kampagne ohne bisherige Aktivität bekommt trotzdem einen Eintrag mit Nullen.

POST /api/v1/programs

Scope: read_write. Rolle erforderlich: Manager oder höher (bei JWT; API-Schlüssel gelten wie ein Owner und kommen durch).

Anfrage:

{
  "name": "Holiday promo program",
  "slug": "holiday-2026",
  "description": "Limited-time 30% commission for Q4.",
  "termsUrl": "https://yourbrand.com/affiliate-terms",
  "defaultCommissionType": "Percentage",
  "defaultCommissionValue": 30.0,
  "defaultCommissionModel": "Cps",
  "cookieWindowDays": 30,
  "recurringMonths": null,
  "recurringDelayInvoices": 0,
  "attributionModel": "LastTouch",
  "autoApprove": false,
  "budgetCents": 500000,
  "referralDestinationUrl": "https://yourbrand.com/landing",
  "minPayoutCents": 2500
}

Antwort: 201 Created mit der neuen ProgramResponse. Löst keinen Webhook aus (für Kampagnen gibt es derzeit keine Webhooks).

Enums:

  • defaultCommissionType: Percentage | Fixed
  • defaultCommissionModel: Cps (pro Verkauf, fester Betrag oder wiederkehrender Prozentsatz) | Cpc (pro Klick) | Cpl (pro Lead). Benennt die Hauptvergütungsart der Kampagne; defaultCommissionType / defaultCommissionValue enthalten ihren Satz, und partnerspezifische Sätze überschreiben ihn. Die anderen Vergütungsarten fügst du mit leadBountyCents, clickRewardCents und saleCommissionType / saleCommissionValue hinzu (in beliebiger Kombination; siehe unten). Ältere Werte, die für bestehende Kampagnen noch angenommen, für neue aber nicht mehr angeboten werden: Cpa, RevShare, Hybrid (alle verhalten sich wie Cps).
  • attributionModel: FirstTouch | LastTouch
  • networkVisibility: Private | Networked | InviteOnly. Networked listet die Kampagne nach einer Prüfung im Creator-Netzwerk von Rekomi und setzt einen aktiven bezahlten Plan und eine Finanzierungsquelle in der Organisation voraus. Fehlt eines davon, wird eine Anfrage, die das Listing einschaltet, als Private gespeichert und ohne Fehler so zurückgegeben; lies networkVisibility deshalb aus der Antwort zurück. Eine Kampagne, die schon Networked ist, behält den Wert; Creator sehen sie nicht mehr, solange der Plan oder die Finanzierungsquelle fehlt, und sie erscheint von selbst wieder, sobald beides zurück ist.
  • recurringMonths: wiederkehrende Laufzeit pro Verkauf. null / weggelassen = Kundenlaufzeit (jede Verlängerung); 0 = einmalig (keine Verlängerungen); N = jede Zahlung in den ersten N Kalendermonaten jedes Kunden, gezählt ab seiner ersten Zahlung (ein Monatsplan zahlt N-mal, ein Jahresplan bei N gleich 12 einmal). Das Zeitfenster gilt pro Kunde und hängt an der Kunden-ID der Abrechnungsplattform, ein erneutes Abo unter einer neuen Abo-ID bleibt also darin. Sende bei PATCH den Wert -1, um eine bestehende Begrenzung wieder auf Kundenlaufzeit zurückzusetzen.
  • budgetCents: Kampagnen, die pro Klick oder pro Lead zahlen (bei jeder Hauptvergütungsart). Obergrenze für die Ausgaben für Klick- und Lead-Vergütungen in kleinsten Einheiten der Heimatwährung der Organisation; sobald die nicht erstatteten Klick- und Lead-Vergütungen sie erreichen, bringen neue Klicks und Leads keine Provision mehr. Verkaufsprovisionen zählen nie dagegen. Lass das Feld weg, wenn es keine Obergrenze geben soll; das ist nicht empfehlenswert, wenn du pro Klick oder pro Lead zahlst. Die Obergrenze darf höchstens dem Gegenwert von USD 100,000,000 entsprechen, bewertet in deiner Heimatwährung zum heute gespeicherten Referenzkurs; ein höherer Wert wird mit 400 budget_out_of_range abgelehnt (der Antwortinhalt nennt homeCurrency), nie gekappt. Die Ausgaben prüfst du mit GET /api/v1/programs/{id}/budget-status.
  • referralDestinationUrl: die Website, die Affiliates bewerben (bei jeder Kampagne); ein validierter Klick auf rekomi.link leitet mit dem Code des Affiliates dorthin weiter. minPayoutCents: Mindestbetrag für Auszahlungen pro Kampagne (kleinste Einheiten).
  • hostedCheckoutDoorEnabled (bool, Standard false), hostedCheckoutDoorMode (redirect | email, Standard redirect), hostedCheckoutThankYouUrl (http(s)-URL oder "" zum Leeren): der Checkout-Weg für Plattformen, auf denen kein Skript laufen kann (bezahlte Abos bei beehiiv). Ist er eingeschaltet, lauten Links zum Teilen https://rekomi.link/{code}?p={campaignId} (ein optionales &to=/path führt per Deep Link auf eine Seite der Kampagnen-Website), und die Weiterleitung der Plattform nach dem Kauf sollte https://rekomi.link/thanks?p={campaignId}&session_id={CHECKOUT_SESSION_ID} sein. Auf diesem Weg gutgeschriebene Verkäufe tragen attributionMethod: "checkout_door". Ungültiger Modus: 400 invalid_hosted_checkout_door_mode; ungültige URL: 400 invalid_hosted_checkout_thank_you_url.
  • leadBountyCents: jede Hauptvergütungsart außer Cpl (der Satz pro Lead einer Cpl-Kampagne ist defaultCommissionValue). Die Vergütungsart pro Lead: ein fester Betrag in kleinsten Einheiten für jeden qualifizierten Lead, der über das von Rekomi gehostete Lead-Formular der Kampagne erfasst wird, zusätzlich zu allem anderen, was die Kampagne zahlt. Setzt den Plan Starter oder höher voraus. Der Wert ist in kleinsten Einheiten der Heimatwährung der Organisation angegeben (Cent bei USD als Heimatwährung, ganze Yen bei JPY) und muss zwischen dem Gegenwert von USD 0.10 und USD 1,000 liegen, bewertet zum heute gespeicherten Referenzkurs dieser Währung; alles außerhalb dieser Spanne wird mit 400 lead_bounty_out_of_range abgelehnt (der Antwortinhalt nennt homeCurrency), nie gekappt. Ein positiver Wert setzt ihn, 0 löscht ihn, weggelassen = bei PATCH unverändert. Wird bei der Hauptvergütungsart Cpl ignoriert (auf null gesetzt).
  • clickRewardCents: jede Hauptvergütungsart außer Cpc (der Satz pro Klick einer Cpc-Kampagne ist defaultCommissionValue). Die Vergütungsart pro Klick: ein fester Betrag in kleinsten Einheiten der Heimatwährung der Organisation für jeden validierten Klick (mit Betrugs-Score geprüft, dedupliziert, begrenzt, innerhalb von budgetCents), zwischen dem Gegenwert von USD 0.01 und USD 1,000 zum heute gespeicherten Kurs, sonst 400 click_reward_out_of_range, nie gekappt. Ein positiver Wert setzt ihn, 0 löscht ihn, weggelassen = bei PATCH unverändert; bei der Hauptvergütungsart Cpc ignoriert (auf null gesetzt). Ist er eingeschaltet, ist die Kampagne nur auf Einladung zugänglich (automatische Freigabe, Listing im Netzwerk und öffentliche Auffindbarkeit werden zwangsweise ausgeschaltet); außerdem setzt er den Plan Starter oder höher voraus.
  • saleCommissionType / saleCommissionValue: nur bei den Hauptvergütungsarten Cpc / Cpl. Die Vergütungsart pro Verkauf bei einer Klick- oder Lead-Kampagne: Percentage (0 bis 100) oder Fixed (Haupteinheiten der Währung, in der der jeweilige Verkauf eingeht, bis 1,000,000), immer zusammen gesendet. Verlängerungen folgen recurringMonths / recurringDelayInvoices genau wie bei einer Verkaufskampagne. Bei PATCH löscht saleCommissionValue: 0 das Paar. Bei Verkaufs-Hauptvergütungsarten auf null gesetzt (ihr Verkaufssatz ist der Standardsatz). Partnerspezifische Sätze gelten hier nie (sie überschreiben den Satz pro Einheit der Hauptvergütungsart).
  • Grenzen für defaultCommissionValue: Percentage liegt zwischen 0 und 100. Ein Fixed-Wert bei Cpc/Cpl ist die Gebühr pro Klick oder pro Lead in Haupteinheiten der Heimatwährung der Organisation und darf höchstens dem Gegenwert von USD 1,000 entsprechen (400 unit_fee_out_of_range); ein Fixed-Wert bei einem Verkaufsmodell zahlt so viele Einheiten der Währung des jeweiligen Verkaufs und darf höchstens dem Gegenwert von USD 1,000,000 entsprechen (400 fixed_commission_out_of_range). Beide Obergrenzen werden zum heute gespeicherten Referenzkurs bewertet und Werte darüber abgelehnt statt gekappt.

PATCH /api/v1/programs/{id}

Scope: read_write. Rolle: Manager.

Alle Felder sind optional; aktualisiert werden nur die Schlüssel, die du sendest.

{
  "name": "Holiday promo (extended)",
  "isActive": true,
  "defaultCommissionValue": 35.0,
  "autoApprove": true,
  "customDomain": "go.yourbrand.com",
  "networkVisibility": "Networked"
}

Antwort: 200 mit der aktualisierten ProgramResponse. customDomainVerified springt auf false zurück, wenn sich customDomain ändert; bestätige die Domain dann im Dashboard.

DELETE /api/v1/programs/{id}

Scope: read_write. Rolle: Owner. Löscht die Kampagne nur logisch (Soft Delete, setzt archivedAt). Liefert 204 No Content.

GET /api/v1/programs/{id}/tracking-status

Scope: read. Prüfung der Installation: ob die Kampagne jemals einen Klick oder eine Conversion erfasst hat. Liefert { verified, lastClickAt, lastConversionAt, totalClicks30d, totalConversions30d }.

GET /api/v1/programs/{id}/recent-events

Scope: read. Die neuesten Klicks und Conversions der Kampagne, chronologisch gemischt. Query: limit (Standard 20, höchstens 100).

GET /api/v1/programs/check-slug

Scope: read. Prüft vorab, ob ein Slug frei ist, bevor du eine Kampagne erstellst. Query: slug. Liefert { "available": true, "slug": "spring-launch" } oder { "available": false, "slug": "spring-launch", "suggestion": "spring-launch-2" } mit der ersten freien Variante von -2 bis -10. 400 slug_required oder 400 invalid_slug_format (4 bis 64 Kleinbuchstaben, Ziffern und Bindestriche, Anfang und Ende alphanumerisch).

PATCH /api/v1/programs/{id}/slug

Scope: read_write. Rolle: Manager. Ändert den Slug einer Kampagne, also den Pfadabschnitt ihrer Anmeldeseite. Der bisherige Slug bleibt als Weiterleitungsalias erhalten, alte Links leiten also per 308 auf den neuen weiter. Anfrageinhalt: { "slug": "new-slug" }. Liefert { "slug": "new-slug", "previousSlug": "old-slug" }. Fehler: 400 invalid_slug_format, 400 slug_reserved, 409, wenn der Slug von einer anderen aktiven Kampagne belegt ist oder ein ausgemusterter Alias einer anderen Kampagne ist.

GET /api/v1/programs/{id}/tracking-status/lookup

Scope: read. Was Rekomi für einen bestimmten Käufer gesehen hat, für den Support bei einem gehosteten Checkout. Query: email. Plus-Adressen desselben Postfachs zählen als dieselbe Person. Liefert den Lead (falls vorhanden), den zugeordneten Verkauf (falls vorhanden), die letzten Käufe, die der verbundene Zahlungsanbieter für diese E-Mail-Adresse geliefert hat, und ein Ergebnis in einem Wort, verdict: attributed, attributed_by_platform, lead_and_purchase_but_no_sale, lead_only, purchase_without_lead oder nothing_seen.

POST /api/v1/programs/{id}/custom-domain/verify

Scope: read_write. Rolle: Admin. Planstufe: jeder bezahlte Plan, Testphasen eingeschlossen. Führt die DNS-Prüfung für die eigene Tracking-Domain der Kampagne erneut aus und stellt bei Erfolg das Zertifikat bereit. Liefert { "verified": true, "domain": "go.yourbrand.com", "resolvedTarget": "cname.rekomi.com", "errorReason": null }. Mit Rate-Limit.

POST /api/v1/programs/{id}/test-event

Scope: read_write. Rolle: Manager. Löst einen künstlichen Klick plus eine Conversion über $10.00 für den vorab angelegten Test-Affiliate der Kampagne aus, und zwar über die echte Verarbeitungskette einschließlich des Webhooks conversion.created, damit du eine Integration von Anfang bis Ende testen kannst. Testzeilen erscheinen in der Conversion-Liste und in der Aktivität der Kampagne, zählen aber nie zu Auszahlungen oder Summen im Dashboard. Liefert { "clickId": "...", "conversionId": "...", "commissionCents": 100 }.

Affiliates

Planstufe: Starter+ für Aufrufer mit API-Schlüssel. Im Dashboard per JWT ohne Einschränkung.

GET /api/v1/affiliates

Scope: read. Listet die Affiliates auf. Mit Cursor-Paginierung.

Query: take (Standard 50, höchstens 200), skip, cursor, programId. Diese Liste hat keinen Filter nach Status oder Suchtext; ausstehende Bewerbungen findest du unter GET /api/v1/affiliates/applications.

{
  "items": [
    {
      "id": "01234567-...",
      "programId": "0789abcd-...",
      "email": "jane@creator.com",
      "fullName": "Jane Doe",
      "status": "Approved",
      "customCommissionType": null,
      "customCommissionValue": null,
      "tags": ["youtube", "tier-1"],
      "notes": "Top performer Q1 2026",
      "source": "Public",
      "appliedAt": "2026-04-15T10:00:00Z",
      "approvedAt": "2026-04-16T11:30:00Z",
      "createdAt": "2026-04-15T10:00:00Z",
      "links": [
        {
          "id": "abcdef12-...",
          "affiliateId": "01234567-...",
          "slug": "jane-recommends",
          "customAlias": null,
          "isDefault": true,
          "format": "Short",
          "createdAt": "2026-04-16T11:30:00Z"
        }
      ]
    }
  ],
  "nextCursor": null,
  "hasMore": false
}

Hinweise:

  • email ist die E-Mail-Adresse des Affiliates, für dich als Inhaber der Marke sichtbar (deine eigenen Daten).
  • notes sind interne Anmerkungen der Marke. Gib sie nicht an den Affiliate weiter.
  • Die Antwort lässt ClerkUserId, StripeConnectAccountId, PaypalEmail, TaxFormStatus und KycStatus bewusst weg. Für Aufrufer mit API-Schlüssel fehlt außerdem applicationData (das JSON aus dem Formular-Builder). Über das Dashboard per JWT bleibt applicationData erhalten, damit die Prüfansicht im Produkt es anzeigen kann.
  • Enum status: Pending | Approved | Rejected | Paused | Banned.
  • Enum source: Direct | Public | Invite | Network.
  • Enum format (bei jedem Eintrag in links[]): Short | Query | Path | Hash.
  • Der Standardlink (isDefault: true) ist der Haupt-Slug des Affiliate-Kontos. Bei Kampagnen mit Verkaufsmodell kann ein Affiliate bis zu 20 Links pro Kampagne haben (einschließlich seines Hauptlinks), die er in seinem Dashboard verwaltet.

GET /api/v1/affiliates/stats

Scope: read. Eine Zeile pro Affiliate mit den Zahlen aus den Affiliate-Berichten im Dashboard: Klicks (visitors), Leads, Verkäufe (conversions), Provisionstöpfe und Umsatz, alle Geldwerte in kleinsten Einheiten, umgerechnet in deine Heimatwährung. Standardmäßig Werte seit Beginn. Query: programId (optional: eine Kampagne), since und until (optionale ISO-Zeitstempel; since einschließlich, until ausschließlich).

{
  "currency": "USD",
  "items": [
    {
      "affiliateId": "01234567-...",
      "visitors": 43,
      "leads": 2,
      "conversions": 5,
      "unpaidCommissionCents": 12000,
      "dueNowCommissionCents": 8000,
      "paidCommissionCents": 45000,
      "processingCommissionCents": 0,
      "failedPayoutCommissionCents": 0,
      "grossRevenueCents": 310000,
      "netRevenueCents": 298000
    }
  ]
}

Für den Verlauf eines einzelnen Affiliates Tag für Tag nimm GET /api/v1/dashboard/timeseries mit affiliateId.

GET /api/v1/affiliates/{id}

Scope: read. Eine einzelne AffiliateResponse. Liefert 404, wenn der Affiliate nicht zu deiner Organisation gehört.

GET /api/v1/affiliates/applications

Scope: read. Listet ausstehende Bewerbungen auf, die auf Freigabe warten. Gleicher Aufbau pro Affiliate wie in der Hauptliste, gefiltert auf status=Pending. Liefert ein flaches Array, paginiert über take/skip (ohne Cursor-Envelope).

GET /api/v1/affiliates/{id}/tax-form

Scope: read. Rolle: Admin. Liefert eine signierte URL, die 5 Minuten gilt, zum Herunterladen des PDFs mit dem W-9 / W-8BEN des Affiliates. Jeder Abruf wird im Audit-Log festgehalten.

{
  "id": "tx-form-uuid",
  "affiliateId": "01234567-...",
  "type": "W9",
  "submittedAt": "2026-04-20T14:00:00Z",
  "signedUrl": "https://nyc3.digitaloceanspaces.com/rekomi-tax/...sig=...&exp=...",
  "expiresInSeconds": 300
}

Die signedUrl ist eine vorsignierte URL mit begrenzter Gültigkeit (5 Minuten). Sie ist nicht nur einmal verwendbar, also protokolliere sie nicht, gib sie nicht weiter und speichere sie nicht zwischen. Liegt das Formular beim Auszahlungsanbieter statt bei Rekomi, liefert der Endpunkt statt des Aufbaus oben { "heldByStripe": true }.

POST /api/v1/affiliates

Scope: read_write. Legt einen Affiliate direkt an (ohne das Bewerbungsformular). Der Standard-Tracking-Link des neuen Affiliates wird automatisch erzeugt.

Anfrage:

{
  "programId": "0789abcd-...",
  "email": "newaffiliate@example.com",
  "fullName": "New Affiliate",
  "status": "Approved",
  "customCommissionType": null,
  "customCommissionValue": null,
  "customAlias": null
}

Antwort: 201 mit der neuen AffiliateResponse. Löst affiliate.created aus. Bei status=Approved zusätzlich affiliate.approved.

PATCH /api/v1/affiliates/{id}

Scope: read_write. Aktualisiert Felder des Affiliates. Alle optional.

{
  "fullName": "Jane Doe (updated)",
  "status": "Approved",
  "customCommissionType": "Percentage",
  "customCommissionValue": 25.0,
  "notes": "Promoted to tier-1 on 2026-05-12.",
  "tags": ["youtube", "tier-1", "vip"]
}

Antwort: 200 mit der aktualisierten AffiliateResponse.

POST /api/v1/affiliates/{id}/approve

Scope: read_write. Gibt eine ausstehende Bewerbung frei. Löst affiliate.approved aus.

Anfrageinhalt: {} (leer). Antwort: 200 mit der aktualisierten AffiliateResponse.

POST /api/v1/affiliates/{id}/reject

Scope: read_write. Lehnt eine ausstehende Bewerbung ab.

{ "reason": "Off-topic audience" }

Antwort: 200 mit der aktualisierten AffiliateResponse (Status jetzt Rejected).

POST /api/v1/affiliates/bulk-action

Scope: read_write. Rolle: Admin (JWT) mit bestätigter E-Mail-Adresse; Aufrufer mit API-Schlüssel kommen absichtlich durch die Rollenprüfung. Gibt bis zu 200 Affiliates in einem Aufruf per approve frei, pausiert sie per pause oder löscht sie per delete. Idempotent (einen freigegebenen Affiliate erneut freizugeben ändert nichts), jede Aktion wird im Audit-Log festgehalten.

{ "affiliateIds": ["uuid-1", "uuid-2"], "action": "approve" }

POST /api/affiliates/bulk/approve und POST /api/affiliates/bulk/pause

Gibt Affiliates gesammelt frei oder pausiert sie. Das sind Endpunkte für die Dashboard-Sitzung (nur Clerk-JWT; es gibt keinen Alias mit API-Schlüssel als Bearer). Rolle: Manager mit bestätigter E-Mail-Adresse. Bis zu 500 IDs pro Aufruf.

{ "ids": ["uuid-1", "uuid-2", "..."] }

Die Antwort meldet, wie viele Zeilen betroffen waren; IDs, die nicht zu deiner Organisation gehören oder die Voraussetzung der Aktion nicht erfüllen, werden übersprungen.

GET /api/v1/affiliates/export.csv

Scope: read. Die vollständige Affiliate-Liste als CSV, eine Zeile pro Affiliate (für die ganze Organisation oder mit programId für eine Kampagne), mit den Identitätsspalten aus dem Dashboard plus den Statistikspalten von GET /api/v1/affiliates/stats (clicks, leads, sales, die Provisionstöpfe, Brutto- und Nettoumsatz, currency). Optionale since und until begrenzen den Zeitraum der Statistikspalten. Streamt bis zu 50,000 Zeilen. Rate-Limit wie bei /stats.

GET /api/v1/affiliates/{id}/commission-summary

Scope: read. Das Geld eines Affiliates, aufgeteilt nach Währung, nach denselben Regeln wie ein Auszahlungslauf, dueNowCents ist also genau das, was der nächste Lauf zahlen würde.

{
  "primaryCurrency": "USD",
  "graceDays": 30,
  "hasStripeConnect": true,
  "payoutReady": true,
  "rail": "StripeConnect",
  "blockedReason": null,
  "byCurrency": [
    { "currency": "USD", "dueNowCents": 8000, "pendingInGraceCents": 4000, "paidCents": 45000, "processingCents": 0, "refundedCents": 1200, "totalCommissionCents": 58200, "grossRevenueCents": 310000, "netRevenueCents": 298000, "dueConversionCount": 3 }
  ],
  "referrals": { "visitors": 43, "leads": 2, "conversions": 5 }
}

GET /api/v1/affiliates/{id}/commissions

Scope: read. Paginierte Provisionszeilen eines Affiliates für einen Topf. Query: bucket (due | pending | paid | refunded), currency (optional), page (Standard 1), pageSize (Standard 25). Aufgeführt werden nur auszahlbare Zeilen; ein Lead- oder Klick-Marker mit $0 erscheint nie. Die Identität des Kunden ist enthalten, weil das die Seite der Marke ist.

{
  "items": [
    { "conversionId": "c0nv0001-...", "programId": "0789abcd-...", "programName": "Spring launch", "amountCents": 9900, "commissionCents": 1980, "netCommissionCents": 1980, "refundedAmountCents": 0, "currency": "USD", "type": "Sale", "payoutStatus": "Paid", "payoutId": "payout-uuid", "customerName": "Sam Lee", "customerEmail": "sam@example.com", "customerAvatarUrl": null, "createdAt": "2026-04-16T11:30:00Z" }
  ],
  "page": 1, "pageSize": 25, "totalCount": 12, "currency": "USD",
  "counts": { "due": 3, "pending": 2, "paid": 6, "refunded": 1 }
}

GET /api/v1/affiliates/{id}/crm-sync

Scope: read. Eine Zeile pro verbundener CRM-Integration mit dem letzten Sync-Job für diesen Affiliate.

[
  { "integrationId": "...", "slug": "klaviyo", "displayName": "Klaviyo", "integrationEnabled": true, "defaultListName": "Voltra Affiliates", "jobId": "...", "status": "Succeeded", "attempts": 1, "lastAttemptAt": "2026-09-12T12:19:00Z", "succeededAt": "2026-09-12T12:19:02Z", "error": null }
]

status ist NeverSynced, solange es noch keinen Job gibt, sonst der Zustand des Jobs (Pending, Succeeded, Failed, Skipped); error enthält den Grund für das Scheitern oder Überspringen.

POST /api/v1/affiliates/{id}/crm-resync

Scope: read_write. Planstufe: nur bezahlte Pläne (Testphasen bekommen 402). Verwirft die früheren Sync-Jobs des Affiliates und stellt einen neuen Sync zu jedem verbundenen CRM in die Warteschlange. Liefert { "deletedPriorJobs": 2 }. Mit Rate-Limit.

POST /api/v1/affiliates/{id}/remind-payout-setup

Scope: read_write. Rolle: Admin. Schickt einem Affiliate, der Provision verdient, aber noch kein Auszahlungskonto hat, eine E-Mail mit der Bitte, eines zu verbinden. Einmal pro Affiliate alle 24 Stunden. Liefert { "sent": true, "email": "jane@creator.com" }; 409 already_connected, wenn er schon eines hat; 422 no_email; 429 innerhalb der Sperrfrist.

GET /api/v1/affiliates/{id}/claim-reminder/context

Scope: read. Für importierte Affiliates, die ihr Rekomi-Konto noch nicht beansprucht haben. Liefert, was eine Erinnerungs-E-Mail einsetzen kann: die Kampagnen der Person, die aufgelaufenen Einnahmen pro Währung, die Zahl der Empfehlungen, die aufgelösten Beispielwerte für {{claim.campaigns}} und {{claim.earnings}} sowie den Stand der Sperrfrist.

{
  "email": "jane@creator.com", "firstName": "Jane",
  "campaigns": [ { "programId": "0789abcd-...", "name": "Spring launch" } ],
  "earnings": [ { "currency": "USD", "totalCommissionCents": 12000 } ],
  "referralCount": 7,
  "resolvedTokens": { "campaigns": "Spring launch", "earnings": "$120.00" },
  "cooldown": { "canSend": true, "retryAfterHours": 0, "sendsRemaining": 3 }
}

409 already_joined, wenn die Person schon ein Konto hat, 409 not_eligible für ausstehende, abgelehnte oder gesperrte Zeilen.

POST /api/v1/affiliates/{id}/claim-reminder

Scope: read_write. Schickt einer Person die Erinnerung, ihr Konto zu beanspruchen. Anfrageinhalt: { "subject": "...", "bodyHtml": "..." }. Der Inhalt wird serverseitig bereinigt, Platzhalter werden beim Versand aufgelöst, und den Link zum Beanspruchen hängt Rekomi an. Liefert { "sent": true, "email": "..." }. Fehler: 409 already_joined oder not_eligible, 422 unsafe_subject, empty_body oder no_email, 429 cooldown (24 Stunden pro Person, mit Retry-After) oder resend_limit_reached. Mit Rate-Limit.

POST /api/v1/affiliates/claim-reminder/bulk

Scope: read_write. Planstufe: nur bezahlte Pläne. Anfrageinhalt: { "ids": ["..."], "subject": "...", "bodyHtml": "..." }, bis zu 200 IDs. Dedupliziert nach E-Mail-Adresse, überspringt Personen, die schon beigetreten sind oder in der Sperrfrist liegen, und stellt einen Versand pro Person in die Warteschlange, etwa einen pro Sekunde. Liefert nur Gesamtzahlen: { "queued": 18, "skippedJoined": 1, "skippedCooldown": 0, "skippedIneligible": 1 }.

Conversions

Planstufe: Starter+ für Aufrufer mit API-Schlüssel (programmatischer Lese- und Schreibzugriff). Das Anlegen von Conversions per S2S hat eine eigene Grenze bei Starter+. Im Dashboard per JWT ohne Einschränkung.

Diese Endpunkte sind das Provisions-Tracking hinter dem Konto jedes Affiliates: Über sie wird aus einer Conversion eine auszahlbare Provision.

GET /api/v1/conversions

Scope: read. Listet die Conversions auf. Mit Cursor-Paginierung.

Query: take (höchstens 200; beachte, dass diese Liste ohne take 100 Zeilen liefert, anders als der Standard von 50 Zeilen bei den anderen Listen), cursor, affiliateId, programId, status (Pending | Approved | Paid | Denied | Refunded | Invalidated), kind (sale | lead | click; dieselbe Aufteilung wie die Tabs Verkäufe, Leads und Klicks auf der Seite Conversions), since (ISO), until (ISO, ausschließliche Obergrenze für createdAt), stripeCustomerId (die bei der Conversion erfasste Kunden-ID; bei einem S2S-Postback ist das die customerId, die du gesendet hast) und customerEmail (Groß- und Kleinschreibung egal). Mit status=Invalidated holst du die Prüfwarteschlange der Betrugsquarantäne für Klicks und Leads (CPC/CPL), also Traffic, der als ungültig von der Zahlung ausgenommen ist.

{
  "items": [
    {
      "id": "c0nv0001-...",
      "affiliateId": "01234567-...",
      "programId": "0789abcd-...",
      "amountCents": 9900,
      "currency": "USD",
      "commissionCents": 1980,
      "status": "Approved",
      "type": "Recurring",
      "stripeSubscriptionId": "sub_1ABC...",
      "stripeCustomerId": "cus_1ABC...",
      "externalEventId": "in_1XYZ...",
      "createdAt": "2026-05-10T16:00:00Z",
      "refundedAt": null,
      "attributionMethod": "coupon",
      "couponCode": "SARAH20",
      "subId": null
    }
  ],
  "nextCursor": null,
  "hasMore": false
}

Hinweise:

  • Enum type: OneTime | Recurring | Bonus | ClickReward | LeadReward.
  • Enum status: Pending | Approved | Paid | Refunded | Denied | Invalidated. Invalidated = Klick- oder Lead-Traffic in der Betrugsquarantäne, der von der Zahlung ausgenommen ist; validiere ihn (gib ihn frei) oder erkläre eine Zeile im Status Pending auf der Seite Conversions oder über die API für ungültig (siehe unten).
  • attributionMethod sagt, wie der Verkauf zugeordnet wurde (zum Beispiel coupon, metadata_or_customer, cpc_aggregation, sub_affiliate_override oder null bei S2S). Alle möglichen Werte und wie das Dashboard sie gruppiert, findest du in der Webhook-Referenz. couponCode ist der eingelöste Code, wenn (und nur wenn) der Verkauf über einen Gutschein zugeordnet wurde, sonst null.
  • Lead-Zeilen haben type = LeadReward mit amountCents gleich 0. Eine kostenlose, identifizierte Anmeldung (Attribution lead_signup) hat außerdem commissionCents gleich 0 und verknüpft die E-Mail-Adresse eines Kunden schon vor jeder Zahlung mit der Empfehlung; ein Lead in einer Kampagne, die pro Lead zahlt (CPL, Attribution lead_form), trägt die feste Gebühr pro Lead der Kampagne in commissionCents. Beides ist kein Verkauf; ein späterer bezahlter Verkauf wird als eigene Zeile OneTime / Recurring erfasst. Das verlässliche Merkmal für null Provision ist die Attribution lead_signup, nicht der Typ LeadReward. Siehe Leads und Anmeldungen tracken.
  • payability sagt, ob eine Zeile Geld trägt: payable (ein Verkauf oder ein Lead oder Klick, für den die Kampagne zahlt; folgt dem Lebenszyklus der Provision oben), not_paid_on_campaign (ein Lead oder Klick in einer Kampagne, die für diesen Typ nicht zahlt; bleibt für die Attribution und die Betrugsprüfung erhalten, wird nie zur Provision) oder attribution_only (ein Lead- oder Klick-Marker mit $0 in einer Kampagne, die für diese Vergütungsart zahlt; der bezahlte Lead oder Klick ist eine eigene Zeile). Das Feld steht sowohl in der Listen- als auch in der Detailantwort.
  • stripeSubscriptionId und stripeCustomerId sind die eigenen Stripe-Kennungen der Marke (also deine cus_* / sub_*). Es sind nicht die Stripe-Kennungen des Affiliates (Affiliates haben keine). Nutze sie, um Conversions von Rekomi deinen Stripe-Daten zuzuordnen.
  • subId ist der Kanal-Tag, den der Besuch des Kunden mitbrachte (?sub=yt am Link des Affiliates), oder null, wenn kein Tag gesetzt war. Filtere die Liste mit subId=yt oder mit subId=untagged für Zeilen ohne Tag. Siehe Sub-IDs.
  • Für die Details zu einer einzelnen Conversion samt Verlauf der Abo-Ereignisse rufe GET /api/v1/conversions/{id} auf.

GET /api/v1/conversions/{id}

Scope: read. Enthält die Zeitleiste der Abo-Ereignisse.

{
  "id": "c0nv0001-...",
  "affiliateId": "01234567-...",
  "programId": "0789abcd-...",
  "amountCents": 9900,
  "currency": "USD",
  "commissionCents": 1980,
  "status": "Approved",
  "type": "Recurring",
  "stripeSubscriptionId": "sub_1ABC...",
  "stripeCustomerId": "cus_1ABC...",
  "externalEventId": "in_1XYZ...",
  "createdAt": "2026-05-10T16:00:00Z",
  "paidAt": null,
  "refundedAt": null,
  "refundedAmountCents": 2000,
  "attributionMethod": "coupon",
  "couponCode": "SARAH20",
  "events": [
    { "id": "evt-uuid-1", "eventType": "Charged", "amountCents": 9900, "commissionCents": 1980, "occurredAt": "2026-05-10T16:00:00Z" },
    { "id": "evt-uuid-2", "eventType": "Refunded", "amountCents": -2000, "commissionCents": -400, "occurredAt": "2026-06-02T09:12:03Z" }
  ]
}

refundedAmountCents ist die erstattete Gesamtsumme in kleinsten Einheiten der Währung der Conversion; refundedAt wird erst gesetzt, wenn diese Summe den vollen Betrag abdeckt. Jede Erstattung erscheint außerdem in events als Eintrag mit eventType gleich Refunded, mit einem negativen amountCents für diese Erstattung und ihrem occurredAt. So lässt sich eine Erstattung, deren HTTP-Antwort verloren ging, hier bestätigen, bevor du sie wiederholst. Die Werte von eventType sind Created, Charged, Upgraded, Downgraded, Paused, Cancelled, Reactivated und Refunded.

GET /api/v1/conversions/export.csv

Scope: read. CSV-Export. Dieselben Filter wie beim Listen-Endpunkt plus until. Begrenzt auf 50,000 Zeilen pro Aufruf. Wird im Audit-Log festgehalten.

Kopfzeile (gegen den Code geprüft):

id,affiliate_id,program_id,amount_cents,currency,commission_cents,status,type,external_event_id,created_at,refunded_at,attribution_method,coupon_code,sub_id

attribution_method, coupon_code und sub_id sind die letzten drei Spalten (angehängt, damit die Positionen der bestehenden Spalten gleich bleiben). coupon_code ist nur bei Zeilen gefüllt, die über einen Gutschein zugeordnet wurden; sub_id ist der Kanal-Tag, leer, wenn kein Tag gesetzt war. Stripe-IDs fehlen in der CSV bewusst; nutze den JSON-Endpunkt, wenn du sie brauchst.

POST /api/v1/conversions

Scope: read_write. Rolle erforderlich: Manager oder höher mit bestätigter E-Mail-Adresse (bei JWT; API-Schlüssel gelten wie ein Owner und kommen durch). Rate-Limit nach der strengen Richtlinie für Schreibzugriffe.

Erfasst von Hand einen Verkauf, der außerhalb deiner verbundenen Abrechnung stattgefunden hat, oder legt für einen Affiliate eine eigenständige Bonusprovision an. Die Zeile läuft durch dieselbe Verarbeitungskette wie jede andere Conversion (Attributionsmethode manual), es greifen also das Provisionssystem der Kampagne, die Override-Provisionen für Sub-Affiliates, der Webhook conversion.created und die Benachrichtigung über verdiente Provision. Die Provision wird auf dem normalen Weg über Wartefrist und Auszahlungsschwelle auszahlbar. Hier entsteht auszahlbares Geld; der Vorgang wird im Audit-Log als conversion.manual_created festgehalten.

Anfrage:

{
  "programId": "0789abcd-...",
  "affiliateId": "01234567-...",
  "kind": "sale",
  "amountCents": 9900,
  "currency": "USD",
  "commissionCents": null,
  "customerEmail": "buyer@example.com",
  "occurredAt": "2026-05-01T12:00:00Z",
  "note": "Invoice #1042, paid by bank transfer"
}
  • kind: sale (Standard) | bonus.
  • amountCents: nur bei einem Verkauf, Pflicht und > 0, in kleinsten Einheiten von currency. Obergrenze 1,000,000,000,000 kleinste Einheiten, dieselbe währungsunabhängige Plausibilitätsgrenze wie bei jedem anderen Übermittlungsweg (über jedem denkbaren Einzelverkauf). Ein bonus muss das Feld weglassen (bonus_has_no_amount).
  • currency: optional; Standard ist die Heimatwährung deiner Organisation. Muss auf der Positivliste der unterstützten Währungen stehen (unsupported_currency).
  • commissionCents: bei einem sale optional, überschreibt die vom Provisionssystem berechnete Provision, höchstens das 5-Fache von amountCents (commission_exceeds_cap). Bei einem bonus Pflicht und > 0, in kleinsten Einheiten von currency, mit derselben Obergrenze von 1,000,000,000,000 kleinsten Einheiten.
  • customerEmail: optional; verknüpft den Verkauf mit einem Kunden.
  • occurredAt: optionales Rückdatum (ISO 8601). Nie in der Zukunft, höchstens 5 Jahre zurück. Ein rückdatierter Verkauf, der die Wartefrist für Erstattungen schon hinter sich hat, ist sofort auszahlbar.
  • note: optional, auf 1,000 Zeichen gekürzt; kommt im Envelope der Antwort zurück.

Der Affiliate muss zur gewählten Kampagne gehören und Approved sein (affiliate_not_in_program, 409 affiliate_not_approved). Der vorab angelegte Test-Affiliate wird abgelehnt (test_affiliate_not_allowed); nutze stattdessen das Testereignis mit einem Klick. Eine gesperrte Kampagne liefert 409 program_suspended. Dieser Endpunkt dedupliziert nicht von selbst (jeder Aufruf erzeugt eine neue interne Ereignis-ID), eine blinde Wiederholung legt also eine zweite Conversion an. Sende bei jeder Anfrage, die du möglicherweise wiederholst, einen Idempotenzschlüssel (idempotency key) im Header Idempotency-Key.

Antwort: 201 Created.

{
  "conversion": {
    "id": "c0nv0002-...",
    "affiliateId": "01234567-...",
    "programId": "0789abcd-...",
    "amountCents": 9900,
    "currency": "USD",
    "commissionCents": 1980,
    "status": "Pending",
    "type": "OneTime",
    "attributionMethod": "manual",
    "createdAt": "2026-05-01T12:00:00Z"
  },
  "note": "Invoice #1042, paid by bank transfer"
}

Ein bonus kommt mit amountCents: 0 und type: "Bonus" zurück.

GET /api/v1/conversions/kind-counts

Scope: read. Zeilenzahlen seit Beginn pro Art für die Organisation, ohne für ungültig erklärte Zeilen: { "sales": 120, "leads": 34, "clicks": 0 }. Das Dashboard entscheidet damit, ob die Tabs Leads und Klicks überhaupt angezeigt werden.

Betrugsprüfung und Budget für Klicks und Leads (CPC/CPL)

Kampagnen, die pro Klick oder pro Lead zahlen (CPC/CPL), einschließlich des Endpunkts für bezahlte Leads, der Prüfwarteschlange für Betrug und des Budgetstatus, sind in einer eingeschränkten Vorschau. Der Endpunkt für bezahlte Leads liefert 404, bis die Vorschau für dein Konto freigeschaltet ist. Diese Endpunkte steuern den Ablauf der Betrugsprüfung für Klicks und Leads und die Ausgabengrenzen. Die Schreibaktionen verlangen die Rolle Manager.

POST /api/v1/conversions/{id}/validate

Rolle: Manager. Setzt eine Conversion aus der Betrugsquarantäne (Invalidated) zurück auf Pending, damit sie wieder in den normalen Freigabeablauf kommt, hebt also einen Fehlalarm auf. Löst conversion.validated aus.

POST /api/v1/conversions/{id}/invalidate

Rolle: Manager. Stellt eine Conversion mit Pending oder Approved, die du für betrügerisch hältst, unter Quarantäne und setzt sie auf Invalidated, damit sie nicht ausgezahlt wird. Löst conversion.invalidated aus.

GET /api/v1/programs/{id}/budget-status

Scope: read. Der Stand der Ausgabengrenze für Klicks und Leads der Kampagne: Gesamtbudget, ausgegeben (nicht erstattete Provision) und verbleibend, in kleinsten Einheiten der Heimatwährung der Organisation. Unbegrenzt, wenn für die Kampagne kein Budget festgelegt ist. Ausgaben, die sich nicht in der Heimatwährung bewerten ließen (eine Verkaufswährung ohne brauchbaren Wechselkurs für das Datum der Zeile), fließen nicht in spentCents ein, statt zum Nennwert gezählt zu werden, und werden in fxUnconvertibleByCurrency gemeldet, einer Map von Währungscode zu nativen kleinsten Einheiten; diese Map ist der maßgebliche Wert. Der ältere Einzelwert fxUnconvertibleCents bleibt aus Kompatibilitätsgründen in der Antwort, ist aber eine Summe nativer kleinster Einheiten über alle Währungen hinweg und damit nur ein Signal, kein Geldbetrag: Ist er nicht null, lies die Map.

GET /api/v1/fraud-events

Scope: read. Listet die Betrugssignale auf, die Rekomi bei bezahlten Klicks und Leads markiert hat (IPs mit hohem Risiko, Bots, selbst eingereichte Leads, Wegwerf-E-Mail-Adressen, überschrittene Grenzen pro IP, missbräuchliche Affiliates). Diese wurden bereits verworfen, ohne Auszahlung. Query: programId, status (Standard open; außerdem resolved, dismissed oder all).

  • PATCH /api/v1/fraud-events/{id} (Manager) mit { "action": "resolve" | "dismiss" } prüft ein Signal.
  • POST /api/v1/fraud-events/ban-affiliate/{affiliateId} (Manager) sperrt einen Affiliate: Keiner seiner künftigen Klicks, Leads und Conversions bringt noch eine Provision.

GET /api/v1/fraud-events/summary

Scope: read. Rolle: Manager. Zusammengefasste Betrugsstatistik für eine Kampagne, die pro Klick oder pro Lead zahlt. Query: programId (Pflicht; ohne kommt 400 program_id_required), days (Standard 30, begrenzt auf 1 bis 365). Liefert { windowDays, blockedClicks, blockedClicksByReason, heldClicks, quarantinedLeads, estimatedNotBilledCents, currency, fraudStrictness }.

GET /api/v1/uncredited-conversions

Scope: read. Verkäufe, die einem Affiliate zugeordnet ankamen, der in diesem Moment keine Provision verdienen konnte (zum Zeitpunkt des Verkaufs nicht Approved: ausstehend, pausiert, abgelehnt oder gesperrt). Query: status (Standard open | dismissed | all), affiliateId. Liefert bis zu 500 Zeilen mit { id, affiliateId, affiliateEmail, affiliateName, programId, amountCents, currency, reason, affiliateStatusAtDrop, status, occurredAt }. Zugehörige Endpunkte:

  • GET /api/v1/uncredited-conversions/summary liefert { openCount, byCurrency: [{ currency, amountCents, count }] }.
  • PATCH /api/v1/uncredited-conversions/{id}/dismiss (Manager) markiert eine Zeile als geprüft.

War eine Zeile ein Versehen (zum Beispiel ein versehentlich pausierter Affiliate), korrigiere den Status des Affiliates und schreib ihm den Verkauf mit einer manuellen Conversion gut.

POST /api/v1/lead (bezahlter Lead in einer Kampagne, die pro Lead zahlt, CPL)

Eingeschränkte Vorschau (liefert 404, bis sie für dein Konto freigeschaltet ist). Erfasst einen abrechenbaren Lead in einer Kampagne, die pro Lead zahlt. Anfrageinhalt: { slug, programId?, email, name?, token }, wobei token ein verpflichtendes Cloudflare-Turnstile-Token ist. Der Lead durchläuft Prüfungen auf E-Mail-Format, Wegwerf-Domains, selbst eingereichte Leads, Duplikate pro Kampagne, Tageslimit, Budget und Betrug über IPQS. Ein Lead mit hohem Risiko kommt unter Quarantäne (Invalidated, neutrales 200); eine Kampagne mit ausgeschöpftem Budget liefert 409 campaign_budget_reached; ist die Betrugsbewertung nicht erreichbar, liefert der Aufruf 503 (erneut versuchen) und erfasst nichts. Das ist etwas anderes als der Anmelde-Lead ohne Provision (/api/v1/lead/signup); siehe Leads und Anmeldungen tracken.

Import (Sammelumzug)

Planstufe: Starter+ für Aufrufer mit API-Schlüssel. Im Dashboard per JWT ohne Einschränkung. Scope: read_write.

Ein allgemeiner Sammelimport, unabhängig von der Plattform. Anders als beim Umzug im Dashboard (der eine bestimmte CSV eines Anbieters ausliest) sendest du hier kanonische JSON-Zeilen, jede Quelle funktioniert also, sobald du sie in den Aufbau von Rekomi übertragen hast. Beide Wege münden in dieselben geprüften Dienste: Es gibt genau einen Weg, der Affiliates anlegt, und einen, der Conversions erfasst.

Gemeinsame Schutzmechanismen für alle Endpunkte unten:

  • Zeilenlimit: höchstens 1,000 Zeilen pro Anfrage (row_cap_exceeded, 400). Teile größere Importe in Seiten auf.
  • Größe des Anfrageinhalts: etwa 4 MB JSON (413 über der Grenze).
  • Rate-Limit: die eigene Richtlinie import, 6 Anfragen pro Minute pro Aufrufer (429 über der Grenze).
  • Parallelität: ein Import pro Organisation gleichzeitig. Ein zweiter, gleichzeitiger Import liefert 409 migration_already_in_progress (gemeinsam mit dem Importer im Dashboard).
  • Mandantentrennung: Eine programId, die nicht zu deiner Organisation gehört, liefert 404 program_not_found.
  • Rückgängig machbar: Jeder Import legt einen MigrationJob an; die zurückgegebene jobId erscheint in der Importhistorie und lässt sich im Dashboard rückgängig machen.

POST /api/v1/affiliates/import

Scope: read_write. Importiert eine Affiliate-Liste gesammelt. Dedupliziert nach E-Mail-Adresse innerhalb der Kampagne UND innerhalb des Stapels (ein erneuter Lauf ist idempotent: Bestehende E-Mail-Adressen kommen als skipped zurück und werden nie doppelt angelegt). Jede Zeile behält ihren Tracking-Slug oder bekommt einen neu erzeugten.

Anfrage:

{
  "programId": "0789abcd-...",
  "affiliates": [
    { "email": "alex@example.com", "fullName": "Alex Smith", "status": "approved", "paypalEmail": "alex@example.com", "slug": "alex" },
    { "email": "jordan@example.com", "fullName": "Jordan Lee" }
  ],
  "includeStatuses": ["approved", "pending"],
  "notifyAffiliates": false,
  "notificationMessageHtml": null
}
  • email ist pro Zeile Pflicht. status ist optional und ist ohne Angabe approved (der Affiliate war auf der vorherigen Plattform schon aktiv); ein unbekannter Wert wird zu pending. Erlaubt: approved | pending | rejected | paused | banned.
  • slug (optional) behält den bisherigen Empfehlungs-Slug des Affiliates bei, damit seine alten Links weiter funktionieren; ist er belegt oder ungültig, wird ein neuer Slug erzeugt.
  • includeStatuses (optional) ist eine Positivliste; Zeilen, deren zugeordneter Status nicht darin steht, werden übersprungen (status_excluded). Lass das Feld weg, um alle Zeilen zu importieren.
  • notifyAffiliates setzt Growth+ mit einem aktiven Abonnement voraus; bei einem niedrigeren Plan klappt der Import trotzdem, es werden aber keine E-Mails verschickt.

Antwort: 200.

{
  "jobId": "1234abcd-...",
  "status": "Completed",
  "kind": "Affiliates",
  "total": 2,
  "imported": 2,
  "skipped": 0,
  "errored": 0,
  "reasons": {}
}

reasons zählt, warum Zeilen nicht importiert wurden (zum Beispiel already_exists, status_excluded, missing_email).

POST /api/v1/conversions/import/preview

Scope: read_write. Probelauf eines Conversion-Imports: ordnet Zeilen ein, löst Affiliates auf und summiert die Beträge, schreibt aber nichts. Ruf diesen Endpunkt immer vor dem echten Import auf, damit du den offenen Saldo zeigen kannst, bevor du die Zahlung freigibst. Gleicher Zeilenaufbau und gleiche Validierung wie beim Import-Endpunkt.

Anfrage: { "programId": "...", "conversions": [ ... ], "excludePaid": false } (gleicher Zeilenaufbau wie beim Import). Übergib dasselbe excludePaid, das du beim Import verwenden willst: Bei true werden bezahlte Zeilen als übersprungen gemeldet (excluded_paid) und nicht in wouldImport / paidBalance gezählt, sodass die Vorschau genau dem entspricht, was der Import schreiben wird.

Antwort: 200.

{
  "rowsTotal": 2,
  "wouldImport": 2,
  "wouldSkip": 0,
  "wouldError": 0,
  "skipReasons": {},
  "paidBalance": [{ "currency": "USD", "commissionCents": 2500, "count": 1 }],
  "unpaidBalance": [{ "currency": "USD", "commissionCents": 5000, "count": 1 }]
}

unpaidBalance ist genau die Provision pro Währung, die auszahlbar würde, wenn du anschließend mit confirmUnpaidPayout=true importierst.

POST /api/v1/conversions/import

Scope: read_write. Importiert frühere Conversions und Provisionen, damit umgezogene Affiliates ihre Einnahmen und Statistiken behalten (und mit Stripe-IDs auch künftige Verlängerungen wieder gutgeschrieben bekommen). Idempotent (ein erneuter Lauf überspringt bereits importierte Zeilen; es wird nie doppelt gezahlt).

Anfrage:

{
  "programId": "0789abcd-...",
  "conversions": [
    {
      "affiliateEmail": "alex@example.com",
      "affiliateSlug": null,
      "customerEmail": "buyer@example.com",
      "customerName": "Buyer One",
      "amountCents": 10000,
      "commissionCents": 2500,
      "currency": "USD",
      "occurredAt": "2025-01-15T00:00:00Z",
      "paid": true,
      "type": "OneTime",
      "stripeCustomerId": "cus_ABC",
      "stripeSubscriptionId": "sub_XYZ",
      "externalId": "src-row-1"
    }
  ],
  "confirmUnpaidPayout": false,
  "excludePaid": false
}

Validierung pro Zeile (jeder Fehler liefert 400 invalid_rows mit { index, error } für jede betroffene Zeile, bevor irgendetwas geschrieben wird):

  • Mindestens eines von affiliateEmail / affiliateSlug ist Pflicht (affiliate_email_or_slug_required).
  • amountCents und commissionCents müssen ganze Zahlen in [0, 1000000000000] sein (kleinste Einheiten von currency; eine währungsunabhängige Plausibilitätsgrenze über jedem denkbaren Einzelverkauf). Negative Werte oder Werte über der Grenze werden abgelehnt (amount_out_of_range / commission_out_of_range).
  • currency muss ein unterstützter ISO-4217-Code sein (unsupported_currency).
  • occurredAt ist Pflicht (ISO 8601; legt den Kohortenmonat in der Historie fest).
  • type ist OneTime (Standard) oder Recurring (invalid_type).

Sicherheit beim Geld (bitte lesen):

  • Zeilen mit paid: true werden als abgerechnete Historie erfasst (Paid, gesperrt) und können nie noch einmal ausgezahlt werden.
  • Zeilen mit paid: false werden standardmäßig als gesperrte Historie erfasst. Sie werden nur dann zu einem auszahlbaren Saldo (Approved, ein Betrag, den Rekomi auszahlt), wenn du confirmUnpaidPayout: true setzt. confirmUnpaidPayout ist standardmäßig false.
  • Ein Import bewegt nie von sich aus Geld; Auszahlungen werden weiterhin von dir ausgelöst und setzen Stripe Connect voraus.
  • excludePaid: true überspringt bereits bezahlte Zeilen komplett (importiert nur offene Salden).

Antwort: 200.

{
  "jobId": "1234abcd-...",
  "status": "Completed",
  "kind": "Conversions",
  "total": 1,
  "imported": 1,
  "skipped": 0,
  "errored": 0,
  "unpaidConfirmed": [],
  "unpaidConfirmedCurrency": null,
  "unpaidConfirmedCents": 0,
  "confirmUnpaidPayout": false,
  "excludePaid": false,
  "reasons": {}
}

unpaidConfirmed ist genau die Provision, die dieser Import auszahlbar gemacht hat (die Approved-Conversions, die er angelegt hat), pro Währung als Einträge { currency, commissionCents, count } im selben Aufbau wie unpaidBalance aus der Vorschau. Ein Stapel, der JPY 1,000 und USD 10.00 bestätigt hat, liefert [{ "currency": "JPY", "commissionCents": 1000, "count": 1 }, { "currency": "USD", "commissionCents": 1000, "count": 1 }]. Ist confirmUnpaidPayout gleich false, bleibt die Liste leer.

unpaidConfirmedCurrency ist der Code dieser Währung, wenn genau eine Währung auszahlbar wurde, sonst null.

unpaidConfirmedCents bleibt für ältere Integrationen erhalten. Es ist eine reine Summe nativer kleinster Einheiten über alle Währungen des Stapels, lies den Wert also nur dann als Geldbetrag, wenn unpaidConfirmedCurrency gesetzt ist. Ist unpaidConfirmedCurrency gleich null, zeigt der Wert nur an, dass etwas auszahlbar wurde; die Beträge stehen in unpaidConfirmed.

Auszahlungen

Planstufe: Starter+ für Aufrufer mit API-Schlüssel. Im Dashboard per JWT ohne Einschränkung.

GET /api/v1/payouts

Scope: read. Listet die Auszahlungen auf, eine Zeile pro Affiliate und Lauf. Mit Cursor-Paginierung.

{
  "items": [
    {
      "id": "payout-uuid",
      "affiliateId": "01234567-...",
      "amountCents": 9800,
      "currency": "USD",
      "method": "StripeConnect",
      "status": "Paid",
      "stripeTransferId": "tr_1ABC...",
      "externalTransactionId": null,
      "notes": null,
      "failureReason": null,
      "periodStart": "2026-04-01",
      "periodEnd": "2026-04-30",
      "createdAt": "2026-05-01T09:00:00Z",
      "paidAt": "2026-05-01T09:00:05Z",
      "failedAt": null,
      "lineItems": [
        { "id": "li-uuid-1", "conversionId": "c0nv0001-...", "amountCents": 1980, "currency": "USD", "nativeAmountCents": 1980, "nativeCurrency": "USD", "fxRate": 1, "fxRateDate": "2026-05-01", "reversedAmountCents": 0, "reversedAt": null },
        { "id": "li-uuid-2", "conversionId": "c0nv0002-...", "amountCents": 7820, "currency": "USD", "nativeAmountCents": 7200, "nativeCurrency": "EUR", "fxRate": 0.9207, "fxRateDate": "2026-04-30", "reversedAmountCents": 0, "reversedAt": null }
      ]
    }
  ],
  "nextCursor": null,
  "hasMore": false
}

Enums:

  • method ist bei gespeicherten Auszahlungen StripeConnect, PayPal oder GlobalPayouts (Manual kommt nur in historischen Zeilen vor). Anfragen dürfen StripeConnect oder Auto senden (jeder Affiliate wird über seinen ermittelten Auszahlungsweg bezahlt); Wise wird nicht unterstützt.
  • status: Pending | Processing | Paid | Failed.

Jede Auszahlungszeile ist die Auszahlung eines Affiliates aus einem Lauf, und lineItems listet die Provisionen darin auf:

  • amountCents + currency: was die Position ausgezahlt hat (USD bei zusammengefassten Läufen; ein älterer Lauf in einer einzigen Währung trägt die Währung dieses Laufs).
  • nativeAmountCents + nativeCurrency: die Provision so, wie sie verdient wurde.
  • fxRate: der gespeicherte Referenzkurs auf USD-Basis, zu dem die Position umgerechnet wurde, angegeben als native Haupteinheiten pro 1 USD (der Wechselkurs für nativeCurrency am fxRateDate; USD-Positionen tragen 1). usd = native / fxRate ergibt die Position, und der Kurs liest sich als „0.9207 EUR pro USD“, nie als USD pro nativer Einheit.
  • fxRateDate: das Datum, an dem der Kurs veröffentlicht wurde (bei USD-Positionen das Datum des Laufs).
  • reversedAmountCents + reversedAt: eine Rückforderung nach der Zahlung (0 / null, solange die Position unverändert ist).

Die vier FX-Felder sind null bei Auszahlungen, die entstanden sind, bevor FX pro Position erfasst wurde.

GET /api/v1/payouts/{id}

Scope: read. Eine einzelne PayoutResponse (gleicher Aufbau wie ein Listeneintrag).

Finanzierung nur im Dashboard

Die Finanzierung von Auszahlungen (ein Bankkonto zur Finanzierung auf einem beliebigen Weg verknüpfen, die Finanzierung per Banküberweisung einschalten, Auszahlungen ausführen, wiederholen oder zurückbuchen) ist für API-Schlüssel und OAuth-Tokens bewusst nicht freigegeben: Geldbewegungen verlangen eine interaktive Dashboard-Sitzung als Owner oder Admin. Die API bietet nur Lesen und Vorschau. Siehe Auszahlungen: Auszahlungen finanzieren.

GET /api/v1/payouts/funding/status lässt sich mit einem API-Schlüssel lesen. Der Endpunkt liefert den Finanzierungsmodus (debit oder balance), das Guthaben aus Banküberweisungen in Cent, wenn der Modus balance ist, den Stand der Verifizierung, payoutsAllowed mit payoutsBlockedReason und payoutsBlockedMessage (der Satz, den das Dashboard anzeigt) sowie unverifiedRules (Haltetage, die rollierende Obergrenze und der im aktuellen Zeitraum genutzte und verbleibende Teil davon). bankTransferInstructions ist für Aufrufer mit API-Schlüssel und OAuth immer null, und canManageFunding ist false: Die Nummern des eigenen Einzahlungskontos sieht nur eine interaktive Sitzung als Owner oder Admin im Dashboard.

POST /api/v1/payouts/preview

Rolle: Manager oder höher. Berechnet die auszahlbaren Salden, ohne etwas festzuschreiben. Beliebig oft sicher aufrufbar.

Anfrageinhalt (alle Felder optional):

{
  "programId": null,
  "since": null,
  "affiliateId": null
}
  • programId: beschränkt die Vorschau auf eine Kampagne.
  • since: berücksichtigt nur Conversions ab diesem ISO-8601-Zeitstempel.
  • affiliateId: Vorschau für einen einzelnen Affiliate.

Die Antwort meldet die auszahlbaren Summen und die Kandidaten pro Affiliate:

  • totalEligibleCents, candidateCount, candidatesWithStripeConnectCount
  • graceDays und homeCurrency
  • eligibleByCurrency: auszahlbare Summen pro Währung über alle Kandidaten, jeweils in den nativen kleinsten Einheiten der Währung
  • unconvertibleByCurrency: native kleinste Einheiten pro Währung, die sich nicht in der Heimatwährung bewerten ließen (kein brauchbarer gespeicherter Kurs); totalEligibleInHomeCurrencyCents lässt sie weg
  • nativeTotalsForRun: native auszahlbare Summen pro Währung über die Kandidaten, die bereit zur Zahlung sind. Das ist genau die Map, die das Dashboard beim Ausführen der Auszahlung bestätigt, und ihre Schlüssel sind die Währungen, die der Lauf abdeckt
  • fxUnavailableCurrencies: Währungen, die aus dem Lauf herausgenommen werden, weil kein aktueller gespeicherter Referenzkurs vorliegt; ihre Salden laufen weiter auf und werden auszahlbar, sobald wieder Kurse vorliegen
  • usdEstimateAtRunRateCents: eine reine Anzeigeschätzung in USD für alles Auszahlbare zu den heute gespeicherten Kursen (Auszahlungen werden in USD abgerechnet; der endgültige Wert steht fest, wenn der Lauf ausgeführt wird)
  • fxRunRates: Map von Währungscode zu { rate, asOf }, dem gespeicherten Referenzkurs auf USD-Basis, zu dem jede Währung der Vorschau umgerechnet würde, als native Haupteinheiten pro 1 USD (USD ist 1); fehlt für die Währungen in fxUnavailableCurrencies. Der USD-Wert eines Topfs ist round(nativeCents / minorUnitsPerMajor(native) / rate * 100)
  • Gebührenfelder, die den Plattformanteil am Lauf aus der Vorschau beschreiben
  • candidates[]: ein Eintrag pro Affiliate mit affiliateId, affiliateEmail, currency, eligibleCents, eligibleConversionIds, payoutReady und blockedReason (warum ein Affiliate noch nicht ausgezahlt werden kann, zum Beispiel weil ein Auszahlungskonto fehlt)

Ein Lauf erzeugt aus den bestätigten Währungen eine USD-Auszahlung pro Affiliate, rechnet jede Provision zum gespeicherten Kurs am Tag des Laufs um und vermerkt den nativen Betrag, den Kurs und das Kursdatum bei jeder Position (die Felder von lineItems oben).

POST /api/v1/payouts/run

Nur in der Dashboard-Sitzung. Ein Auszahlungslauf lässt sich nicht mit einem API-Schlüssel oder OAuth-Token auslösen; jeder Aufrufer mit API-Schlüssel bekommt 403 interactive_session_required. Nur eine angemeldete Dashboard-Sitzung als Owner kann einen Auszahlungslauf starten.

Zur Information: Der Aufruf aus dem Dashboard nimmt confirmTotalCents an (Pflicht, als Sicherheitsprüfung), dazu optional affiliateIds, programId, since, currency (Standard "USD"), method, externalTransactionId, notes und conversionIds. confirmTotalCents muss der vom Server berechneten Summe entsprechen; weichen die Werte ab (zum Beispiel, weil zwischen Vorschau und Lauf eine Conversion abgeschlossen wurde), liefert die Anfrage 409 { "error": "confirm_total_mismatch" }. Hol dann eine neue Vorschau und versuch es erneut. Jeder Lauf löst pro Empfänger payout.created aus, danach payout.paid oder payout.failed.

Vertrauen und Sicherheit (5. September 2026). Vier weitere 409-Codes können von einem Lauf zurückkommen: payouts_require_active_subscription (die Organisation ist in der Testphase oder hat keinen aktiven bezahlten Plan), unverified_prefund_only (eine nicht verifizierte Organisation wollte einen Lauf über ein Bankeinzugsmandat ausführen; finanziere per Banküberweisung oder beantrage die Verifizierung), unverified_cap_exceeded (der Lauf würde eine nicht verifizierte Organisation über ihre rollierende Obergrenze bringen; die Meldung nennt die Zahlen) und funding_frozen (die Finanzierung von Auszahlungen ist nach einem Bankstreitfall bei einer Finanzierungsbelastung pausiert; die Meldung bittet dich, support@rekomi.com zu kontaktieren). Ein fünfter, high_risk_unverified, kommt von einem Lauf und von den Endpunkten zur Einrichtung der Finanzierung zurück, wenn der Betrugs-Score einer nicht verifizierten Organisation auf oder über der Schwelle liegt, die die Super-Admins von Rekomi festlegen (standardmäßig 60): Finanzierung und Auszahlungen warten, bis jemand bei Rekomi das Konto für die Finanzierung freigibt oder verifiziert, und der Finanzierungsstatus trägt neben dem Code verificationRequired: true. Ein sechster, identity_required, kommt von einem Lauf und von den Finanzierungsendpunkten zurück, wenn Rekomi eine Identitätsprüfung verlangt (ein Betrugs-Score auf oder über der Schwelle, die die Super-Admins von Rekomi dafür festlegen, oder eine Vorgabe eines Prüfers für die Marke), bis die Marke die Prüfung von Ausweis und Selfie bei Stripe bestanden hat oder ein Super-Admin sie aufhebt; er wird vor high_risk_unverified geprüft, und der Finanzierungsstatus trägt neben dem Code identityRequired: true. Ein Lauf kann auch erfolgreich sein, während jede Zeile needsManualReview: true trägt: Dann wartet der Lauf vor jeder Finanzierungsbelastung auf die Prüfung durch Rekomi, es wird nichts belastet und nichts ausgezahlt, bis er freigegeben ist, und die Zeilen gehen nach der Entscheidung in den normalen Finanzierungsablauf über (oder auf Failed). Der Beleg des Laufs zeigt reviewStatus und bei nicht verifizierten Organisationen holdUntil: Die abgerechnete Finanzierung wartet die Haltefrist ab, bevor die Affiliates ausgezahlt werden. Siehe Verifizierung und Prüfung von Auszahlungen.

POST /api/v1/payouts/{id}/retry

Nur in der Dashboard-Sitzung. Wiederholt eine Auszahlung mit Failed. Wie /run lehnt dieser Endpunkt jeden Aufrufer mit API-Schlüssel und OAuth mit 403 interactive_session_required ab; nur eine angemeldete Dashboard-Sitzung als Owner kann wiederholen. Löst bei Erfolg dieselben Ereignisse aus wie /run.

GET /api/v1/payouts/automatic/preview

Scope: read. Probelauf des Zeitplans für automatische Auszahlungen: was der nächste Lauf zahlen würde, wessen Saldo unter dem Mindestbetrag in den nächsten Lauf wandert und wer Geld offen hat, aber kein verbundenes Konto.

{
  "enabled": true, "cadence": "Weekly", "minPayoutCents": 2000,
  "lastAutomaticPayoutAt": "2026-09-22T14:00:00Z", "nextRunAtUtc": "2026-09-29T14:00:00Z",
  "wouldPay": { "affiliateCount": 6, "totalCents": 41800, "byCurrency": { "USD": 41800 } },
  "belowMinimum": { "affiliateCount": 2, "totalCents": 1500, "byCurrency": { "USD": 1500 } },
  "notConnected": { "affiliateCount": 1, "totalCents": 9000, "byCurrency": { "USD": 9000 } }
}

GET /api/v1/payouts/ledger

Scope: read. Das Plattformjournal deiner Organisation: Belastungen der Marke, Auszahlungen an Affiliates und Rückbuchungen, die neuesten zuerst. Query: take, skip.

[
  { "id": "...", "payoutId": "...", "affiliateId": "...", "direction": "Debit", "type": "BrandFunding", "amountCents": 10094, "currency": "USD", "rail": "StripeAch", "railReferenceId": "py_...", "occurredAt": "2026-09-01T09:00:00Z", "reconciliationStatus": "Settled" }
]

GET /api/v1/payouts/receipts

Scope: read. Ein Beleg pro finanziertem Auszahlungslauf. Query: take, skip.

[
  { "id": "...", "receiptNumber": "R-2026-0031", "currency": "USD", "grossCents": 98000, "platformFeeCents": 2940, "affiliateFeeCents": 1125, "passthroughCents": 0, "netToAffiliatesCents": 96875, "brandTotalCents": 100940, "payoutCount": 5, "billingCase": "BrandFunded", "brandFundingChargeId": "py_...", "brandFundingStatus": "Succeeded", "periodStart": "2026-08-01", "periodEnd": "2026-08-31", "createdAt": "2026-09-01T09:00:00Z", "settledAt": "2026-09-03T00:00:00Z", "reversedAt": null }
]

GET /api/v1/payouts/receipts/{id} liefert denselben Eintrag unter receipt plus organizationName und affiliates, eine Zeile pro Auszahlung (payoutId, affiliateId, affiliateName, affiliateEmail, grossCents, platformFeeCents, affiliateFeeCents, netCents, currency, status, reversed und fxLines bei zusammengefassten Auszahlungen in mehreren Währungen). GET /api/v1/payouts/receipts/{id}/receipt.html stellt den Beleg als HTML dar, und GET /api/v1/payouts/receipts/{id}/receipt.csv lädt die Zeilen herunter. Andernfalls 404 receipt_not_found.

Auszahlungsaktionen nur im Dashboard

POST /api/v1/payouts/funding/setup, POST /api/v1/payouts/funding/bank-transfer/enable, POST /api/v1/payouts/funding/bank-transfer/disable, POST /api/v1/payouts/verification-request, POST /api/v1/payouts/{id}/reverse und POST /api/v1/payouts/{id}/reverse-lines bewegen Geld oder ändern, wie es finanziert wird, deshalb verlangen sie eine angemeldete Dashboard-Sitzung. Ein API-Schlüssel bekommt dort 403.

Audit-Log

Planstufe: Pro+ für Aufrufer mit API-Schlüssel (Export für SIEM oder Compliance). Im Dashboard per JWT als Owner ohne Einschränkung und mit dem vollständigen Schema. Aufrufer mit API-Schlüssel bekommen außerdem eine geschwärzte Antwort (die Felder für IP, UserAgent und interne UserId der handelnden Person sind im JSON auf null gesetzt; die CSV-Kopfzeile schrumpft auf 8 Spalten).

GET /api/v1/audit-log

Scope: read. Rolle: Owner (nur JWT; API-Schlüssel umgehen die Rollenprüfung, die Antwort ist dann aber geschwärzt; siehe unten).

Query: action, entityType, since (ISO), until (ISO), page (Standard 1), pageSize (Standard 50, höchstens 100).

Aufrufer mit API-Schlüssel bekommen einen geschwärzten Inhalt ohne personenbezogene Daten der handelnden Personen (IPs, interne UserIds, UserAgents). Aufrufer über das Dashboard per JWT bekommen den vollständigen Inhalt für Analysen im Produkt. Die Query-Filter actorUserId und ip sind für Aufrufer mit API-Schlüssel ebenfalls abgeschaltet (passend zur Schwärzung, damit sich die Werte nicht erschließen lassen).

// API-key response (redacted)
{
  "page": 1,
  "pageSize": 50,
  "total": 1234,
  "items": [
    {
      "id": "audit-uuid",
      "action": "program.created",
      "entityType": "program",
      "entityId": "0789abcd-...",
      "actorType": "user",
      "actorUserId": null,
      "ip": null,
      "userAgent": null,
      "before": null,
      "after": "{\"id\":\"0789abcd-...\",\"name\":\"Default program\",\"...\":...}",
      "createdAt": "2026-05-01T12:00:00Z"
    }
  ]
}

actorType ordnet ein, wer oder was die Aktion ausgelöst hat. Die Werte sind user (über Clerk angemeldeter Nutzer der Marke), api_key (programmatischer Aufruf über rk_live_*), mcp (über MCP weitergeleiteter Aufruf über rk_mcp_*), system (Hintergrundjob / Hangfire), admin (Aktion im Super-Admin-Portal). Aufrufer mit API-Schlüssel sehen das Feld, weil es eine Kategorie ist und keine personenbezogenen Daten.

GET /api/v1/audit-log/export.csv

Scope: read. Rolle: Owner (gleiche Schwärzung wie beim JSON-Endpunkt).

CSV-Kopfzeile für API-Schlüssel (geschwärzt, 8 Spalten):

id,created_at,action,entity_type,entity_id,actor_type,before,after

CSV-Kopfzeile im Dashboard per JWT (vollständig, 11 Spalten):

id,created_at,action,entity_type,entity_id,actor_type,actor_user_id,ip,user_agent,before,after

Begrenzt auf 10,000 Zeilen. Einträge im Audit-Log werden nur angehängt; über die API lassen sie sich weder bearbeiten noch löschen.

Dashboard-Kennzahlen

Planstufe: Starter+ für Aufrufer mit API-Schlüssel. Im Dashboard per JWT ohne Einschränkung.

Zusammengefasste Kennzahlen auf Seite der Marke. Rolle: Manager bei JWT; API-Schlüssel umgehen die Prüfung und lesen.

GET /api/v1/dashboard/summary

Scope: read. Liefert die wichtigsten KPIs für den aktiven Zeitraum (der Query-Parameter since ist standardmäßig 30 Tage zurück).

{
  "totalClicks": 1842,
  "totalConversions": 87,
  "totalLeads": 134,
  "totalEarningsCents": 1734500,
  "conversionRate": 4.7232,
  "activeAffiliates": 23,
  "pendingPayoutsCents": 412000,
  "homeCurrency": "USD",
  "earningsUnconvertibleByCurrency": [],
  "pendingPayoutsUnconvertibleByCurrency": []
}

conversionRate ist der Prozentwert (zum Beispiel 4.72 = 4.72%), gerundet auf 4 Nachkommastellen. Conversions mit Refunded und Denied zählen nicht zu totalConversions und totalEarningsCents. Geldfelder sind in deine homeCurrency umgerechnet; Beträge, die sich nicht umrechnen ließen (kein Wechselkurs für diese Währung), werden pro Währung in earningsUnconvertibleByCurrency und pendingPayoutsUnconvertibleByCurrency gemeldet, statt stillschweigend in die Summen einzufließen. totalLeads zählt alle Lead-Zeilen im Zeitraum (kostenlose, identifizierte Anmeldungen plus Leads aus Kampagnen, die pro Lead zahlen); siehe Leads und Anmeldungen tracken.

GET /api/v1/dashboard/timeseries

Scope: read. Zeitreihen für Diagramme.

Query: metric (Pflicht: clicks | conversions | commissions | leads | payouts | refunds | revenue), since, until, granularity (derzeit nur day; Zeiträume über 400 Tage werden monatlich gebündelt), affiliateId (optional: beschränkt die Reihe auf einen Affiliate, das liest die Ansicht Aktivität auf der Detailseite eines Affiliates; funktioniert für jede Kennzahl außer payouts), programId (optional: beschränkt auf eine Kampagne). Summiere die count-Werte für eine Gesamtzahl im Zeitraum, zum Beispiel die Klickzahl eines Affiliates.

metric=leads liefert die tägliche Zahl aller Lead-Zeilen (kostenlose, identifizierte Anmeldungen plus Leads aus Kampagnen, die pro Lead zahlen); siehe Leads und Anmeldungen tracken.

{
  "metric": "conversions",
  "granularity": "day",
  "since": "2026-04-14T00:00:00Z",
  "until": "2026-05-14T00:00:00Z",
  "points": [
    { "bucket": "2026-04-14", "count": 3 },
    { "bucket": "2026-04-15", "count": 5 },
    { "bucket": "2026-04-16", "count": 0 }
  ]
}

Bei Geldkennzahlen (payouts, commissions, refunds, revenue) ist das Feld count (trotz des Namens) die tägliche, per FX umgerechnete Summe in kleinsten Einheiten; revenue sind die Bruttobeträge der Verkäufe, commissions die Einnahmen der Affiliates. Bei clicks, conversions und leads ist count eine echte Anzahl.

GET /api/v1/dashboard/leaderboard

Scope: read. Die besten Affiliates nach Kennzahl.

Query: metric (clicks | conversions | earnings | epc), limit (Standard 10, höchstens 50), since, programId (optional: nur die Affiliates einer Kampagne in die Rangliste aufnehmen).

[
  { "affiliateId": "01234567-...", "fullName": "Jane Doe", "email": "jane@creator.com", "value": 12 },
  { "affiliateId": "abcdef00-...", "fullName": "Joe Smith", "email": "joe@example.com", "value": 9 }
]

value ist pro Affiliate die Zahl der Conversions (metric=conversions), die Zahl der Klicks (clicks), die Summe von commissionCents in der Heimatwährung (earnings) oder die Einnahmen pro Klick in Cent (epc, per FX umgerechnete Provision geteilt durch Klicks; berücksichtigt werden nur Affiliates mit mindestens einem Klick). email ist die E-Mail-Adresse des Affiliates (deine eigenen Daten).

GET /api/v1/dashboard/traffic-sources

Scope: read. Aufschlüsselung der Klicks nach Quelle, eingeordnet nach Kanal. Die 20 wichtigsten Quellen; jede Zeile trägt einen channel: paid (gclid/msclkid/ttclid oder Werbe-Referrer; beachte, dass fbclid allein als social eingeordnet wird, weil Facebook es auch an organisch geteilte Beiträge hängt), search, social, email, ai (Referrer von ChatGPT/Perplexity/Claude), signin (Anmeldedienste), onsite (die eigene Website der Marke), portal (von Rekomi gehostete Anmeldeseiten), bot, referral oder direct.

Query: since, until, programId (optional: eine Kampagne), affiliateId (optional: ein Affiliate).

[
  { "source": "youtube.com", "channel": "social", "count": 540, "percentage": 32.5 },
  { "source": "Google Ads", "channel": "paid", "count": 312, "percentage": 18.8 },
  { "source": "(direct)", "channel": "direct", "count": 280, "percentage": 16.9 }
]

Leere oder nicht auswertbare Referrer landen unter (direct).

GET /api/v1/dashboard/export.csv

Scope: read. CSV-Export der Zeitreihe (nur metric=clicks|conversions|payouts, plus since / until).

date,conversions
2026-04-14,3
2026-04-15,5

Auswertungen (Analytics)

Planstufe: Starter+ für Aufrufer mit API-Schlüssel. Im Dashboard per JWT ohne Einschränkung. Rolle: Manager bei JWT; API-Schlüssel umgehen die Prüfung und lesen. Scope: read.

Tiefere Aufschlüsselungen auf Seite der Marke unter GET /api/v1/dashboard/insights/{module} (Alias von /api/metrics/insights/{module}). Jedes Modul nimmt since, until, programId (eine Kampagne), affiliateId (ein Affiliate) und format=csv an, um dieselben Zeilen als CSV herunterzuladen (gehärtet gegen das Einschleusen von Tabellenformeln). Alle Geldwerte sind per FX in deine Heimatwährung umgerechnet; Zeilen, für deren Währung zum jeweiligen Datum kein brauchbarer gespeicherter Kurs vorliegt, bleiben außen vor und werden pro Währung gemeldet (siehe overview unten). Die Daten stammen nur aus dem, was Rekomi ohnehin erfasst. Ansichten, die einen Verkauf einer Klick-Dimension zuordnen, berücksichtigen deshalb die Abdeckung: Nur Conversions mit einem auslösenden Klick tragen diese Dimension. sale-origin meldet einen Anteil coverage als Zahl (zugeordnete im Verhältnis zu allen Conversions); geography und devices fassen Verkäufe ohne Klick-Dimension unter (unknown) zusammen, statt eine vollständige Zuordnung vorzutäuschen.

Module:

  • overview: clicks, conversions, conversionRate, epcCents, aovCents, grossRevenueCents, commissionCents, refundedConversions, refundRate, recurringConversions, recurringSharePct, homeCurrency. Außerdem liefert es leads (Zahl der Anmeldungen), clickToLeadRate und leadToSaleRate (beide als Anteile 0..1); siehe Leads und Anmeldungen tracken. Geld, das sich nicht in der Heimatwährung bewerten ließ (eine Verkaufswährung ohne brauchbaren gespeicherten Kurs für das Datum der Zeile), fließt nicht in grossRevenueCents, aovCents, commissionCents und epcCents ein, statt zum Nennwert gezählt zu werden, und wird pro Währung in nativen kleinsten Einheiten gemeldet: fxUnconvertibleByCurrency für die Umsatzzeilen und fxUnconvertibleCommissionByCurrency für die Provisionszeilen (jeweils null, wenn alles umgerechnet wurde). Diese Maps sind die maßgeblichen Werte. Der ältere Einzelwert fxUnconvertibleCents bleibt aus Kompatibilitätsgründen in der Antwort, ist aber eine Summe nativer kleinster Einheiten über alle Währungen hinweg und damit nur ein Signal, kein Geldbetrag: Ist er nicht null, lies die Maps. Der CSV-Download enthält dieselben drei als fx_unconvertible_cents, fx_unconvertible_by_currency und fx_unconvertible_commission_by_currency (jede Map als durch Leerzeichen getrennte Paare CODE:minorUnits).
  • sale-origin: die wichtigsten verweisenden Hosts nach conversions + revenueCents, plus attributedConversions / totalConversions / coverage.
  • geography: clicks, conversions, revenueCents pro Land.
  • devices: Aufteilung von clicks + conversions nach Mobile / Tablet / Desktop / Bot.
  • utm: Query dimension (source | medium | campaign); clicks, conversions, revenueCents pro Wert.
  • time-patterns: Zellen aus Wochentag (0=So) mal Tagesstunde (UTC) mit clicks + conversions.
  • time-to-convert: Töpfe <1h / 1-24h / 1-7d / >7d.
  • funnel: clicks -> conversions -> approved -> paid. Jeder Schritt ist { step, count }; das Dashboard zeigt den Anteil jedes Schritts am vorherigen (im Client berechnet, nicht als API-Feld geliefert). Gibt es im Zeitraum Lead-Daten, wird je nach Lage eine Stufe leads zwischen clicks und conversions eingefügt (clicks -> leads -> conversions -> approved -> paid); Marken ohne Leads im Zeitraum sehen nie eine leere Lead-Zeile.
  • attribution: Aufteilung von conversions, revenueCents, commissionCents nach Coupon / Link / Other.
  • recurring: Mischung aus einmalig und wiederkehrend, mrrByMonth (aus Abo-Belastungen), churnedSubscriptions.
  • customers: Zahl neuer und wiederkehrender Kunden (wiederholter Stripe-Kunde) + revenueCents (ohne Identität der Kunden).
  • top-products: die meistverkauften Produkte nach vermitteltem Umsatz. Abgedeckt sind Shopify, Stripe, Paddle und Chargebee (Positionen), Gumroad, Lemon Squeezy, Polar, Creem, Dodo (Bestellungen mit einem Produkt), Mollie (wenn die Zahlung Bestellpositionen enthält) und Abos bei Braintree; Recurly ist noch nicht abgedeckt. revenueCents ist die Zwischensumme der Position vor Rabatten, nicht der tatsächlich bezahlte rabattierte Betrag.
// GET /api/v1/dashboard/insights/sale-origin
{
  "sources": [{ "source": "youtube.com", "conversions": 12, "revenueCents": 184000 }],
  "attributedConversions": 41,
  "totalConversions": 58,
  "coverage": 0.7069,
  "homeCurrency": "USD"
}

Affiliates haben ein auf sie selbst beschränktes Gegenstück ohne personenbezogene Daten unter GET /api/me/insights/{module} (overview, sale-origin, geography, devices, links, time-patterns, utm, channels), jeweils ebenfalls mit affiliateId (um eine Mitgliedschaft herauszugreifen) und format=csv. Sie liefern nur die eigenen Zahlen und Provisionen des Aufrufers plus Hosts, Länder, Geräteklassen, Link-Slugs, Sub-IDs und Zeitfenster, nie die Identität von Kunden.

Embed (White-Label-iframe)

Planstufe: Growth+ (gilt für die Authentifizierung per JWT und per API-Schlüssel, weil Embed eine White-Label-Funktion ist und nicht nur API-Zugriff). Liefert bei niedrigeren Plänen 402 { "error": "plan_tier_required", required: "Growth", current: "Starter" }.

POST /api/v1/embed/tokens

Scope: read_write. Erzeugt ein iframe-Token pro Affiliate.

{
  "affiliateId": "01234567-...",
  "allowedOrigins": "https://yourapp.com,https://app.yourapp.com",
  "rotate": false,
  "expiresInHours": 24
}
  • allowedOrigins: durch Kommas getrennte Origins als vollständige URL (Schema + Host + Port). Am öffentlichen iframe-Endpunkt gilt strikte Gleichheit; keine Platzhalter, kein Abgleich per StartsWith.
  • rotate: Bei false und einem schon vorhandenen Token für dieses (program, affiliate) liefert die Antwort nur Metadaten (alreadyExists: true) und kein Token im Klartext. Bei true wird das bestehende Token ersetzt und ein neues im Klartext zurückgegeben.
  • expiresInHours: Standard 24. Begrenzt auf [1, 720] (höchstens 30 Tage).

Antwort (neues oder rotiertes Token):

{
  "token": "ZGVtbzAxYWJjZGVm...",
  "allowedOrigins": "https://yourapp.com,https://app.yourapp.com",
  "expiresAt": "2026-05-15T12:00:00Z"
}

Antwort (Token schon vorhanden, rotate=false):

{
  "id": "embed-token-uuid",
  "allowedOrigins": "https://yourapp.com,https://app.yourapp.com",
  "expiresAt": "2026-05-15T12:00:00Z",
  "alreadyExists": true
}

Das token im Klartext kommt genau einmal zurück. Speichere es sofort auf deinem Anwendungsserver; Rekomi speichert nur seinen SHA-256-Hash und kann den Klartext nicht wiederherstellen.

POST /api/v1/embed/tokens/{id}/revoke

Scope: read_write. Widerruft ein ausgegebenes Token.

Anfrageinhalt: {}. Antwort: { "ok": true }.

GET /api/embed/public/dashboard?token=...

Ohne Authentifizierung, aber durch Token und Origin geschützt. Wird direkt vom iframe im Browser genutzt. Liefert die Einnahmen des Affiliates + das Branding der Marke.

{
  "affiliateName": "Jane Doe",
  "programName": "Default program",
  "earnedCents": 12450,
  "paidCents": 9800,
  "pendingCents": 2650,
  "brandColor": "#0E7C7B",
  "brandName": "Your Brand",
  "recentConversions": [
    {
      "id": "c0nv0001-...",
      "amountCents": 9900,
      "commissionCents": 1980,
      "status": "Approved",
      "createdAt": "2026-05-10T16:00:00Z"
    }
  ]
}

Fehler:

  • 400: Query-Parameter token fehlt
  • 401: {} leerer Antwortinhalt ODER { "error": "token_expired" }, wenn das expiresAt des Tokens in der Vergangenheit liegt
  • 403: { "error": "no_allowed_origins_configured" } | { "error": "origin_required" } | { "error": "origin_invalid" } | { "error": "origin_not_allowed" }

Die Origin-Prüfung verlangt, dass die Anfrage einen Header Origin sendet UND dieser Origin (Schema + Host + Port) einem Eintrag in allowedOrigins entspricht. Server-zu-Server-Aufrufer ohne Origin-Header werden abgelehnt.

earnedCents, paidCents und pendingCents werden über die 50 neuesten Conversions des Affiliates berechnet, nicht über die gesamte Historie.

GET /api/v1/embed/dashboard

Ohne Authentifizierung. Die Daten, die das White-Label-iframe darstellt, gelesen mit dem Token pro Affiliate aus POST /api/v1/embed/tokens: Query token. Wird gegen die erlaubten Origins des Tokens geprüft. Liefert affiliateName, programName, earnedCents, paidCents, pendingCents, brandColor, brandName und recentConversions (bis zu 20 Zeilen mit id, amountCents, commissionCents, status, createdAt).

S2S-Tracking

Planstufe: Starter+. Organisationen in der Testphase werden immer angenommen (14 Tage kostenlos in jedem Plan). Organisationen unter Starter, die nicht in der Testphase sind, bekommen 402.

POST /api/tracking/s2s

Liegt außerhalb des Namensraums /api/v1/*, der nur einen Bearer verlangt, weil S2S zum Bearer noch eine HMAC-Integritätsprüfung hinzufügt.

Header:

X-Rekomi-Api-Key: rk_live_xxxx
X-Rekomi-Signature: t=1715366423,sig=8f4e2c5b...
Content-Type: application/json

(Authorization: Bearer rk_live_xxxx funktioniert auch anstelle von X-Rekomi-Api-Key.)

Anfrageinhalt:

{
  "externalEventId": "purchase_abc123",
  "affiliateSlug": "jane-recommends",
  "amountCents": 9900,
  "currency": "USD",
  "customerId": "cus_external_xyz"
}

Antwort (neu):

{ "ok": true, "conversionId": "c0nv0001-..." }

Antwort (Duplikat, sicher wiederholbar; dieselbe externalEventId wurde für diese Organisation schon erfasst):

{ "ok": true, "deduped": true }

Gegenseitiger Ausschluss: Solange eine direkte Verbindung zu einem Zahlungsdienstleister aktiv ist (Stripe Connect, Paddle, Braintree, Lemon Squeezy, die Apps für Shopify, Wix und BigCommerce, das WooCommerce-Plugin, Chargebee, Polar, Recurly, Gumroad, Creem, Dodo oder Mollie), liefert der Endpunkt 409 mit dem passenden Fehlercode (stripe_connect_active, paddle_connect_active, braintree_connect_active, lemonsqueezy_connect_active, shopify_connect_active, wix_connect_active, bigcommerce_connect_active, wordpress_connect_active, chargebee_connect_active, polar_connect_active, recurly_connect_active, gumroad_connect_active, creem_connect_active, dodo_connect_active, mollie_connect_active), damit derselbe Verkauf nie doppelt gezählt wird.

Die vollständige Berechnung der Signatur, den Schutz vor Replays und Beispiele in mehreren Sprachen findest du unter S2S-Tracking.

POST /api/tracking/lead

Erfasst einen Lead (eine kostenlose, identifizierte Anmeldung, also eine mit der Empfehlung verknüpfte E-Mail-Adresse) statt eines Verkaufs. Gleiche Authentifizierung wie bei /api/tracking/s2s (Bearer-API-Schlüssel oder X-Rekomi-Api-Key, plus die HMAC über X-Rekomi-Signature), und dieselben Konflikte 409 *_connect_active durch gegenseitigen Ausschluss, solange eine direkte Verbindung zu einem Zahlungsdienstleister aktiv ist. Ein Lead allein erzeugt keine Provision; ein späterer Verkauf mit derselben E-Mail-Adresse wird dem vermittelnden Affiliate über den Fallback per E-Mail-Abgleich gutgeschrieben, auch wenn das Klick-Cookie verschwunden ist.

Anfrageinhalt:

{
  "affiliateSlug": "jane-recommends",
  "email": "user@example.com",
  "name": "Jane Doe",
  "externalEventId": "signup_abc123"
}
  • affiliateSlug (Pflicht): der Affiliate, dem der Lead gutgeschrieben wird. Groß- und Kleinschreibung spielt keine Rolle, SERKAN und serkan führen also zum selben Affiliate.
  • email (Pflicht): die E-Mail-Adresse des Kunden, mit der ein späterer Verkauf abgeglichen wird.
  • name (optional) und externalEventId (optional, dient der Deduplizierung, damit Wiederholungen sicher sind).
  • subId (optional): der Kanal-Tag, den die Anmeldung mitbrachte; ein späterer Verkauf mit derselben E-Mail-Adresse übernimmt ihn.

Unter Leads und Anmeldungen tracken findest du das Gegenstück Rekomi.convert() im Browser, den automatischen Weg bei Stripe / Paddle und wie die Attribution per E-Mail-Abgleich funktioniert.

Öffentliche Endpunkte (ohne Authentifizierung)

Diese Endpunkte brauchen keinen API-Schlüssel; sie haben ein Rate-Limit pro IP.

GET /api/p/by-handle/{handle}/{campaign?}

Öffentliche Informationen zur Kampagne für die Landingpage, die Affiliates sehen, bezogen auf die Organisation. Anonym; Rate-Limit pro IP. {handle} ist die Marken-Subdomain der Organisation (das {handle} in {handle}.rekomi.com); der Resolver findet die Organisation über das Handle und darin die Kampagne über ihren Slug. Lass {campaign} weg, um die Hauptkampagne der Organisation abzurufen (die direkt unter {handle}.rekomi.com ausgeliefert wird).

Dieser Endpunkt ersetzt die frühere globale Slug-Abfrage GET /api/p/{slug}. Sie wurde entfernt, weil Kampagnen-Slugs nur pro Organisation eindeutig sind, nicht global.

{
  "id": "0789abcd-...",
  "organizationName": "Your Brand",
  "name": "Default program",
  "slug": "default",
  "description": "Earn 20% recurring on every annual plan.",
  "termsUrl": "https://yourbrand.com/affiliate-terms",
  "customAliasSupported": true
}

Die vollständige Antwort enthält mehr als oben gezeigt: außerdem organizationDescription, die Provisionseinstellungen (defaultCommissionType, defaultCommissionValue, defaultCommissionModel), cookieWindowDays, recurringMonths, attributionModel, autoApprove, isPubliclyDiscoverable, landingPageConfig sowie die URLs von Logo und Favicon der Marke, also alles, was die öffentliche Landingpage zum Darstellen braucht.

Ein ausgemusterter (umbenannter) Kampagnen-Slug liefert 200 { "redirect": true, "campaign": "<current-slug>" }, damit der Aufrufer zum aktuellen Slug weitergehen kann. Liefert 404 Not Found, wenn das Handle zu keiner Organisation führt, oder bei inaktiven oder archivierten Kampagnen. Außer der id der Kampagne wird keine GUID der Organisation preisgegeben.

GET /api/p/by-domain?host={host}

Löst eine eigene Domain (die Domain einer Marke, zum Beispiel affiliates.yourbrand.com; in jedem Plan verfügbar) zum Handle der Organisation und zum Slug der Hauptkampagne auf. Anonym; Rate-Limit pro IP.

{ "handle": "yourbrand", "slug": "default" }

Liefert 404 Not Found, wenn der Host bei keiner Organisation eine bestätigte eigene Domain ist.

POST /api/p/id/{programId}/apply

Reicht eine Bewerbung als Affiliate ein. Der Pfad hängt an der GUID der Kampagne ({programId}), nicht an ihrem Slug, weil Kampagnen-Slugs nur pro Organisation eindeutig sind, nicht global. Ermittle die GUID vorher über GET /api/p/by-handle/{handle}/{campaign?} (das Feld id). Anonym; Rate-Limit auf 5 Anfragen pro Minute pro IP. Löst affiliate.created aus; hat die Kampagne autoApprove=true, zusätzlich affiliate.approved.

Anfrage:

{
  "email": "newaffiliate@example.com",
  "fullName": "New Affiliate",
  "website": "https://creator.example.com",
  "audience": "200k YouTube subscribers in the fitness niche",
  "whyInterested": "Already an evangelist for the product",
  "customAlias": "newaff-fit"
}

Alle Felder außer email sind optional. customAlias muss zu [a-z0-9-]{3,64} passen.

Antwort: Der Endpunkt liefert immer denselben, anonymisierten Aufbau, damit sich Bewerber nicht ausforschen lassen. Er gibt nie die echte ID des Affiliates oder den Link-Slug zurück; affiliateId ist immer die GUID aus lauter Nullen und affiliateLinkSlug immer null. Eine doppelte Bewerbung (dieselbe E-Mail-Adresse hat sich schon für diese Kampagne beworben) liefert genau denselben Aufbau wie eine neue.

{
  "affiliateId": "00000000-0000-0000-0000-000000000000",
  "status": "Pending",
  "affiliateLinkSlug": null,
  "accountExists": false,
  "email": "newaffiliate@example.com",
  "message": "Application received."
}
  • accountExists: ob der Bewerber für diese E-Mail-Adresse schon ein Affiliate-Konto bei Rekomi hat (das öffentliche Formular leitet ihn damit zur Anmeldung weiter).
  • email: die eigene E-Mail-Adresse des Bewerbers, zurückgegeben.

Fehler:

  • 400: Die Validierung der Anfrage ist gescheitert; liefert ein Standardobjekt mit Problemdetails ({ type, title, status, errors }), nicht den Envelope für Geschäftsregeln
  • 404: Kampagne nicht gefunden / inaktiv
  • 409: { "error": "alias_already_taken" } oder { "error": "program_at_capacity", "message": "..." }

Header-Referenz

HeaderRichtungZweck
Authorization: Bearer rk_live_*AnfrageAPI-Schlüssel als Bearer (jeder authentifizierte Endpunkt).
X-Rekomi-Api-Key: rk_live_*Anfrage (nur S2S)Alternative zu Authorization bei /api/tracking/s2s.
X-Rekomi-Signature: t=<unix>,sig=<hex>Anfrage (nur S2S)HMAC über <unix>.<body> mit deinem Signing Secret rks_*.
Idempotency-Key: <uuid>AnfrageFunktioniert bei jedem POST und PATCH, den du möglicherweise wiederholst (Importe, Einladungen, Anlegen von Affiliates). Replay-Fenster von 60 Minuten. Gleicher Schlüssel + gleicher Inhalt → ursprüngliche Antwort; gleicher Schlüssel + anderer Inhalt → 409.
Idempotency-Replayed: trueAntwortGesetzt, wenn eine Antwort aus einem früheren idempotenten Aufruf wiederholt wird.
X-Rekomi-Signature: t=<unix>,v1=<hex>Ausgehend (Webhook-Zustellung)HMAC-Signatur auf ausgehenden Webhook-Payloads. Nutze v1= zur Prüfung von Webhooks (im Unterschied zu sig= bei eingehendem S2S; getrennte Code-Pfade).
X-Rekomi-Timestamp: <unix>Ausgehend (Webhook-Zustellung)Unix-Zeitstempel in Sekunden, über den die Signatur berechnet wird.
X-Rekomi-Event: <type>Ausgehend (Webhook-Zustellung)Ereignistyp für das Routing, ohne den Inhalt zu parsen.
X-Rekomi-Delivery-Id: <uuid>Ausgehend (Webhook-Zustellung)Schlüssel zur Deduplizierung bei Wiederholungen.

Einstellungen

GET /api/v1/settings/home-currency

Liefert die aktuelle Heimatwährung (HomeCurrency) der Organisation, den Zeitpunkt der letzten Änderung, die Positivliste der unterstützten ISO-4217-Codes und ob der Plan des Aufrufers eine Änderung erlaubt (Plan Pro oder höher).

{
  "homeCurrency": "USD",
  "changedAt": null,
  "supportedCurrencies": [
    "AED", "AFN", "ALL", "AMD", "AOA", "ARS", "AUD", "AWG", "AZN", "BAM",
    "BBD", "BDT", "BHD", "BIF", "BMD", "BND", "BOB", "BRL", "BSD", "BWP",
    "BYN", "BZD", "CAD", "CDF", "CHF", "CLP", "CNY", "COP", "CRC", "CVE",
    "CZK", "DJF", "DKK", "DOP", "DZD", "EGP", "ETB", "EUR", "FJD", "FKP",
    "GBP", "GEL", "GIP", "GMD", "GNF", "GTQ", "GYD", "HKD", "HNL", "HTG",
    "HUF", "IDR", "ILS", "INR", "ISK", "JMD", "JOD", "JPY", "KES", "KGS",
    "KHR", "KMF", "KRW", "KWD", "KYD", "KZT", "LAK", "LBP", "LKR", "LRD",
    "LSL", "MAD", "MDL", "MGA", "MKD", "MMK", "MNT", "MOP", "MUR", "MVR",
    "MWK", "MXN", "MYR", "MZN", "NAD", "NGN", "NIO", "NOK", "NPR", "NZD",
    "OMR", "PAB", "PEN", "PGK", "PHP", "PKR", "PLN", "PYG", "QAR", "RON",
    "RSD", "RUB", "RWF", "SAR", "SBD", "SCR", "SEK", "SGD", "SHP", "SLE",
    "SOS", "SRD", "SZL", "THB", "TJS", "TND", "TOP", "TRY", "TTD", "TWD",
    "TZS", "UAH", "UGX", "USD", "UYU", "UZS", "VND", "VUV", "WST", "XAF",
    "XCD", "XCG", "XOF", "XPF", "YER", "ZAR", "ZMW"
  ],
  "canChange": true
}

PUT /api/v1/settings/home-currency

Ändert die Heimatwährung. Setzt den Plan Pro oder höher voraus. Die Änderung ist idempotent, wenn Vorher und Nachher gleich sind; im Audit-Log als org.home_currency_changed festgehalten. Frühere Conversions behalten ihre native Währung; nur die Umrechnung beim Lesen im Dashboard wird gegen die neue Heimatwährung neu berechnet.

Anfrageinhalt:

{ "homeCurrency": "EUR" }

Antwort:

{ "homeCurrency": "EUR", "changed": true, "changedAt": "2026-05-18T05:42:00Z" }

Fehler:

  • 400 unsupported_currency: steht nicht auf der Positivliste.
  • 402 plan_tier_required: Der Aufrufer ist im Plan Starter oder Growth.
  • 403: Der Aufrufer ist nicht Owner oder hat eine unbestätigte E-Mail-Adresse.

Ausführliche Erklärung: Berichte in mehreren Währungen. Details zur Übermittlung: Conversion-Währung.

CRM-Integrationen

Planstufe: Starter+ für Aufrufer mit API-Schlüssel. Scope: read.

GET /api/v1/integrations/crm

Nur verbundene Integrationen mit CRMs und E-Mail-Plattformen; ein Anbieter ohne Verbindung fehlt einfach. Eine Zeile pro Integration, sortiert nach slug: provider, slug, enabled, syncCustomers, salesAttributionEnabled, salesAttributionStatus, salesAttributionCheckedAt, salesAttributionCredited und webhookRegisteredAt. Den Sync-Status pro Affiliate findest du unter GET /api/v1/affiliates/{id}/crm-sync.

Sub-Affiliate-Recruiting

Planstufe: in jedem Plan enthalten und pro Kampagne einschaltbar. Die Endpunkte auf Seite der Affiliates (/me/sub-affiliate/*) sind ohne Einschränkung nutzbar, sobald die Kampagne es eingeschaltet hat.

Begrenzte Override-Provision über 2 Stufen: Affiliate A wirbt B; A verdient bei jedem Verkauf, den B abschließt, B.commission × Program.SubAffiliateCommissionPercent / 100. Eine Rekrutierungsebene mit fester Obergrenze; keine Weitergabe über eine Kette, nie mehrstufig. Override-Zeilen haben amountCents = 0, commissionCents > 0, attributionMethod = "sub_affiliate_override" und parentAffiliateConversionId, das auf die Zeile des ursprünglichen Verkaufs zeigt. Die Konzeptseite: Sub-Affiliate-Recruiting. Der vollständige Leitfaden für Entwickler mit Webhook-Payloads: Sub-Affiliate-API.

GET /api/v1/programs/{id}/sub-affiliate-config

Scope: read. Liefert die aktuelle Einstellung der Kampagne.

{
  "enabled": true,
  "percent": 10.0
}

PUT /api/v1/programs/{id}/sub-affiliate-config

Scope: read_write. Rolle: Owner. Plan: in jedem Plan enthalten. Bestätigung der E-Mail-Adresse: erforderlich. Im Audit-Log als program.sub_affiliate_config_changed festgehalten.

{ "enabled": true, "percent": 10 }

Fehler:

HTTPerrorUrsache
400unsupported_valueProzentwert außerhalb von [0, 100]
403email_not_verifiedDer Owner 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: nur Clerk-JWT (Seite der Affiliates). Liefert die persönliche Rekrutierungs-URL des aufrufenden Affiliates.

{
  "affiliateId": "01234567-...",
  "programId": "0789abcd-...",
  "programSlug": "default",
  "url": "https://{handle}.rekomi.com/default?recruited_by=v6media"
}

Für recruited_by gilt: Slug zuerst. Verwendet wird der kontoweite ?via=-Slug des Affiliates, falls er einen hat, sonst sein Link-Slug der Kampagne, sonst die GUID der Affiliate-ID. Liefert 400 program_id_required ohne den Query-Parameter, 400 sub_affiliate_recruiting_disabled, wenn der Schalter in der Kampagne aus ist, 403, wenn der aufrufende Affiliate nicht Approved ist, 404, wenn der aufrufende Affiliate die Kampagne nicht sehen kann, 409 portal_not_configured, wenn die Marke keinen Host für ein öffentliches Portal hat.

GET /api/v1/me/sub-affiliate/recruits

Scope: nur Clerk-JWT (Seite der Affiliates). Die Geworbenen des aufrufenden Affiliates. Mit Cursor-Paginierung.

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

GET /api/v1/affiliates/{id}/sub-affiliates

Scope: read. Detailansicht für die Marke auf die Geworbenen eines beliebigen Affiliates. Liefert ein flaches Array (ohne Paginierungs-Wrapper).

[
  {
    "id": "fed00b00-...",
    "email": "recruit@example.com",
    "fullName": "Recruited Person",
    "status": "Approved",
    "totalCommissionCents": 12400,
    "overrideEarnedCents": 1240,
    "currency": "USD"
  }
]

Ergänzung im Bewerbungsformular

POST /api/p/id/{programId}/apply nimmt im Anfrageinhalt einen optionalen Verweis auf den Werber an: recruitedBy (String: GUID der Affiliate-ID, Link-Slug der Kampagne oder kontoweiter ?via=-Slug) oder das ältere recruitedByAffiliateId (GUID). Führt der Verweis zu einem freigegebenen Affiliate in derselben Kampagne, wird RecruitedByAffiliateId des neuen Affiliates gesetzt. Nicht auflösbare Werte werden stillschweigend ignoriert; wirbt ein Affiliate sich selbst (derselbe Clerk-Nutzer oder dieselbe E-Mail-Adresse), wird der Verweis stillschweigend verworfen, die Bewerbung geht aber trotzdem durch; solange der Schalter für das Werben in der Kampagne aus ist, wird kein Werber verknüpft.

Organisationsprofil

Diese Endpunkte liegen unter ihrem kanonischen Pfad /api/organizations/me (ohne Alias unter /v1).

GET /api/organizations/me

Scope: read. Liefert das vollständige Profil der aufrufenden Organisation: Name, Slug, Marken-Subdomain samt Änderungszähler und Obergrenze, E-Mail-Adresse für die Abrechnung, Zeitzone, Markenfarbe, URLs von Logo und Favicon, Beschreibung, White-Label-Kennzeichen, Planstufe, Status des Abonnements, Ende der Testphase, Tage der Wartefrist für Auszahlungen (und das in der aktuellen Stufe erlaubte Maximum) sowie die Einstellungen für automatische Auszahlungen.

{
  "id": "01234567-...",
  "name": "Acme",
  "slug": "acme",
  "subdomain": "acme",
  "subdomainChangeCount": 0,
  "subdomainChangesMax": 2,
  "billingEmail": "billing@acme.com",
  "timezone": "America/Los_Angeles",
  "primaryBrandColor": "#3B0764",
  "logoUrl": "https://cdn.rekomi.com/...",
  "faviconUrl": null,
  "description": "Acme makes the world's best widgets.",
  "whiteLabelEnabled": false,
  "planTier": "Growth",
  "subscriptionStatus": "Active",
  "trialEndsAt": null,
  "payoutGraceDays": 30,
  "payoutGraceDaysMax": 90,
  "automaticPayoutEnabled": false,
  "automaticPayoutCadence": "Manual",
  "minPayoutCents": 0,
  "lastAutomaticPayoutAt": null
}

Die Antwort enthält außerdem die Branchenkategorie der Marke und die Felder des Katalogs für Attributionsparameter, die die Oberfläche des Dashboards nutzt.

Schreibzugriffe (PATCH /api/organizations/me, POST /api/organizations/me/logo usw.) bleiben auf JWT mit der Rolle Owner beschränkt. API-Schlüssel und MCP-Tokens können das Profil nicht ändern.

Abrechnungsstatus

GET /api/billing/state

Scope: read. Liefert die Planstufe der Organisation, den Status des Abonnements, das Ende der Testphase, den Status der Stripe-Verknüpfung und eine Momentaufnahme der Plangrenzen. Das ist der maßgebliche Endpunkt, den du aufrufst, bevor du einen Installationsweg empfiehlst: stripeConnectLinked = true bedeutet, dass die Checkout-Webhooks der Organisation automatisch bei Rekomi ankommen (der Feldname bleibt für einen stabilen Vertrag bestehen; die Verknüpfung ist die Rekomi-App für Stripe); false bedeutet, dass die Organisation Stripe nicht für die Attribution von Conversions verknüpft hat und Gutscheincodes, das Browser-Pixel oder S2S braucht, um Conversions zuzuordnen.

{
  "planTier": "Growth",
  "subscriptionStatus": "Active",
  "billingProvider": "Stripe",
  "trialEndsAt": null,
  "interval": "Monthly",
  "stripeSubscriptionId": "sub_...",
  "stripeCustomerLinked": true,
  "stripeConnectLinked": true,
  "couponsEnabled": true,
  "limits": {
    "affiliates": { "current": 12, "max": null },
    "campaigns": { "current": 2, "max": null },
    "partnerPayoutsThisMonthCents": { "current": 285000, "max": 1500000 },
    "teamSeats": { "current": 1, "max": 5 }
  },
  "stripeConnectedVia": "StripeApp"
}

billingProvider sagt, worüber die Organisation abgerechnet wird (zum Beispiel Stripe oder Shopify). couponsEnabled sagt, ob die Gutscheinfunktion nutzbar ist. stripeConnectedVia ist StripeApp, wenn das Stripe-Konto der Organisation über die Rekomi-App verknüpft ist, oder null, wenn Stripe nicht verknüpft ist; der Wert stimmt immer mit stripeConnectLinked überein.

Die Endpunkte für Stripe Checkout, das Portal und Zahlungsmethoden bleiben auf JWT beschränkt. API-Schlüssel und MCP-Tokens können keine neuen Checkout-Sitzungen öffnen und die Abrechnung nicht ändern.

Affiliate-Einladungen

Ablauf für Einladungen von der Marke an Affiliates. Liegt unter /api/affiliate-invites (ohne Alias unter /v1). Der Endpunkt für Sammeleinladungen setzt einen bezahlten Plan ab Starter voraus; einzelne Einladungen gehen in jedem Plan, auch in der Testphase.

POST /api/affiliate-invites

Scope: read_write. Rolle: Manager+. Bestätigung der E-Mail-Adresse: erforderlich für Aufrufer über das Dashboard (JWT); API-Schlüssel und MCP-Tokens sind ausgenommen.

Legt eine Einladung an. Liefert die Einladung plus eine Annahme-URL, die einmal gilt.

Anfrageinhalt:

{
  "programId": "01234567-...",
  "email": "alex@example.com",
  "fullName": "Alex Example",
  "slug": "alex",
  "personalNoteHtml": "<p>Hi Alex, would love to have you on our affiliate program.</p>",
  "customCommissionType": "Percentage",
  "customCommissionValue": 30,
  "autoApprove": true
}

Das optionale Feld slug behält den bisherigen Slug des Affiliates von einer früheren Plattform bei (für den direkten Umzug). Wird es mitgeschickt, setzt der Annahmeablauf ihn als neuen AffiliateLink.Slug, sofern er die Formatprüfung noch besteht (3 bis 64 Zeichen, [a-z0-9-], kein Bindestrich am Anfang oder Ende) UND weiterhin global eindeutig ist. Andernfalls nimmt der Annahmeablauf einen zufälligen Slug und zeigt eine Warnung. Bei der ersten Anmeldung des Affiliates wird daraus sein kontoweiter Empfehlungs-Slug (derselbe für alle Kampagnen, genutzt als ?via={slug}); hat er schon ein Affiliate-Konto bei Rekomi, bleibt stattdessen sein bestehender Konto-Slug. Der Server liefert 409 slug_already_taken, wenn der Slug beim Anlegen der Einladung schon vergeben ist; 400 slug_reserved, wenn er auf der Liste reservierter Wörter steht (api, admin usw.); 400 mit field: "slug", wenn Zeichensatz oder Länge die Validierung nicht bestehen.

personalNoteHtml wird über RichTextSanitizer (parserbasiert mit Ganss.Xss) auf eine strenge Positivliste bereinigt: p, br, strong, b, em, i, u, ul, ol, li, a. Das einzige erlaubte Attribut ist href an <a>, beschränkt auf https://-URLs. Alles andere wird entfernt. Unterstützte Platzhalter: {{affiliate.firstName}}, {{program.commission}}, {{brand.name}}. Die Platzhalter werden erst nach der Bereinigung eingesetzt, über ihre Werte lässt sich also kein Markup einschleusen.

Antwort:

{
  "invite": {
    "id": "...",
    "programId": "...",
    "programName": "...",
    "email": "alex@example.com",
    "state": "Pending",
    "expiresAt": "2026-05-26T...",
    "...": "..."
  },
  "acceptUrl": "https://app.rekomi.com/invites/affiliate/<one-time-token>"
}

Fehler:

  • 400 validation_failed mit field: E-Mail-Adresse fehlt oder ist ungültig, programId fehlt.
  • 409 affiliate_already_exists: In der Kampagne gibt es schon einen Affiliate mit dieser E-Mail-Adresse.
  • 409 pending_invites_limit_reached: Die Organisation hat 500 offene Einladungen.

POST /api/affiliate-invites/bulk

Scope: read_write. Rolle: Manager+. Bestätigung der E-Mail-Adresse: erforderlich für Aufrufer per JWT. Plan: Starter+ mit SubscriptionStatus = Active (Nutzer in der Testphase bekommen absichtlich 402 paid_plan_required; der Weg für massenhafte E-Mails ist missbrauchsanfällig und bekommt auch für MCP keine Ausnahme).

Bis zu 50 E-Mail-Adressen pro Aufruf. Duplikate innerhalb des Stapels und E-Mail-Adressen, die schon zu bestehenden Affiliates gehören, werden stillschweigend übersprungen. Angenommene Einladungen werden in einem gleichmäßigen, gedrosselten Tempo verschickt.

Zwei Formen des Anfrageinhalts werden angenommen. Die neuere Form recipients ist für Umzüge die bessere Wahl, weil du damit Slugs pro Affiliate beibehalten kannst; die ältere Form emails bleibt aus Gründen der Abwärtskompatibilität verfügbar.

Bevorzugte Form:

{
  "programId": "01234567-...",
  "recipients": [
    { "email": "alex@example.com", "slug": "alex", "fullName": "Alex Example" },
    { "email": "ben@example.com", "slug": "ben-r" },
    { "email": "carol@example.com" }
  ],
  "personalNoteHtml": "<p>We're migrating from Tapfiliate. Same commission, same campaign.</p>",
  "autoApprove": true
}

Ältere Form (ohne Slug pro Zeile):

{
  "programId": "01234567-...",
  "emails": ["alex@example.com", "ben@example.com", "carol@example.com"],
  "personalNoteHtml": "<p>...</p>",
  "autoApprove": true
}

Werden sowohl recipients als auch emails gesendet, gilt recipients.

Antwort:

{
  "created": 3,
  "skipped": 0,
  "invites": [ /* InviteResponse[] */ ],
  "slugConflicts": [
    { "email": "ben@example.com", "requestedSlug": "alex", "reason": "duplicate_within_batch" }
  ]
}

slugConflicts meldet das Ergebnis pro Zeile, wenn ein gewünschter Slug abgelehnt wurde (global vergeben, reserviert, ungültiger Zeichensatz oder doppelt im selben Stapel). Diese Zeilen bekommen trotzdem eine Einladung; bei der Annahme weichen sie auf einen neuen zufälligen Slug aus. Nutze das Array, um in deiner Oberfläche einen Hinweis wie „Diese 3 Affiliates haben ihren gewünschten Slug nicht bekommen“ anzuzeigen.

Fehler:

  • 400 validation_failed: leerer oder zu großer Stapel, ungültige programId.
  • 402 paid_plan_required: Die Organisation ist in der Testphase oder unter Starter.
  • 409 pending_invites_limit_reached: Die Organisation hat 500 offene Einladungen.

Lebenszyklus

  • GET /api/affiliate-invites (nur JWT): listet Einladungen auf, gefiltert nach programId + state (Pending / Accepted / Expired / Revoked).
  • POST /api/affiliate-invites/{id}/resend (nur JWT): rotiert das Token und verschickt die Einladung erneut.
  • DELETE /api/affiliate-invites/{id} (nur JWT): widerruft eine offene Einladung.
  • GET /api/affiliate-invites/by-token/{token} (anonym, mit Rate-Limit): zeigt Marke und Kampagne einer noch nicht angenommenen Einladung in der Vorschau.
  • POST /api/affiliate-invites/by-token/{token}/accept (anonym, mit Rate-Limit): nimmt die Einladung an. Legt die Zeile Affiliate an (je nach AutoApprove mit Approved oder Pending). Idempotent: Eine erneute Annahme liefert die ursprüngliche Affiliate-ID.

Darstellung des Installations-Snippets

GET /api/v1/r/install-snippet

Scope: read. Erzeugt auf dem Server das kanonische <script>-Snippet zur Installation, mit der echten Kampagnen-ID der aufrufenden Organisation, eigenen Attributionsparametern (wenn unter Einstellungen > Link-Tags eingerichtet), E-Mail-Erfassung und optional dem öffentlichen Pixel-Schlüssel, alles schon eingesetzt. Genutzt vom MCP-Tool get_install_snippet, damit ein KI-Agent einer Marke genau den Text zum Einfügen geben kann, ohne die HTML-Attribute selbst zusammenzusetzen; die Installationskarte im Dashboard erzeugt dasselbe Tag aus eigenen Vorlagen.

Optionale Query-Parameter:

  • programId (Guid): welche Kampagne eingebettet wird. Standard ist die neueste aktive Kampagne der Organisation.
  • withPixel (bool): fügt data-rekomi-org für den Weg hinzu, bei dem das Browser-Pixel die Conversion auslöst. Setzt voraus, dass vorher unter /dashboard/setup/install ein öffentlicher Pixel-Schlüssel angelegt wurde. Liefert 409 pixel_not_provisioned, wenn der Wert true ist und kein Schlüssel existiert.
  • emailCapture (bool): ob das Tag data-rkmi-email-capture="auto" trägt. Weggelassen oder true = an, der Standard für jedes Tag, das Rekomi ausgibt; false erzeugt das Tag ohne das Attribut. Ist es an, erfasst Rekomi eine E-Mail-Adresse, die ein vermittelter Besucher in ein E-Mail- oder Textfeld auf den Seiten mit dem Tag eintippt (Registrierung, Login, Checkout und andere Formulare), damit der Verkauf auch dann gutgeschrieben werden kann, wenn der Checkout keine Empfehlung weitergibt. Ohne Empfehlung wird nichts erfasst. Die Marke kann das Attribut auch aus einem eingefügten Tag entfernen, um die Funktion auszuschalten; ausdrückliche Aufrufe von Rekomi.convert() funktionieren weiterhin.

Antwort:

{
  "snippet": "<!-- Rekomi tracking -->\n<script async src=\"https://api.rekomi.com/api/v1/r/loader.js\" data-program-id=\"01234567-...\" data-rkmi-email-capture=\"auto\" data-rkmi-params=\"my_aff,brand_ref\"></script>\n<noscript>\n  <img src=\"https://api.rekomi.com/api/v1/r/c.gif\" width=\"1\" height=\"1\" alt=\"\" />\n</noscript>",
  "programId": "01234567-...",
  "programSlug": "default",
  "withPixel": false,
  "hasCustomAttributionParams": true,
  "customAttributionParams": "my_aff,brand_ref",
  "scriptHost": "https://api.rekomi.com",
  "usingTrackingDomain": false,
  "emailCapture": true
}

emailCapture in der Antwort sagt, ob das erzeugte Tag das Attribut trägt, damit ein Aufrufer die Zeile oben neben dem Tag ausgeben kann, ohne das HTML zu parsen. scriptHost ist der Host, von dem das Tag lädt (die bestätigte First-Party-Tracking-Domain der Marke, wenn eine aktiv ist, sonst der API-Host), und usingTrackingDomain sagt, welcher von beiden.

Fehler:

HTTPerrorUrsache
404no_program_foundKeine passende Kampagne in der aufrufenden Organisation (oder es gibt noch keine aktiven Kampagnen)
409pixel_not_provisionedwithPixel=true, aber die Organisation hat keinen öffentlichen Pixel-Schlüssel
403forbiddenDem API-Schlüssel fehlt der nötige Scope; der Antwortinhalt enthält eine Meldung, die den Scope nennt

Von Grund auf sicher gegen XSS: Jedes Feld im Snippet stammt vom Server (die Kampagnen-ID ist eine Guid, eigene Parameter werden über AttributionParamsService.ParseCustom erneut gegen [a-z0-9_-]{2,32} geprüft, der öffentliche Pixel-Schlüssel kommt aus der Datenbank). Keine Eingabe des Aufrufers landet in der HTML-Ausgabe.

Lead-Erfassung (Browser)

POST /api/v1/r/lead/signup

Der öffentliche Browser-Endpunkt hinter Rekomi.convert() / Rekomi.identify(). Erfasst einen Lead (eine kostenlose, identifizierte Anmeldung) zur aktuellen Empfehlung, gesendet vom Tracking-Loader per sendBeacon / fetch keepalive. CORS für öffentliches Tracking, Rate-Limit pro IP. Damit sich Anmeldungen nicht ausforschen lassen, liefert er immer ein neutrales 200, egal ob ein Lead erfasst wurde.

Anfrageinhalt:

{ "slug": "jane-recommends", "email": "user@example.com", "name": "Jane Doe", "programId": "0789abcd-..." }

slug (der aufgelöste Empfehlungs-Slug) ist Pflicht; programId beschränkt den Lead auf eine Kampagne; name ist optional. Der Rekomi-Loader (Rekomi.convert) ermittelt die Empfehlung aus dem Rekomi-Cookie des Besuchers und schickt den aufgelösten slug (und programId) für dich im Anfrageinhalt mit. Diesen Endpunkt rufst du nicht direkt auf; nutze das Rekomi.convert({ email }) des Loaders. Bei Stripe und Paddle werden Leads ganz ohne Aufruf automatisch erfasst. Siehe Leads und Anmeldungen tracken.

Der gehostete MCP-Server bietet das entsprechende Tool record_lead (affiliateSlug, email, optional name, campaignId); siehe MCP.

Attributionsparameter (direkter Umzug)

Diese Endpunkte legen fest, welche Query-Parameter der Rekomi-Loader für die Attribution von Affiliates erkennt. Die 21 kanonischen Standardparameter (passend zu Rewardful, Affonso, Tapfiliate, FirstPromoter, UpPromote, GoAffPro, BixGrow und anderen großen Affiliate-Plattformen) werden immer erkannt; Marken können bis zu 10 eigene Parameter für Nischenplattformen hinzufügen.

GET /api/v1/settings/attribution-params/defaults

Öffentlich, ohne Authentifizierung. Liefert die Liste der kanonischen Standardparameter plus die Validierungsgrenzen (höchste Zahl eigener Einträge, höchste Gesamtzahl an Zeichen, Längengrenzen pro Parameter). Genutzt von der Oberfläche des Dashboards und der Marketing-Website, um das Raster der Chips darzustellen, ohne den Katalog fest einzuprogrammieren.

{
  "defaults": ["via", "ref", "fpr", "aff", "afmc", "rfsn", "sca_ref", "bg_ref", "a_aid", "atp", "affonso", "awc", "sscid", "cjevent", "irclickid", "partner_key", "affid", "affiliate_id", "affcode", "idev", "mbsy"],
  "maxCustomEntries": 10,
  "maxTotalChars": 256,
  "minParamLength": 2,
  "maxParamLength": 32
}

Rate-Limit über die Richtlinie anonymous-tracking.

GET /api/v1/settings/attribution-params

Scope: read. Liefert die aktuelle Konfiguration der aufrufenden Organisation plus die zusammengeführte, tatsächlich geltende Rangfolge.

{
  "custom": "myparam,brand_ref",
  "defaults": ["via", "ref", "fpr", "aff", "..."],
  "effectiveOrder": ["myparam", "brand_ref", "via", "ref", "..."],
  "maxCustomEntries": 10,
  "maxTotalChars": 256
}

custom ist null, wenn die Marke keine eigenen Parameter eingerichtet hat; dann entspricht effectiveOrder genau defaults. Eigene Einträge werden beim Lesen gegen die Standardparameter dedupliziert, damit derselbe Parameter in effectiveOrder nie zweimal vorkommt.

PATCH /api/v1/settings/attribution-params

Scope: read_write. Rolle: Owner (bei der Authentifizierung api_key gemäß dem üblichen Verhalten der Rollenattribute umgangen). Bestätigung der E-Mail-Adresse: erforderlich für Aufrufer per JWT (bei api_key umgangen). Im Audit-Log als org.attribution_params_changed mit Vorher und Nachher festgehalten.

Anfrageinhalt:

{ "custom": "myparam,brand_ref" }

Übergib null oder "", um die eigenen Parameter zu löschen und zu den reinen Standardparametern zurückzukehren.

Fehler:

HTTPerrorUrsache
400attribution_params_too_longAnfrageinhalt länger als 256 Zeichen
400too_many_attribution_paramsMehr als 10 Einträge
400invalid_attribution_paramEin Eintrag passt nicht zu [a-z0-9_-]{2,32} (die Antwort nennt die abgelehnten Einträge in einem Array invalid)
403(leer)Weder Rolle Owner noch API-Schlüssel
404(leer)Organisation nicht gefunden

Jede erfolgreiche Änderung erzeugt genau eine Zeile im Audit-Log mit dem vorherigen und dem neuen Wert. Der Loader übernimmt die neue Konfiguration beim nächsten Seitenaufruf auf der Website der Marke (ohne Verzögerung durch einen Cache über das Cache-Control von 1 Stunde der Loader-Antwort hinaus).

Wie es weitergeht

  • Authentifizierung: Schlüssel erzeugen und verwalten.
  • Webhooks: Ereignisse empfangen, Signaturen prüfen, Wiederholungen behandeln.
  • S2S-Tracking: Conversions ohne Stripe übermitteln.
  • Leads und Anmeldungen tracken: kostenlose Anmeldungen erfassen und einen späteren Verkauf per E-Mail zuordnen.
  • Embed: das Affiliate-Dashboard auf deiner eigenen Domain bereitstellen.
  • MCP-Server: Integration für KI-Assistenten mit 61 Tools, darunter ein Installationsassistent.
  • Zapier: Automatisierungen ohne Code mit über 8,000 Apps. 9 Trigger, 9 Aktionen, 4 Suchen.
  • Conversion-Währung: Positivliste nach ISO 4217, Aufbau der Fehler, Verhalten bei Wechselkursen.

On this page

Kampagnen (Programs)GET /api/v1/programsGET /api/v1/programs/{id}GET /api/v1/programs/statsPOST /api/v1/programsPATCH /api/v1/programs/{id}DELETE /api/v1/programs/{id}GET /api/v1/programs/{id}/tracking-statusGET /api/v1/programs/{id}/recent-eventsGET /api/v1/programs/check-slugPATCH /api/v1/programs/{id}/slugGET /api/v1/programs/{id}/tracking-status/lookupPOST /api/v1/programs/{id}/custom-domain/verifyPOST /api/v1/programs/{id}/test-eventAffiliatesGET /api/v1/affiliatesGET /api/v1/affiliates/statsGET /api/v1/affiliates/{id}GET /api/v1/affiliates/applicationsGET /api/v1/affiliates/{id}/tax-formPOST /api/v1/affiliatesPATCH /api/v1/affiliates/{id}POST /api/v1/affiliates/{id}/approvePOST /api/v1/affiliates/{id}/rejectPOST /api/v1/affiliates/bulk-actionPOST /api/affiliates/bulk/approve und POST /api/affiliates/bulk/pauseGET /api/v1/affiliates/export.csvGET /api/v1/affiliates/{id}/commission-summaryGET /api/v1/affiliates/{id}/commissionsGET /api/v1/affiliates/{id}/crm-syncPOST /api/v1/affiliates/{id}/crm-resyncPOST /api/v1/affiliates/{id}/remind-payout-setupGET /api/v1/affiliates/{id}/claim-reminder/contextPOST /api/v1/affiliates/{id}/claim-reminderPOST /api/v1/affiliates/claim-reminder/bulkConversionsGET /api/v1/conversionsGET /api/v1/conversions/{id}GET /api/v1/conversions/export.csvPOST /api/v1/conversionsGET /api/v1/conversions/kind-countsBetrugsprüfung und Budget für Klicks und Leads (CPC/CPL)POST /api/v1/conversions/{id}/validatePOST /api/v1/conversions/{id}/invalidateGET /api/v1/programs/{id}/budget-statusGET /api/v1/fraud-eventsGET /api/v1/fraud-events/summaryGET /api/v1/uncredited-conversionsPOST /api/v1/lead (bezahlter Lead in einer Kampagne, die pro Lead zahlt, CPL)Import (Sammelumzug)POST /api/v1/affiliates/importPOST /api/v1/conversions/import/previewPOST /api/v1/conversions/importAuszahlungenGET /api/v1/payoutsGET /api/v1/payouts/{id}POST /api/v1/payouts/previewPOST /api/v1/payouts/runPOST /api/v1/payouts/{id}/retryGET /api/v1/payouts/automatic/previewGET /api/v1/payouts/ledgerGET /api/v1/payouts/receiptsAuszahlungsaktionen nur im DashboardAudit-LogGET /api/v1/audit-logGET /api/v1/audit-log/export.csvDashboard-KennzahlenGET /api/v1/dashboard/summaryGET /api/v1/dashboard/timeseriesGET /api/v1/dashboard/leaderboardGET /api/v1/dashboard/traffic-sourcesGET /api/v1/dashboard/export.csvAuswertungen (Analytics)Embed (White-Label-iframe)POST /api/v1/embed/tokensPOST /api/v1/embed/tokens/{id}/revokeGET /api/embed/public/dashboard?token=...GET /api/v1/embed/dashboardS2S-TrackingPOST /api/tracking/s2sPOST /api/tracking/leadÖffentliche Endpunkte (ohne Authentifizierung)GET /api/p/by-handle/{handle}/{campaign?}GET /api/p/by-domain?host={host}POST /api/p/id/{programId}/applyHeader-ReferenzEinstellungenGET /api/v1/settings/home-currencyPUT /api/v1/settings/home-currencyCRM-IntegrationenGET /api/v1/integrations/crmSub-Affiliate-RecruitingGET /api/v1/programs/{id}/sub-affiliate-configPUT /api/v1/programs/{id}/sub-affiliate-configGET /api/v1/me/sub-affiliate/link?programId={id}GET /api/v1/me/sub-affiliate/recruitsGET /api/v1/affiliates/{id}/sub-affiliatesErgänzung im BewerbungsformularOrganisationsprofilGET /api/organizations/meAbrechnungsstatusGET /api/billing/stateAffiliate-EinladungenPOST /api/affiliate-invitesPOST /api/affiliate-invites/bulkLebenszyklusDarstellung des Installations-SnippetsGET /api/v1/r/install-snippetLead-Erfassung (Browser)POST /api/v1/r/lead/signupAttributionsparameter (direkter Umzug)GET /api/v1/settings/attribution-params/defaultsGET /api/v1/settings/attribution-paramsPATCH /api/v1/settings/attribution-paramsWie es weitergeht