MCP server
Connect AI assistants (Claude, Cursor, Continue, Zed, ChatGPT, Windsurf) to your Rekomi account through Model Context Protocol.
Rekomi runs a hosted MCP server at https://mcp.rekomi.com/api/mcp. Pick your AI client below, paste two values, restart. That's it.
Claude Desktop
Paste two lines into claude_desktop_config.json. Restart.
Claude Code
One claude mcp add command. CLI persists the config.
ChatGPT
Settings → Connectors → Add custom connector. UI only.
Cursor
Add to ~/.cursor/mcp.json. Same JSON shape as Claude.
Windsurf
Cascade panel → MCP servers → Add. Streamable-HTTP.

Continue
Add to ~/.continue/config.json. Restart the extension.
Zed
Add to ~/.config/zed/settings.json under context_servers.
Generate a connection token
Every connection needs an rk_mcp_* token. It's the only credential your AI client uses.
- Sign in to app.rekomi.com.
- Open Settings → AI assistant.
- Click Generate connection token, give it a name (e.g. Claude Desktop laptop), and copy the
rk_mcp_*value. It is shown exactly once.
Owner-only to create. Up to 10 active per organization. Revoke at any time; the change propagates within ~10 seconds. Free and Trial plans included.
Connect Claude Desktop
Edit your claude_desktop_config.json:
{
"mcpServers": {
"rekomi": {
"url": "https://mcp.rekomi.com/api/mcp",
"headers": {
"Authorization": "Bearer rk_mcp_YOUR_TOKEN_HERE"
}
}
}
}Restart Claude Desktop. Ask it "what's in my Rekomi account?" to confirm.
Connect Claude Code
claude mcp add --transport http rekomi https://mcp.rekomi.com/api/mcp \
--header "Authorization: Bearer rk_mcp_YOUR_TOKEN_HERE"The URL must come before --header. The --header flag is variadic, so any positional argument after it gets eaten as another header. If you see error: missing required argument 'commandOrUrl', that's the cause.
The CLI persists the config to ~/.claude.json and subsequent sessions auto-connect.
Connect ChatGPT
ChatGPT supports MCP via the Connectors UI on chatgpt.com (Plus, Pro, Team, Enterprise — 2026 Apps SDK rollout). There's no JSON config to paste; the setup is in the UI.
- Open chatgpt.com → profile menu (bottom-left) → Settings → Connectors → Add custom connector.
- Name:
Rekomi. - MCP server URL:
https://mcp.rekomi.com/api/mcp - Authentication: Custom header.
- Header name:
Authorization - Header value:
Bearer rk_mcp_YOUR_TOKEN_HERE
- Header name:
- Save. Start a new chat, enable the Rekomi connector, ask "list my pending Rekomi affiliates".
Connect Cursor
Add to ~/.cursor/mcp.json (global) or .cursor/mcp.json in your project root:
{
"mcpServers": {
"rekomi": {
"url": "https://mcp.rekomi.com/api/mcp",
"headers": {
"Authorization": "Bearer rk_mcp_YOUR_TOKEN_HERE"
}
}
}
}Connect Windsurf
Open the Cascade panel and click the hammer/plug icon → MCP servers → Add server. Pick Streamable-HTTP transport and paste:
{
"mcpServers": {
"rekomi": {
"serverUrl": "https://mcp.rekomi.com/api/mcp",
"headers": {
"Authorization": "Bearer rk_mcp_YOUR_TOKEN_HERE"
}
}
}
}Restart Windsurf. The Rekomi tools appear under Available tools in the Cascade panel.
Connect Continue
Add to ~/.continue/config.json under experimental.modelContextProtocolServers, then restart the Continue extension:
{
"experimental": {
"modelContextProtocolServers": [
{
"transport": {
"type": "streamable-http",
"url": "https://mcp.rekomi.com/api/mcp"
},
"headers": {
"Authorization": "Bearer rk_mcp_YOUR_TOKEN_HERE"
}
}
]
}
}Connect Zed
Add to ~/.config/zed/settings.json under context_servers:
{
"context_servers": {
"rekomi": {
"source": "custom",
"command": null,
"settings": {
"url": "https://mcp.rekomi.com/api/mcp",
"headers": {
"Authorization": "Bearer rk_mcp_YOUR_TOKEN_HERE"
}
}
}
}
}How it works
AI assistants speak MCP to read your affiliate program, approve applications, draft outreach, mint embed tokens, and more, all on behalf of the human asking. Every tool call lands in your audit log with actor_type = mcp. MCP works on every plan, including Trial and Free; feature-tier gates (e.g. embed tokens are Growth+) still apply regardless of how the call arrives.
Machine-readable docs for LLM-driven IDEs and agents: https://rekomi.com/llms/mcp.txt.
AI client ──Bearer rk_mcp_xxx──► mcp.rekomi.com/api/mcp
│
│ Forwards + co-signs:
│ Authorization: Bearer rk_mcp_xxx
│ X-Rekomi-MCP-Identity: t=<unix>,v1=<hmac>
▼
api.rekomi.com/api/v1/*
│
│ Validates the HMAC, validates the token,
│ checks role + scope, runs the action,
│ logs to audit log with actor_type=mcp.
▼
Your dataThe token alone is useless. The backend rejects any direct call to api.rekomi.com with an rk_mcp_* token (returns mcp_relay_required). The trusted relay at mcp.rekomi.com is the only path that produces the HMAC the backend requires.
Transport is MCP Streamable-HTTP in stateless mode: each tool call is a self-contained JSON-RPC request/response over HTTPS. No long-lived sessions, no SSE upgrade. A GET https://mcp.rekomi.com/api/mcp returns a small JSON identity blob you can use as a health check.
Available tools
There are 39 tools (23 reads and 16 safe writes). Each tool maps 1:1 to one Rekomi V1 API endpoint (or, for the install-recipe tools, to an MCP-local content registry). Composition (e.g. "approve every pending affiliate") happens in your AI client's reasoning loop, not inside a tool, so every individual action is surfaced to you for approval and lands in the audit log on its own row.
| Tool | Inputs | What it does |
|---|---|---|
list_affiliates | cursor?, take? (1–200, default 50), campaignId? | List affiliates across your account. Cursor-paginated. Filter by campaign. |
get_affiliate | id | Fetch one affiliate's full detail (totals, recent conversions, notes). |
list_conversions | cursor?, take?, affiliateId?, campaignId?, status? (Pending/Approved/Paid/Refunded/Denied), since? (ISO8601) | List sales. Cursor-paginated. |
record_lead | affiliateSlug, email, name?, campaignId? | Record a free signup as a lead (an email tied to the referral, before payment), so a later sale is still credited even if the click cookie is gone. For Stripe and Paddle, leads are captured automatically; use this tool for other processors or free-account signups. See Track leads and signups. |
list_campaigns | cursor?, take? | List referral campaigns with commission/payout rules and affiliate count. |
get_campaign | id | Fetch one campaign with commission/payout rules and recent stats. |
list_payouts | cursor?, take?, status? (Pending/Processing/Paid/Failed), affiliateId?, since? | List payout batches. |
get_payout | id | Fetch one payout batch's per-affiliate line items. |
dashboard_summary | range? (7d/30d/90d/ytd/all, default 30d) | At-a-glance KPIs (revenue, conversions, leads, active affiliates, pending approvals). |
dashboard_timeseries | metric (revenue/conversions/leads/clicks), range? (7d/30d/90d/ytd, default 30d) | Daily timeseries for charting (revenue, conversions, leads, or clicks). ≤ ~365 points. |
list_audit_log | page? (1-based, default 1), pageSize? (max 100, default 50), action?, entityType?, since?, until? | Audit entries. Offset-paginated (not cursor). Returns actorType (user/api_key/mcp/system/admin), action, entityType, entityId, before, after, createdAt. |
approve_affiliate | id | Approve a pending application. Sends the magic-link welcome email. |
reject_affiliate | id, reason? (max 1000 chars), notify? (default true) | Reject a pending application. Optionally notifies the applicant. |
preview_payout | campaignId?, since? | Compute a payout batch's per-affiliate breakdown without running it. Safe to call repeatedly. |
mint_embed_token | affiliateId, expiresInHours? (1–720, default 24), allowedOrigins? (string or array of URLs), rotate? | Issue a short-lived affiliate-dashboard embed token. Growth+ only; Starter/Trial receive plan_tier_required (402). |
get_home_currency | (no inputs) | Read the org's HomeCurrency setting plus the supported ISO 4217 allowlist + whether the caller's plan permits changing it. |
set_home_currency | homeCurrency (3-letter ISO 4217) | Change the org's HomeCurrency. Pro only; lower tiers receive plan_tier_required (402). Idempotent on identical before/after; audit-logged. Historical conversions keep their native currency. |
get_program_sub_affiliate_config | programId | Read a campaign's sub-affiliate (single-tier override) settings: { enabled, percent }. |
set_program_sub_affiliate_config | programId, enabled, percent (0–100) | Enable / disable sub-affiliate recruiting on a campaign and set the override percent. Pro only; lower tiers receive plan_tier_required (402). Owner role + verified email required. Audit-logged. |
list_affiliate_sub_affiliates | affiliateId | Brand-side drilldown: list the sub-affiliates recruited by a specific affiliate, with each recruit's status, total commission generated, and override earned. Flat array (no pagination). |
list_campaign_coupons | programId, cursor?, take? | List all coupon codes assigned at the campaign level. |
create_campaign_coupon | programId, name, percentOff?, amountOffCents?, currency?, duration?, durationInMonths?, maxRedemptions?, expiresAt?, metadata? | Create a campaign-level coupon code. |
list_affiliate_coupons | affiliateId, cursor?, take? | List per-affiliate coupon codes for an affiliate. |
list_my_coupons | cursor?, take? | Affiliate-side: list the calling affiliate's self-mintable campaigns and existing per-affiliate coupon codes. |
generate_affiliate_coupon | affiliateId, programId, vanity? | Mint a per-affiliate coupon code for an affiliate on a self-mint-enabled campaign. |
revoke_affiliate_coupon | couponId | Revoke (deactivate) a per-affiliate coupon code. |
get_org_profile | (no inputs) | Read the calling org's profile: name, slug, brand color, logo, description, plan tier, subscription status, trial end, payout grace days. Useful as a "who am I?" check at the start of any assistant conversation. |
get_billing_state | (no inputs) | Read the org's billing state. Returns planTier, subscriptionStatus, trialEndsAt, stripeCustomerLinked, stripeConnectLinked, plus a plan-limits snapshot. The stripeConnectLinked boolean is the deciding factor for install path (Stripe-webhook vs pixel vs S2S). |
list_install_platforms | (no inputs) | List every platform with a Rekomi install recipe (29 today). Returns platformKey, displayName, category, conversionFire, requiresStripeConnect, requiresApiKey. Use this to map the user's "I use Webflow" to a platform key. |
get_install_recipe | platformKey | Fetch the full structured install recipe for one platform: ordered steps, common gotchas, conversion-fire path, and the canonical docs URL. Unknown keys return a generic 3-path fallback recipe instead of failing. |
create_campaign | name, slug, description?, termsUrl?, defaultCommissionType? (Percentage/Fixed), defaultCommissionValue?, defaultCommissionModel? (Cps/Cpc/Cpl; legacy Cpa/RevShare/Hybrid still accepted), cookieWindowDays?, attributionModel?, autoApprove?, networkVisibility? | Create a new campaign (referral program). Backend enforces commission type/model pairing (RevShare requires Percentage type, Cpc/Cpl require Fixed). Audit-logged. |
create_affiliate_invite | campaignId, email, fullName?, slug?, personalNoteHtml?, customCommissionType?, customCommissionValue?, autoApprove? (default true) | Send a branded invite email to one affiliate for a campaign. Available on every plan including Trial. Personal note HTML is server-sanitized to a strict allowlist. MIGRATION: pass slug to preserve the affiliate's existing slug from a prior platform so their existing public referral URL keeps working. Returns 409 slug_already_taken on collision. |
bulk_invite_affiliates | campaignId, recipients[]? (preferred) OR emails[]? (legacy, 1-50), personalNoteHtml?, autoApprove? | Bulk-invite up to 50 affiliates at once. Requires Starter tier or higher with an active paid subscription (Trial users receive paid_plan_required HTTP 402). The recipients shape carries per-row email, slug?, fullName? so migration imports preserve every affiliate's existing slug. Response includes slugConflicts[] for rows where the requested slug was rejected; those still get an invite with a fresh random slug. |
import_affiliates | campaignId, affiliates[] (1-1000; per row email, fullName?, status?, paypalEmail?, slug?), includeStatuses?, notifyAffiliates?, notificationMessageHtml? | Bulk-import an affiliate roster from canonical JSON rows (platform-agnostic; you map any export into Rekomi fields). Dedupes by email within the campaign and the batch (idempotent). status defaults to approved. Starter+ tier for direct API-key callers (402 plan_tier_required); MCP-relayed calls bypass the plan gate. notifyAffiliates additionally needs Growth+ with an active subscription. Returns a revertible jobId + { imported, skipped, errored, reasons }. A write tool: confirm before running. |
preview_conversion_import | campaignId, conversions[] (1-1000; canonical rows) | Dry-run a historical conversion/commission import. Writes nothing. Returns { rowsTotal, wouldImport, wouldSkip, wouldError, skipReasons, paidBalance, unpaidBalance }. Always run this before import_conversions and show the user unpaidBalance (how much would become payable). |
import_conversions | campaignId, conversions[] (1-1000; per row affiliateEmail?/affiliateSlug? [one required], customerEmail?, customerName?, amountCents, commissionCents, currency, occurredAt, paid, type?, stripeCustomerId?, stripeSubscriptionId?, externalId?), confirmUnpaidPayout? (default false), excludePaid? | Import historical conversions/commissions. Idempotent (re-running never double-pays). Money safety: paid rows lock as settled history; unpaid rows become payable only when confirmUnpaidPayout=true (defaults false). Starter+ tier for direct API-key callers (402 plan_tier_required); MCP-relayed calls bypass the plan gate. A write tool that can create payable balances: always preview first, then confirm with the user before setting confirmUnpaidPayout=true. Returns a revertible jobId + unpaidConfirmedCents. |
get_attribution_params | (no inputs) | Read the org's custom attribution params + the 16 canonical defaults + the effective priority order the loader uses. Useful for confirming whether a migrating brand needs custom-param config or whether their source platform is already covered by the defaults. |
update_attribution_params | custom? (comma-separated string, max 10 entries) | Set the brand's custom attribution params. Each entry [a-z0-9_-]{2,32}, max 10 entries, max 256 chars total. Empty / omit clears the override. Owner-level write (audit-logged). |
get_install_snippet | campaignId?, withPixel? | Get the ready-to-paste install snippet for the brand with program id + custom attribution params + optional pixel public key already embedded. Single source of truth for snippet generation; agent hands the response verbatim to the user. Defaults to the newest active campaign + click-tracking-only (no pixel). Returns 409 when withPixel=true and the org hasn't provisioned a pixel public key. |
Cursor-paginated list responses always look like { "items": [...], "nextCursor": "…", "hasMore": true }. list_audit_log returns { "page", "pageSize", "total", "items" } instead.
The assistant calls each tool individually and surfaces its result back to you in chat. Destructive or money-affecting tools (approve_affiliate, reject_affiliate, import_affiliates, import_conversions) should be confirmed by the user before the assistant runs them. For a conversion import, always run preview_conversion_import first and review the unpaid balance before authorizing payment with confirmUnpaidPayout.
Install Rekomi with the assistant
The most common use of the MCP server is what it sounds like: "help me install Rekomi on my site." A typical session looks like this.
- You start a chat with Claude (or Cursor, or any MCP-capable client) and say "Help me install Rekomi on my Shopify store."
- The assistant calls
get_org_profileto confirm who you are and what plan you're on. - It calls
get_billing_stateto see whether you've connected Stripe yet. IfstripeConnectLinkedis true, it recommends the stripe-webhook path; if false, it nudges you to coupon codes or browser-pixel. - It calls
list_install_platformsand matches "Shopify" toshopify. - It calls
get_install_recipewithplatformKey: shopifyand walks you through the steps in chat. It surfaces the gotchas inline (Shopify Payments hides your Stripe; Custom Pixel sandbox cannot load external scripts; Shopify Collabs is a separate product). - If you haven't created a campaign yet, the assistant offers to call
create_campaignand asks you to confirm the name, slug, commission type, and value before doing so. - If you're migrating from Rewardful / Tapfiliate / FirstPromoter / PartnerStack / Dub Partners, paste your existing affiliate emails into the chat. The assistant uses
bulk_invite_affiliatesto send branded onboarding emails to all of them at once. (Requires Starter tier or higher with an active paid subscription.)
What the assistant cannot do (security)
For the install flow to be safe, the MCP server intentionally does not expose certain operations as tools:
- The assistant cannot mint API keys. If your install needs a
rk_live_*bearer +rks_*signing secret (Gumroad / Rails / Django / any S2S path), the assistant will deep-link you to Settings → API keys to mint one yourself, then ask you to paste the key back. The MCP token cannot escalate into a long-lived API key that outlives the MCP token itself. - The assistant cannot create new MCP tokens. Token minting is owner-only and JWT-only.
- The assistant cannot change billing or open Stripe Checkout sessions. Billing decisions stay with the human owner; the assistant can only read billing state via
get_billing_state.
If a tool seems missing, that's the reason. The capability is intentionally not exposed.
Plan-gate semantics
There are two different plan gates in the Rekomi backend:
- Public-API gate (
[RequiresPlanForApiKey]): blocks Starter/Trial from calling/api/v1/*with a directrk_live_*key. MCP-relayed traffic bypasses this — that's why MCP works on every plan. - Feature-tier gate (
[RequiresPlan]): blocks Starter/Trial from a specific feature regardless of how it's called. MCP does not bypass this. Today this applies tomint_embed_token(Growth+) and a handful of other surfaces; a Starter user callingmint_embed_tokenover MCP getsplan_tier_required(HTTP 402).
If a tool returns plan_tier_required, the answer is to upgrade the plan, not to change tokens.
Security model
- Token shape:
rk_mcp_*, 256-bit random body, base64url-encoded. Stored as SHA-256 hash; plaintext never persisted. - One-time reveal: the full token is shown only at creation or rotation. Rotate to invalidate the old value.
- Owner-only minting: only an organization owner can create, rotate, or revoke MCP tokens.
- Per-org cap: 10 active tokens per organization.
- Read+write scope: every MCP token is granted the
read_writescope; the per-tool user-consent loop in your AI client is the user-facing approval gate. - Direct use blocked: a stolen
rk_mcp_*is useless without the trusted relay. Direct hits toapi.rekomi.comreturnmcp_relay_required. - Relay binding: the relay HMAC is computed over
{t}.{METHOD}.{path}.{body_sha256_hex}with a shared secret. ±300 s timestamp window, constant-time compare. Replays, body tampering, and method/path swaps all fail validation. - Revoke propagation: revoked tokens stop authenticating within ~10 s across all instances (bounded by the auth cache TTL).
- Audit trail: every tool call lands in your audit log with
actor_type = mcp, the calling token id, action, entity, and timestamps. Filter byactor_type = mcpunder Settings → Audit Log. - Suspension gate: a suspended account stops every MCP call within seconds.
- PII scrubbing: tokens and the relay-identity header are scrubbed from MCP-server-side error reporting before they leave the host.
Best practices
- Use a separate token per device or per AI client. If a laptop is lost, revoke just that token.
- Set up read-only assistant agents by instructing the assistant not to call write tools (the sixteen writes are
approve_affiliate,reject_affiliate,preview_payout,mint_embed_token,set_home_currency,set_program_sub_affiliate_config,create_campaign_coupon,generate_affiliate_coupon,revoke_affiliate_coupon,create_campaign,create_affiliate_invite,bulk_invite_affiliates,update_attribution_params,import_affiliates,import_conversions,record_lead). The audit log still records the actor as the assistant. - Watch your audit log under Settings → Audit Log during the first few days. Filter by
actor_type = mcpto see exactly what the assistant has done. - Don't paste the same token into multiple clients you can't independently revoke later — one token per surface keeps blast radius bounded.
Troubleshooting
mcp_relay_requirederror — your AI client is hittingapi.rekomi.comdirectly with an MCP token. Point it athttps://mcp.rekomi.com/api/mcpinstead.plan_tier_required(HTTP 402) on a specific tool — the tool is feature-gated to a higher plan (e.g.mint_embed_tokenis Growth+). Upgrade the plan or switch to a tool that's available on your tier. Switching tokens will not help — feature gates apply to MCP traffic too.unauthorized(HTTP 401) on every call — the token is wrong, revoked, or rotated. Mint a fresh one under Settings → AI assistant.- Tools list works but every tool returns 401 — the backend cannot validate the relay header. Most often a stale relay secret on one side; contact support.
- Claude Code still sends the old token after I rotated — Claude Code stores the bearer header statically in
~/.claude.json. Rotating the token in the Rekomi dashboard does not propagate. Runclaude mcp remove rekomiand thenclaude mcp add …again with the new token. - Claude Code logs
SDK auth failed: JSON Parse error— harmless. Some MCP clients probe/.well-known/oauth-protected-resource; the server returns a clean JSON 404 and continues with bearer auth. /mcp"Re-authenticate" doesn't do anything — that menu item handles OAuth-flow servers; Rekomi uses a static bearer header. Useclaude mcp remove+claude mcp addto swap credentials.
See also
- Public API reference
- Audit log
- Machine-readable manifest: rekomi.com/llms/mcp.txt