Affiliate commissions and payouts
Partners earn from paid advertising placements: 20% audience allocation and 20% advertiser referral, each of the auction price excluding VAT. Commission clears a 30-day reserve after the 48-hour click window closes; payouts start at €25. Registration and losing bids earn nothing.
The public integration guide explains campaign/sub-ID links, consent and signed callbacks. The implemented OpenAPI contract contains session endpoints under /api/affiliate and scoped OAuth endpoints under /v1/agent/affiliate. Private MCP adds get_affiliate_stats, get_affiliate_links, list_affiliate_payouts, request_affiliate_payout and configure_affiliate_postback. Affiliate-only grants require no bid spending mandate.
Link generation accepts destination=spotlight (default homepage) or advertiser (signup). Direct URLs carry affiliate_code and optional stable affiliate_campaign/affiliate_subid placement labels; legacy /r/{code} links remain compatible. GETs do not earn commission. Homepage referral consent is optional and advertiser signup has a visible optional referral field. Shared campaign labels do not replace deduplicated audience attribution; cookie-free URLs are not automatically exempt from consent requirements.
Campaign reports show recorded referral-page visits separately from qualified outbound clicks. Visits include reloads, earn no commission and are counted over 30 UTC calendar dates. Daily aggregates use no visitor cookies or identifiers; stable placement labels must not contain personal data. Earlier visits cannot be reconstructed. The API/MCP visit field is null for historical click-only campaigns. Excess daily placement labels are grouped as [other]; bounded cleanup removes visit aggregates after 90 days. House ads can generate visits while qualified paid clicks remain zero.
A paid LIVE payment saga creates the immutable rate/window record. D1 owns click deduplication, commission ledger, reversals, payout reservations and callback outbox. Cloudflare Workflows owns durable transfers and bank confirmation; cron performs bounded settlement/cleanup/delivery. A Stripe transfer is not a bank-paid payout. Hosted Connect onboarding collects identity and bank details.
Signed bank webhooks reconcile the current status with Stripe and wake the active payout workflow. A one-day event timeout keeps reconciliation working when a webhook goes missing. Late bank returns after workflow completion update the ledger, callback and durable email outbox. A bank retry explicitly selects the returned payout and reuses its original transfer. Maintenance recovers pending reservations throughout the month.
Core Stripe refund and credit-note events reconcile authoritative invoice revenue under a short D1 lease. Voided credit notes restore eligible rewards; already-paid rewards receive linked adjustments. Unresolved reconciliation holds affected rewards and retries through maintenance. Rates, original calculation bases and allocation evidence remain immutable.
The operator admin lists partners, commission states and Stripe readiness with bounded indexed paging. A suspended partner can read their ledger but cannot change campaigns, Connect or payout settings. Self-service data exports include the affiliate ledger and campaign totals without exposing callback signing secrets or visitor identifiers.
Source and operational decisions: docs/affiliate-system.md. Schema: 0057_affiliates.sql, 0058_affiliate_reconciliation.sql 0059_affiliate_payout_notices.sql and 0060_affiliate_visits.sql. Networks require their own admission and payload adapters; generic signed postbacks alone do not establish Awin/impact integration. Deployment and provider evidence are recorded separately in the dated validation reports.