White-label embed
Fetch an affiliate's earnings data with a per-affiliate token and render it inside your own product.
The embed feature lets a brand surface each affiliate's earnings inside their own product. You mint a per-affiliate token, fetch the affiliate's earnings data with it from your own page, and render the numbers in your UI. Gated to Growth and higher.
What it does
An affiliate logs into your product as usual. Your page fetches Rekomi's embed data endpoint with a per-affiliate token and renders the affiliate's earnings and recent conversions in your own interface. The affiliate never has to leave your domain or log in to Rekomi separately.
Read-only. The endpoint returns data; it does not let the affiliate take actions (no apply, no profile edit, no tax form upload). For those actions they still sign in to their Rekomi affiliate dashboard.
Generate a token
Token minting requires an account Owner or Admin (or an API key with read and write access) on a Growth or higher plan. Manager and Viewer members cannot mint or revoke tokens.
POST /api/v1/embed/tokens
Authorization: Bearer rk_live_xxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
{
"affiliateId": "b3d7e2a1-4c9f-4e8a-9f21-0a1b2c3d4e5f",
"allowedOrigins": "https://yourapp.com,https://app.yourapp.com",
"rotate": false,
"expiresInHours": 24
}Required:
affiliateId: the affiliate (a UUID) to scope this token to. Must be an affiliate in one of your programs.allowedOrigins: comma-separated list of origins (scheme + host + port) that can frame the embed. Strict equality match; no wildcards. Effectively required: the mint succeeds without it, but every data fetch returns 403no_allowed_origins_configureduntil you rotate the token with origins set.
Optional:
rotate(boolean, defaultfalse): whentrue, replaces any existing token for the (program, affiliate) pair with a fresh one. Whenfalseand a token already exists, the response returns the existing token's metadata (id, allowed origins, expires) withalreadyExists: trueand no plaintext token; you mustrotate: trueto mint a new plaintext.expiresInHours(integer, default24, clamped to[1, 720]): time-to-live in hours. Beyond 30 days (720 hours), rotate rather than extend.
Response (new or rotated token):
{
"token": "kY3xTq8Zr1Nf6Wb2Jm9Lp4Vc7Hd0Ag",
"allowedOrigins": "https://yourapp.com,https://app.yourapp.com",
"expiresAt": "2026-05-12T03:14:25.000Z"
}The token is an opaque 24-byte URL-safe random string with no prefix. The plaintext token is returned EXACTLY ONCE. Store it in your application server before passing to the browser; we cannot recover it. Hash-at-rest on our side; lookup on the public dashboard endpoint hashes the inbound token before compare.
Tokens default to 24 hours. Generate fresh tokens when the affiliate signs in. Do not pass long-lived tokens to the browser.
Fetch the data and render it
The dashboard endpoint returns JSON, not an HTML page. Fetch it from your own page (a cross-origin fetch sends the Origin header the allowlist checks) and render the earnings UI in your product, theming with the returned brand color.
const res = await fetch(
"https://api.rekomi.com/api/embed/public/dashboard?token=kY3xTq8Zr1Nf6Wb2Jm9Lp4Vc7Hd0Ag"
);
const data = await res.json();
// render data.earnedCents, data.recentConversions, etc. in your own UIThe response carries read-only earnings and recent conversions for the affiliate the token was issued to, plus your brand color and name so you can theme the panel (see Customization below). The Origin header your browser sends on the cross-origin fetch must exactly match one of the token's allowedOrigins, so call it from a page served on an allowlisted origin.
Origin allowlist
The embed enforces a strict origin check. The Origin header sent by the browser (on the cross-origin fetch) must exactly equal one of the entries in allowedOrigins. Comparison is:
- Scheme (https vs http)
- Host
- Port (explicit if non-default)
No StartsWith checks. No subdomain wildcards. Required because a StartsWith rule would allow https://yourapp.com.evil.example.com to bypass.
If you need multiple origins, add them all to the list. If you serve from many subdomains, generate per-deploy tokens that include only the relevant origin.
What the embed shows
The GET /api/embed/public/dashboard?token=... endpoint returns:
| Field | Type | Description |
|---|---|---|
affiliateName | string|null | Affiliate's full name (falls back to email if no name set) |
programName | string|null | The program the affiliate is in (within your org) |
earnedCents | integer | Commission earned (sum of commissionCents across Approved + Paid conversions) across the affiliate's recent conversions |
paidCents | integer | Subset of earnedCents that has been paid out |
pendingCents | integer | earnedCents - paidCents |
brandColor | string | Your org's brand color (falls back to #0E7C7B if unset). Use to theme the panel. |
brandName | string|null | Your org's display name. Surface in your panel header. |
recentConversions[] | array | Up to 20 most recent conversions, newest first. Each entry: { id, amountCents, commissionCents, status, createdAt }. |
The earnedCents, paidCents, and pendingCents totals are computed over the affiliate's 50 most recent conversions, not lifetime history, so they can understate totals for high-volume affiliates.
Realistic JSON response:
{
"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"
}
]
}That is the full payload. No tax forms, no Stripe Connect status, no settings, no IPs, no PII beyond what the affiliate has consented to display.
What the embed does NOT show
- Other organizations' programs (even if the affiliate is in multiple)
- Tax form status (PII)
- Stripe Connect bank details (PII)
- Other affiliates' data
The embed is strictly scoped to one (organization, affiliate) pair.
Customization
The dashboard response surfaces brandColor (from your organization's brand color setting at /dashboard/settings/branding, defaulting to #0E7C7B when unset) and brandName (your org's display name). Render your panel on your side using those values to match your product's look.
Text color, layout, and typography are not server-customizable; the endpoint returns data only. Future releases will add a customization JSON (hide/show columns, header text, custom CSS variables) on the token creation request.
Plan gate
POST /api/v1/embed/tokens is gated to Growth plan and higher. If your org is on Starter, the endpoint returns HTTP 402 with { error: "plan_tier_required", required: "Growth", current: "Starter" }.
Security checklist
- Always generate tokens server-side. Never expose the bearer API key to the browser.
- Always set
allowedOriginsto the exact production origin. - Generate fresh tokens per session (do not reuse a 24-hour token across many sessions).
- Verify the
Authorizationof your incoming user request before minting a token (only the actual logged-in affiliate should get a token for their own data). - Fetch the data endpoint only from pages served on an allowlisted origin, so the browser sends the
Originheader the allowlist checks.
Troubleshooting
origin_not_allowed: yourOriginheader does not match anything inallowedOrigins. Check scheme, host, port for exact equality. A malformedOriginvalue returnsorigin_invalidinstead.origin_required: the request did not send anOriginheader (e.g., curl, server-to-server). The dashboard endpoint requiresOrigineven whenallowedOriginsis set, so fetch it cross-origin from a browser page rather than server-side.token_expired(401): the token'sexpiresAtis in the past. Generate a fresh one on next session.- Token mint returns 404: the
affiliateIdis not an affiliate in your organization. It must reference an affiliate you own. - Empty response or CORS error: check your browser console. Confirm you are fetching from a page whose origin is on the token's
allowedOrigins, and that the token has not been revoked (an unknown or revoked token returns 401).