GetAff
Sign inList my program
API documentation

MCP server

Everything the partner-recruiting screens do, as tools an AI assistant can call — through the Model Context Protocol, with your API key.

Endpoint
https://www.getaff.org/api/mcp
Transport
Streamable HTTP · 2025-11-25

Endpoint

One URL, Streamable HTTP transport (MCP spec 2025-11-25; 2025-06-18, 2025-03-26 and 2024-11-05 clients are accepted). Stateless: every request carries its own key, answers are plain JSON, and there is no server-to-client stream.

Authentication and scopes

Send Authorization: Bearer gaff_live_… — the same keys, scopes and rate limits as the REST API. Discovery (initialize, tools/list) answers without a key so a client can show the tools; every tool call needs one. A tool whose scope the key lacks answers an error the assistant can read. Create a dedicated key for each assistant, with only the scopes it needs.

API keys

Connect a client

Store the key in an environment variable or the client secret store — never in a shared configuration file.

Claude Code

One command; the header is sent on every request.

shell
claude mcp add --transport http getaff https://www.getaff.org/api/mcp \
  --header "Authorization: Bearer $GETAFF_API_KEY"

Claude Desktop

Through the mcp-remote bridge, which adds the header. Edit claude_desktop_config.json (Settings › Developer), then restart Claude.

claude_desktop_config.json
{
  "mcpServers": {
    "getaff": {
      "command": "npx",
      "args": [
        "-y",
        "mcp-remote",
        "https://www.getaff.org/api/mcp",
        "--header",
        "Authorization:${GETAFF_AUTH}"
      ],
      "env": {
        "GETAFF_AUTH": "Bearer gaff_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
      }
    }
  }
}

claude.ai custom connectors

Custom connectors added in claude.ai sign in with OAuth, which GetAff does not offer yet (see below). Until then, use Claude Desktop with the configuration above, or Claude Code.

Cursor

In ~/.cursor/mcp.json (or .cursor/mcp.json in a project). The key is read from your environment.

~/.cursor/mcp.json
{
  "mcpServers": {
    "getaff": {
      "url": "https://www.getaff.org/api/mcp",
      "headers": {
        "Authorization": "Bearer ${env:GETAFF_API_KEY}"
      }
    }
  }
}

VS Code

In .vscode/mcp.json; VS Code asks for the key once and keeps it in its secret storage.

.vscode/mcp.json
{
  "inputs": [
    {
      "type": "promptString",
      "id": "getaff-key",
      "description": "GetAff API key",
      "password": true
    }
  ],
  "servers": {
    "getaff": {
      "type": "http",
      "url": "https://www.getaff.org/api/mcp",
      "headers": {
        "Authorization": "Bearer ${input:getaff-key}"
      }
    }
  }
}

ChatGPT and the OpenAI API

Connectors added in the ChatGPT app sign in with OAuth: not available until GetAff supports it. From the OpenAI API (Responses API or Agents SDK), declare GetAff as a remote MCP tool with the header — keep approvals on.

Python — OpenAI Responses API
from openai import OpenAI
import os

client = OpenAI()
response = client.responses.create(
    model="gpt-5",
    tools=[{
        "type": "mcp",
        "server_label": "getaff",
        "server_url": "https://www.getaff.org/api/mcp",
        "headers": {"Authorization": f"Bearer {os.environ['GETAFF_API_KEY']}"},
        "require_approval": "always",
    }],
    input="List my GetAff sites, then find wedding creators in France for the first one.",
)
print(response.output_text)

Any other client

Any client that speaks Streamable HTTP and can send a header works. The MCP SDKs (TypeScript, Python) take the header as an option.

Cost confirmation

Tools that spend provider credits — search_partners with includeLive, find_emails — never spend on the first call:

  1. The first call spends nothing. It answers the estimate (credits, provider, what is left today) and a confirmToken.
  2. The assistant shows the cost to the user and waits for an explicit yes.
  3. The same call, with the same arguments plus confirmToken, runs — spending at most the confirmed credits. The token is bound to the key, the tool and the arguments, and expires after 15 minutes; a retried confirmed call replays its first result instead of paying twice.

The same rule over REST

REST clients confirm with a number: without confirmCost at least equal to the estimate, the call answers 402 cost_confirmation_required with the estimate in error.details.estimate, and spends nothing.

JavaScript — REST
const base = 'https://www.getaff.org/api';
const headers = {
  Authorization: `Bearer ${process.env.GETAFF_API_KEY}`,
  'Content-Type': 'application/json',
  'Idempotency-Key': crypto.randomUUID(), // a retry never pays twice
};
const url = `${base}/v1/sites/${siteId}/partner-search`;

