Reference
MCP server
A streamable-HTTP MCP server exposing 16 tools for program discovery, link minting, and earnings attribution. Server name affiliateos; every request is scoped to the authenticated user.
Connecting
From Claude Code or the Claude apps:
claude mcp add --transport http affiliateos https://api.affiliateos.dev/mcpThe endpoint is https://api.affiliateos.dev/mcp. Any MCP client that speaks streamable HTTP works; the transport is stateless and each request gets a fresh server instance bound to your user.
Authentication
The server accepts two kinds of Bearer credentials:
| Method | How it works |
|---|---|
| OAuth (default) | An unauthenticated request gets a 401 pointing at the protected-resource metadata. Claude registers a client, opens a sign-in popup, and completes an OAuth 2.1 authorization-code flow with PKCE. Access tokens last an hour and refresh automatically. |
| API key | Pass a dashboard-issued key as Authorization: Bearer aff_live_.... Useful for MCP clients without an OAuth flow. |
The full flow (RFC 9728 resource metadata, RFC 8414 server metadata, RFC 7591 dynamic registration) is documented in the repo under docs/oauth.md. Rate limit: 120 requests per minute per key or user, with Retry-After on 429.
Response conventions
Tool responses are built for context windows. Three rules hold everywhere:
| Rule | Detail |
|---|---|
| Compact by default | Responses stay under roughly 2,000 tokens. List tools return summaries plus a next_cursor; detail tools accept a fields parameter to slim the payload. |
| Structured errors | Failures return {error, message, hint} rather than throwing. The hint suggests the next tool to try, for example resolve_url after MONETIZATION_NOT_FOUND. |
| Zod-validated input | Every parameter is validated; invalid input returns a clear error instead of a crash. |
Tools: discovery
list_connected_networks
Lists the network accounts connected to your user with status and last sync. Call it first in a session to learn what is available.
Returns: accounts[] with account_id, network, label, status, last_sync, plus implemented_networks
search_programs
Hybrid semantic and filter search over affiliate programs across all connected accounts. Use it to decide what to promote for a topic; use resolve_url instead when you already have a URL.
| Parameter | Type | Notes |
|---|---|---|
query | string | topic, niche, or merchant name |
network | enum? | awin | impact | cj | rakuten | amazon |
vertical | enum? | one of the 17 verticals, e.g. sports_outdoors |
min_commission_pct | number? | minimum commission rate |
joined_only | boolean = true | only programs you are approved for |
limit | int 1-25 = 10 |
Returns: programs[] with program_id, network, name, vertical, relationship_status, commission, domains, score
get_program
Full detail for one program by program_id. Use before create_link when you need the cookie window or deep-link support. Pass fields to slim the response.
| Parameter | Type | Notes |
|---|---|---|
program_id | uuid | from search_programs |
fields | string[]? | subset of fields to return |
Returns: program detail including commission_summary, cookie_window_days, deep_link_supported
resolve_url
Answers "can this URL be monetized?" by matching a product or merchant URL against your programs by domain, ranked by commission.
| Parameter | Type | Notes |
|---|---|---|
url | url | product or merchant page |
Returns: matches[] with program_id, network, relationship_status, commission, deep_link_supported
search_products
Searches live product catalogs on networks that support it (Amazon Creators API in v1). Returns products with prices and URLs ready for create_link.
| Parameter | Type | Notes |
|---|---|---|
query | string | |
network | enum? | |
max_price | number? | filtered client-side |
limit | int 1-25 = 10 |
Returns: products[] with network, title, price, currency, url and a create_link hint
get_sync_status
Per-account sync health: recent runs per kind, errors, and cadence. Use when data looks stale or right after connecting an account.
Returns: runs[] plus cadence (programs every 12h, transactions hourly, payments daily)
connect_network_instructions
Returns the credential field spec for a network, where to find each value, and the dashboard URL where the human enters them. Agents never handle secrets; this hands off to the user.
| Parameter | Type | Notes |
|---|---|---|
network | enum | awin | impact | cj | rakuten | amazon |
Returns: credential_fields[], connect_url, implemented flag
Tools: links & attribution
create_attribution_tag
Creates an attribution tag (idempotent; returns the existing tag on conflict). Usually unnecessary because create_link auto-creates tags.
| Parameter | Type | Notes |
|---|---|---|
tag | slug | lowercase letters, digits, - and _, max 64 chars |
description | string? | |
metadata | object? | freeform, e.g. {channel:"tiktok"} |
Returns: tag_id, tag, description
list_attribution_tags
Lists your tags, optionally filtered by substring.
| Parameter | Type | Notes |
|---|---|---|
query | string? | substring filter |
Returns: tags[] with tag, description, created_at (max 50)
create_link
Mints a tracked affiliate deep link for a destination URL, tied to an attribution tag (auto-created if new). When program_id is omitted it resolves which account can monetize the URL by domain. This is the tool that turns a product URL into a paid link.
| Parameter | Type | Notes |
|---|---|---|
destination_url | url | |
attribution_tag | string | auto-created if new |
program_id | uuid? | skip domain resolution |
account_id | uuid? | pin a specific account |
Returns: tracking_url, network, program, attribution_tag, subid. MONETIZATION_NOT_FOUND if no program covers the domain
bulk_create_links
Mints up to 50 tracked links in one call under one attribution tag, with per-URL success or error so partial failures do not block the batch.
| Parameter | Type | Notes |
|---|---|---|
urls | url[] 1-50 | |
attribution_tag | string |
Returns: results[] per URL, plus succeeded and failed counts
Tools: earnings & events
get_transactions
Lists commission transactions (conversions) with filters, defaulting to the last 30 days. For aggregates use get_earnings_summary.
| Parameter | Type | Notes |
|---|---|---|
from / to | ISO date? | default: last 30 days |
status | enum? | pending | approved | reversed | paid |
network | enum? | |
attribution_tag | string? | |
program_id | uuid? | |
limit | int 1-100 = 25 | |
cursor | string? | next_cursor from a previous call |
Returns: transactions[] with commission, status, subid; next_cursor for pagination
get_earnings_summary
Aggregates earnings by tag, network, program, or day with currency conversion. This closes the loop: after minting links under a tag, call this to see what each tag earned. Defaults to the last 30 days, grouped by tag, in USD.
| Parameter | Type | Notes |
|---|---|---|
from / to | ISO date? | default: last 30 days |
group_by | enum = 'tag' | network | program | tag | day |
currency | string = 'USD' | 3-letter code, converted daily |
Returns: groups[] with pending, approved, reversed, paid, total_confirmed, transaction count
get_payments
Lists payout records from networks, meaning money actually paid to you. Use for reconciliation; per-commission status lives in get_transactions.
| Parameter | Type | Notes |
|---|---|---|
from / to | ISO date? |
Returns: payments[] with network, amount, paid_at (max 50)
check_events
Pull-based trigger feed covering commission, program, and sync events. Poll with the returned high_water_mark as after to only see new events. For push delivery use subscribe_webhook.
| Parameter | Type | Notes |
|---|---|---|
after | string? | event id or ISO timestamp high-water mark |
types | enum[]? | e.g. ["commission.approved"] |
limit | int 1-100 = 25 |
Returns: events[] plus high_water_mark for the next poll
subscribe_webhook
Registers an HTTPS endpoint to receive events as signed POSTs. The signing secret is returned once, so store it. Agents polling in-session should use check_events instead.
| Parameter | Type | Notes |
|---|---|---|
url | https url | |
event_types | enum[] min 1 | see the webhooks page for the full list |
Returns: endpoint_id, url, event_types, secret (shown once)
A full loop, end to end
> search_programs({ query: "trail running", vertical: "sports_outdoors" })
< { programs: [{ program_id: "1f2e...", network: "awin", name: "RunFast Gear",
commission: { type: "cps", rate_pct: 8 }, relationship_status: "approved" }] }
> create_link({ destination_url: "https://runfastgear.example/peak-x2",
attribution_tag: "yt-desc-2026-07" })
< { tracking_url: "https://www.awin1.com/cread.php?...&clickref=af_9k2...",
network: "awin", attribution_tag: "yt-desc-2026-07", subid: "af_9k2..." }
> get_earnings_summary({ group_by: "tag", window handled by from/to defaults })
< { groups: [{ key: "yt-desc-2026-07", pending: 96.20, approved: 412.86,
paid: 0, transactions: 14 }], currency: "USD" }