invitation.dev / docs / agents
agents earn too
If your bot, assistant, or agent recommends products, it can be paid for recommendations that convert. Your API key is the identity — there is no separate agent account to create, no application to fill in.
This surface is gated behind FEATURE_AGENT_API and returns 404 when a deployment has it off. Check against your target environment before building on it.
# the loop
Five calls, start to paid
1. partner_me → confirm the key has the "agent" scope
2. search_offers → find a campaign worth recommending
3. get_referral_link → get the tracked URL + its disclosure line
4. (share both) → the link is worthless without the disclosure
5. get_earnings → conversions credit here, never clicksEnrollment happens on your first get_referral_link for a campaign — you do not apply and wait. Campaign owners can require approval, in which case clicks still track and rewards are held rather than lost.
# setup
Key, config, smoke test
Create a key with the agent scope in settings → API keys, then paste this into your MCP client. The endpoint speaks streamable HTTP with one JSON-RPC message per POST, so it works in Claude Code, Claude Desktop, Cursor, and anything else that speaks MCP over HTTP — no npm install, no local process.
{
"mcpServers": {
"invitation": {
"type": "http",
"url": "https://api3.invitation.codes/api/mcp",
"headers": {
"Authorization": "Bearer invt_..."
}
}
}
}Before wiring anything up, confirm the key sees the tools you expect. The tool list is scope-dependent — a key without agent will not see the earn tools, which is the most common reason a setup looks broken.
# Does the endpoint answer, and which tools does this key see?
curl -X POST https://api3.invitation.codes/api/mcp \
-H "Authorization: Bearer $INVITATION_API_KEY" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'curl -X POST https://api3.invitation.codes/api/mcp \
-H "Authorization: Bearer $INVITATION_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "get_referral_link",
"arguments": { "campaignSlug": "summer-launch" }
}
}'# tools
What the earn side exposes
search_offers
The agent-safe catalog. Only returns campaigns whose owners opted into agent distribution and that pass the safety policy — you cannot accidentally promote something that is off-limits.
get_referral_link
The core primitive. Enrolls you on first call, then returns the same tracked link every time. Carries the disclosure string you are required to render.
track_click
Optional. Registers an outbound click and returns a session token the merchant validates conversions against. Use it when you control the hop to the merchant.
get_earnings
Clicks, conversions, pending and paid coins, per campaign. Your dashboard as an API call.
partner_me
Key introspection — scopes, org binding, enrolled campaigns, balance. Call it first to confirm you are wired up.
Merchant tools (create_campaign, update_campaign, activate_campaign, track_conversion) are on the same endpoint but need an org-bound key with write and org:campaigns. Call tools/list to see what your key actually has.
# disclosure
Render it. Every time.
Every link-issuing response carries a disclosure string, and showing it alongside the link is a term of the agent program — not a suggestion. FTC affiliate-disclosure rules apply to bot recommenders the same way they apply to humans. Repeated omission is grounds for key revocation.
Based on what you described, Notion's team plan fits.
→ https://invitation.codes/campaigns/notion?ref=agent-u_abc
This is a referral link. The assistant's operator may earn a
commission if you make a purchase — at no extra cost to you.The disclosure sits at the top of every MCP tool result, so an agent that passes the whole result through to the user complies by default. You have to actively strip it to get this wrong.
# rules
Things that will surprise you otherwise
Conversion-only rewards
Coins credit when a referred person actually converts — never per click, ever. This is deliberate: it makes click-flooding worthless, which is why the catalog can stay open rather than gated behind manual review.
The catalog is already filtered
search_offers only returns campaigns whose owners opted into agent distribution and that pass the safety policy. An owner flipping that off stops future payouts on that campaign, not just future links.
Links are idempotent
Calling get_referral_link twice for the same campaign returns the same code and the same enrollment. Cache it or do not — both are correct.
sub_id is your attribution
Pass sub_id to split earnings by placement, thread, or channel. One key running several surfaces sees them separately in get_earnings rather than as one undifferentiated number.
# without mcp
Node client, or plain HTTP
MCP is the convenient path, not the only one. Every tool above is a projection of the same procedure — so if your agent framework does not speak MCP, use the typed Node client or raw HTTP and lose nothing.
import { InvitationNodeClient } from "@invitation/node";
const invitation = new InvitationNodeClient({
apiKey: process.env.INVITATION_API_KEY!,
});
const { data: offers } = await invitation.partner.searchOffers({ query: "notion" });
const link = await invitation.partner.getReferralLink({
campaignSlug: offers[0]!.slug,
subId: "thread-42",
});
// Render both. The link without the disclosure is a violation.
reply(`${link.url}\n\n${link.disclosure}`);
const earnings = await invitation.partner.getEarnings();# No MCP client? The same primitive over plain HTTP.
curl -X POST https://api3.invitation.codes/api/partner/v1/links \
-H "Authorization: Bearer $INVITATION_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "program": "summer-launch", "sub_id": "thread-42" }'Full endpoint table, pagination, and the error contract are in the REST reference.