let res = await fetch(url, { method: 'POST', headers, body: JSON.stringify({ includeLive: true }) });
if (res.status === 402) {
  const { error } = await res.json(); // error.code === "cost_confirmation_required"
  const credits = error.details.estimate.credits;
  if (!(await askTheUser(`Spend about ${credits} credits?`))) return;
  headers['Idempotency-Key'] = crypto.randomUUID();
  res = await fetch(url, {
    method: 'POST',
    headers,
    body: JSON.stringify({ includeLive: true, confirmCost: credits }),
  });
}
const { data } = await res.json();

Tools

Each tool calls the same operation as the matching REST endpoint. Hints follow the MCP spec: clients use them to decide what to ask the user before running.

Tools
ToolWhat it doesScopesHints
list_sitesList the GetAff sites of the account, with the affiliate programme each one runs. Call this first: every partner-search tool takes a siteId.readread-only
get_siteOne site: its programme, the criteria the next search will use, shortlist size, the last search, and what a live search would cost today.readread-only
get_criteriaThe partner-search criteria of a site (keywords, niches, platforms, countries, languages, audience bands, recency), those GetAff derives from the site, and the generated TOPIC TERMS the ranking matches (with the user corrections). Use correct_topic_term to fix a term.readread-only
correct_topic_termCorrect one generated topic term of a site, as in the app "Terms used" panel: negative (off-topic, penalised), remove (no longer a match), restore (undo), dismiss (drop a suggestion). The next search ranks with it.readwritewrites
update_criteriaChange the partner-search criteria of a site. Each field given REPLACES the current one (send the full list you want); fields left out are kept. reset: true goes back to the derived criteria. Does not run a search: call search_partners next.readwritewrites
search_partnersRun ONE partner search for a site across every source: the GetAff creator index ranked for this programme, lookalikes of the shortlist, competitor findings and — with includeLive: true (Pro) — live creator-data providers. Returns merged, ranked creators with a fit score and per-source status. Free sources cost nothing. Live sources spend credits: the first call then returns only an estimate and a confirmToken; tell the user the cost, and only after they agree call again with the same arguments plus confirmToken. criteria changes apply to this run only.readsearchspend*writes · paid, confirmed · reaches outside services
get_latest_resultsThe ranked results of the last partner search of a site (kept 30 days), paginated, optionally filtered by minimum fit score or source. No new search is run.readread-only
explain_rankingWhy one creator of the latest search ranks where it does: rank, fit score (0-100) and its reasons (topic match, market, language, reach, recent activity, competitor evidence, similarity to the shortlist).readread-only
shortlist_listThe creators selected for a site ("Ma sélection"), with their prospect status.readread-only
shortlist_addAdd creators of the LATEST partner search to the site shortlist, by their key (e.g. "instagram:1784140"). Up to 50 per call; the shortlist holds 100.readwritewrites
shortlist_removeRemove creators from the site shortlist by key. Prospects already created are kept.readwritewrites · removes data
shortlist_to_prospectsTurn shortlisted creators into private prospects (free: no credit spent; counts on the discovery quota). Without keys, every shortlisted creator not yet a prospect. To get e-mail addresses afterwards, use find_emails (paid).readwritewrites
find_emailsPaid e-mail lookups at the creator-data providers, for prospects still without an address (Pro). The first call returns only the estimate and a confirmToken: tell the user the cost, and only after they agree call again with the same ids plus confirmToken. Never replaces an existing address.readspendspend*writes · paid, confirmed · reaches outside services
list_prospectsThe private prospects of the account, most recently collected first, filtered by status, site, platform or whether they have an address. Paginated by cursor.readread-only
get_prospectOne prospect: status, contact channel, address if known, provenance (where the data comes from).readread-only
chatSend one message to the GetAff partner-search assistant of a site (any language), e.g. "find wedding creators in Belgium with 10k-100k followers". It may change the criteria, run a free search, select creators or explain a rank. It never spends credits: a paid step comes back as pending — show the user its cost and, if they agree, call chat_confirm. One conversation per site; reset: true starts over.readsearchwritewrites · reaches outside services
chat_confirmRun (decision "confirm") or drop (decision "cancel") the paid action a chat turn proposed. Confirming spends the credits shown in pending — only call it with "confirm" after the user explicitly agreed. Pending actions expire after 15 minutes.readsearchwritewrites · reaches outside services
competitors_getThe competitor list of a site, the last lookup summary, and where each competitor recruits in the GetAff directory.readread-only
competitors_setReplace the competitor list of a site (up to 5 domains, optional brand name). Then competitors_run finds who promotes them.readwritewrites
competitors_runLook for public creators whose videos link to, or give a coupon for, the site competitors. Confirmed promoters become prospects (discovery quota). No credit of the user is spent; at most two runs an hour; takes up to two minutes.readsearchwritewrites · reaches outside services
competitors_latestThe last competitor lookup of a site with its findings and their public evidence (video links, coupons).readread-only
creators_searchSearch the GetAff index of public creator profiles with filters (text, platforms, countries, niches, languages, audience, engagement, activity). Counts one of the plan index pages per day. rank: "fit" ranks by fit for the user programme. For a programme-aware search across every source, prefer search_partners.readsearchread-only
get_creatorOne creator of the index, by platform and platform id or handle.readread-only
lookalikesIndex creators similar to seed creators — given by key parts (creators), handles or profile URLs (handles), or the user accepted creator prospects (accepted: true).readsearchread-only
list_found_creatorsThe private library of creators that live providers already returned to this account ("Found creators"): reusable without spending credits again, with the sites each was found for and its fit. Filter by platform, niche, country, language, audience band, text or site.readread-only
found_to_prospectsTurn creators of the found library into private prospects, by key (free: no credit spent; counts on the discovery quota).readwritewrites
list_mailboxesThe mailboxes connected in GetAff that outreach sequences can send from (connecting one is done in the app).readread-only
list_sequencesThe outreach sequences of the account and the state of each of their three steps. Read-only.readread-only
enrol_in_sequenceSchedule the three-step outreach sequence (opening + two follow-ups) for ONE prospect, from a connected mailbox, with every check GetAff makes: valid and unsuppressed address, the recipient country compliance regime (pass the attestations that apply), complete sender settings, a published programme. E-mails are then sent by GetAff at the scheduled times — confirm with the user before calling. Without draft, the draft saved in GetAff for this prospect is used.readwritewrites · reaches outside services
What the server will not do
  • Spend credits without a confirmation token issued for that exact call.
  • Send an e-mail: enrol_in_sequence schedules a sequence with every check of the app (compliance, suppression, sender settings); GetAff sends it from your mailbox later.
  • Accept a browser session: only the API key authenticates, so a signed-in browser grants nothing.
  • Return a key, a token or a network credential, in any tool.
  • Pass creator text off as instructions: names, handles and bios come from public profiles and are labelled as third-party data in every result.

Examples

A raw JSON-RPC call with curl, then the same with the official SDKs.

curl
curl -s https://www.getaff.org/api/mcp \
  -H "Authorization: Bearer $GETAFF_API_KEY" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call",
       "params":{"name":"search_partners",
                 "arguments":{"siteId":"Wq3b9ZK1","includeLive":true}}}'

# → structuredContent.status = "confirmation_required", with the estimate
#   and a confirmToken. Nothing was spent. After the user agrees:
#   same call, "arguments":{"siteId":"Wq3b9ZK1","includeLive":true,"confirmToken":"gcf_…"}
TypeScript — @modelcontextprotocol/sdk
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';

const transport = new StreamableHTTPClientTransport(new URL('https://www.getaff.org/api/mcp'), {
  requestInit: { headers: { Authorization: `Bearer ${process.env.GETAFF_API_KEY}` } },
});
const client = new Client({ name: 'my-agent', version: '1.0.0' });
await client.connect(transport);

const { tools } = await client.listTools();
const sites = await client.callTool({ name: 'list_sites', arguments: {} });
console.log(tools.map((t) => t.name), sites.structuredContent);
Python — mcp
import asyncio, os
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client

async def main():
    headers = {"Authorization": f"Bearer {os.environ['GETAFF_API_KEY']}"}
    async with streamablehttp_client("https://www.getaff.org/api/mcp", headers=headers) as (read, write, _):
        async with ClientSession(read, write) as session:
            await session.initialize()
            tools = await session.list_tools()
            print([t.name for t in tools.tools])
            sites = await session.call_tool("list_sites", {})
            print(sites.structuredContent)

asyncio.run(main())

OAuth: not yet

The MCP authorization spec (OAuth 2.1 with dynamic client registration) is what claude.ai and ChatGPT connectors use to sign in. GetAff does not implement it yet; when it does, the same tools will be available to those connectors without pasting a key. Until then, API keys are the only way in — revocable at any time from /docs/keys.