Marketing
affiliateo.com
This MCP server provides tools for managing affiliate marketing programs, including listing businesses, retrieving commission and affiliate stats, enrolling affiliates, and tracking payouts.
ENDPOINT 1
https://affiliateo.com/api/mcp
MCP server metadata
- Name
- affiliateo
- Version
- 1.0.0
Affiliateo lets businesses run affiliate programs and manage paid ads (Meta, TikTok, Apple Search Ads, Google Ads). Call list_businesses first: it returns the business slugs, ad-network connection status, and app ids/slugs every other tool needs. Money values are integer cents unless a field name says micros (ad spend/revenue are micros: 1/1,000,000 of the currency unit). An affiliate app pays commission on sales from a website, a mobile app, or both at once: read its `rails` object (web / mobile plus the provider connected to each) to know which. Its content_type is "affiliate_app", the only name it has, sent and read back. Never infer rails from it. Read-only API keys can use every read tool; enroll_affiliate, create_app, update_app, launch_ad_campaign, update_ad_entity, and write_ad_object need a read_write key. TWO tools create ads and they answer different questions. launch_ad_campaign builds a WHOLE STACK in one call (campaign plus everything under it, always PAUSED) and is where campaign objectives live — every network takes a different objective vocabulary and a different set of them, so read that tool’s description before assuming a goal exists. write_ad_object creates or edits ONE object at ONE level with the network’s entire field surface, which is what to reach for afterwards (a second ad set, a targeting change, anything the launch body has no field for). Building an ad object is three steps: describe_ad_fields for the field names, then the tool that hands out the ids those fields take (list_ad_audiences for audience_ids / saved_audience_id, call_api on …/ads/catalogs and …/ads/apps for catalog_id, product_set_id, sku_ids and app_id), then write_ad_object. TikTok audiences, product catalogs, promoted apps, automated rules and custom conversions are LIVE — TikTok granted those permissions, verified against the live account on 2026-08-02. If one of them still answers with a missing-permission error, the cause is the TOKEN, not the account: TikTok fixes scopes into the access token at consent, so a business that connected before approval carries an older token and has to reconnect TikTok once. Tell the owner to reconnect rather than retrying with different arguments. Affiliates and creators (users who are not business owners) use the my_* tools — get_my_summary, list_my_affiliations, list_my_conversions, plus get_my_wallet + list_my_transactions for their wallet balance and transaction ledger — to read their OWN earnings and money across every business they joined; list_businesses may be empty for them. MY_* TOOLS ARE SCOPED TO THE API KEY HOLDER AND CAN NEVER RETURN ONE OF YOUR END USERS. They resolve to whoever owns the key being used, so calling them with a business's key returns THAT BUSINESS OWNER's own affiliations, links and wallet — including programs they joined at other companies. They are for a person reading their own data with their own key, and they take no user-identifying argument, which is the tell. If you are building an affiliate screen INSIDE a product, serving many different users, do not use them at all: enroll the user with enroll_affiliate (their email) and read them back with get_affiliate (email or ref_code). Those are keyed by the end user and are the only correct source for a multi-user integration. Embedded Elements are drop-in UI the developer mounts INSIDE their own app or site (referral link, QR, per-product rates, recent sales, this app’s pending/payable/paid, cash out, withdrawal history, identity); mint a session with call_api on POST …/affiliates/embed-session, or use the hosted verification/withdrawal links for a plain redirect. These MIX with the tools above rather than replacing them, per screen: build the screens the developer wants control over from the read tools and mount only the components they would rather not build (bank collection and ID capture are the usual two). A session carries ONLY the components named, so asking for one is normal and correct; requesting all ten to hide eight is not. The numbers agree either way — the balance element renders the same pending/payable/paid get_affiliate already returns. WHICH TO USE PER SCREEN. Every element is its own page load (an iframe on web, a WebView on mobile), so a tab stacking four of them pays four before it paints, and the host cannot cache any of them. Anything the read tools already return is FASTER built natively, and can be cached so the screen paints on open. RECOMMENDED: build link, qr, stats, products, activity and balance yourself from get_affiliate + get_app + list_affiliate_conversions, and mount elements only for withdraw, payouts and identity — the three that collect bank details, expose the account-wide wallet, or run ID capture. True on web and on mobile; it bites hardest on mobile, where every element is a whole WebView. Lists are the sharpest case: the embedded activity and payouts elements show the 25 most recent with NO pagination and NO filtering, so an affiliate with a year of sales cannot reach the 26th and cannot ask to see just their refunds, while list_affiliate_conversions is cursor-paginated (?limit=, ?starting_after=) and filterable by date (?from=&to=) and by type (?type=refund,chargeback, comma-separated, any of subscription, one_time, renewal, trial, refund, chargeback). FILTER SERVER-SIDE, NOT IN THE HOST. Both filters are applied in the query, upstream of the cursor, so a filtered feed pages through matching rows only. Filtering an already-fetched page in host code looks equivalent and is not: it can only search the rows on screen, so "Refunds" finds nothing for someone whose one refund sits 300 rows down, and the filter reads as broken. Unknown ?type= values return 400 rather than an empty page, so a typo cannot be mistaken for "this affiliate has no refunds". PICK THE LAYOUT FROM THE SURFACE, and for most integrations that is ONE PAGE. Adding an affiliate section to an existing product (a settings page, an account area, a creator dashboard) — the common case — wants a SINGLE scrolling page: referral link + QR at the top, then lifetime stats, then earnings with the sales list, then the cash-out button. Less navigation, and everything an affiliate came for is visible at once: what to share, what they have made, how to get it. Build all of it from get_affiliate + get_app + list_affiliate_conversions and mount ONE element, withdraw. CASH OUT ON A ONE-PAGE LAYOUT GOES IN A DIALOG, not stacked inline at the bottom. The element is an iframe you place, so the dialog is YOURS: a modal on web, a full-screen sheet on mobile web, its own pushed screen in a native app. It measures itself and reports its height, so a dialog can size to it. Inline works but makes the page grow and shrink underneath the reader as the bank step expands, on the one surface where that feels worst. DO NOT UNMOUNT THE ELEMENT WHEN THE DIALOG CLOSES — the single easiest way to make this feel broken. The confirm lives in the iframe's memory and nowhere else, so destroying it (which most dialog libraries do on close) throws the confirm away and the person re-confirms on every open. Mount it once and toggle visibility with display:none instead: the confirm then survives the whole visit, and a hidden element emails nobody, because it only asks once it has actually been on screen. That also makes the first open instant. THE CONFIRM BUTTON IS RELABELLABLE, which matters most here: its default reads "Confirm it's you", which on a page somebody opened in order to cash out sounds like a security challenge rather than the thing they clicked toward. Pass gate_label at mint, one of: withdraw ("Withdraw"), verify ("Get set up to be paid"), get_started, confirm (the default). All four are ALREADY translated into 16 languages, so paired with lang the button lands in the viewer's. Which one to send is knowable before the element loads — payout.identity_verified rides every affiliate response, so send verify to somebody who still needs the ID check and withdraw to everyone else. REACH FOR A PRESET FIRST: gate_label_text takes any short literal string instead and is NOT translated, which moves that job to the host permanently in every language it ships, a real cost for a phrase we already have. Use it only when the wording genuinely is not in the list, passing a different string per viewer from the host's own catalogue (the SDK re-mints hourly, so a language change carries over on the next refresh). Either way the explanation beneath stays ours, so a custom label can never misdescribe what the tap authorizes. An unknown gate_label is a 400 naming the valid ones rather than a silent fall back to the default. EMBED VS REDIRECT FOR CASH OUT, and the redirect is the one with LESS friction, which is the opposite of what most people assume. The hosted withdrawal-link is a real page on our domain, so it sees the person's Affiliateo session cookie and a signed-in affiliate skips the confirm ENTIRELY. The embedded element is an iframe on the developer's domain, where that cookie does not travel, so it always costs one tap on web. Choose the embed to keep somebody on your page mid-task, which is what a one-page layout is for, not because it is the smoother of the two. Choose the redirect when leaving is cheap — a dedicated Cash out tab, or a plain "Withdraw earnings" button somewhere quiet — and you also get to word that button yourself, since it is your own. REFERENCE LAYOUT FOR A DEDICATED AFFILIATE SURFACE (our own apps, where affiliates are the whole product), three tabs. LINK: a QR of the short link (short survives a username change, so a printed code keeps working), then the referral link in a field whose own chevron opens the direct/short/username picker, a copy icon beside it, a share button under that, then a clicks / sales / earned row, then the per-product rates. BALANCE: pending+payable+paid as the headline figure, the three buckets as stacked proportional bars, then date chips (all/today/7d/30d/90d/1y), a Type menu beside the list heading, and a paginated sales list under both. CASH OUT: the withdraw element, alone. THE TYPE MENU is five entries over six raw types: All types, Sales (?type=subscription,one_time), Renewals (renewal), Trials (trial), Refunds (?type=refund,chargeback). Whether a first sale renews, and whether a reversal was the customer asking or the bank taking, are not distinctions an affiliate filters on (both mean money back); the rows still say Sale and Chargeback individually, so the difference stays visible where it is useful. NAVIGATION is the host's and no endpoint describes it: on mobile put the three tabs in the platform's native bottom tab bar rather than a hand-built one (the system bar brings the OS's hit-testing, animation and accessibility, and on current iOS it is the floating pill); on the web there is no system equivalent, so use whatever the page already has (a tab strip, a sidebar, three routes). COPY, AND WHERE TO GET IT. The API returns DATA, never UI text: `lang` is a mint parameter for elements only, and no read endpoint takes it or returns a label. So an API-built screen needs its own words, but NOT its own translations: the exact strings the elements use are published at https://affiliateo.com/locales/{lang}.json in all 16 languages, so a developer can lift them instead of writing and translating ~25 strings. Point at the keys that matter: embed.appEarnings.* (Pending / Payable / Paid, plus the hint line explaining that paid money already sits in their Affiliateo balance), embed.activity.* (Sale / Renewal / Trial / Refund / Chargeback and the empty states), embed.filter.preset.* (the date chips) and embed.link.format* (Direct / Short / Username). Copy the VALUES into the host catalogue at build time rather than fetching live: those files are the elements runtime asset, not a versioned API, so a key can be renamed with no deprecation cycle and a live fetch would blank labels in a shipped app. Keep the money words verbatim even when rewording the rest: Pending is earned but not yet released, Payable is released and ready to pay out, and Paid means already in their Affiliateo balance and NOT yet in their bank, which is why "Paid out" is the wording to avoid. Two things nobody translates: product NAMES are whatever the business owner typed, and the tab bar is the host's, since no endpoint describes navigation. The one figure a developer cannot build themselves is the affiliate’s account-wide wallet total: it spans every program that person is in, so no owner-side tool returns it, and it appears only inside the gated withdraw/payouts components or via get_my_wallet with the affiliate’s OWN key. Full REST reference: https://affiliateo.com/docs/api
Known tools 35
list_businessesList every business this API key can act on, with each ad network’s connection status and every app’s id/slug/type/status (all five types).
Inferred read-onlyget_appCommission info for one affiliate app: rate and type (with per-product rates, prices, and approximate commission per sale for the app’s visible products), affiliate count, total paid out, status, and whether joining requires an application.
Inferred read-onlyget_app_summaryThe app’s whole affiliate program at a glance: affiliate counts, lifetime totals (clicks, conversions, renewals, revenue and commission in cents), and payout buckets (pending/payable/paid/refunded) summed across every affiliate (web_traffic apps report per-click earnings here).
Inferred read-onlyenroll_affiliateEmbedded affiliate signup: enroll a user as an affiliate by email.
Potential side effectsget_affiliateOne affiliate’s referral links, lifetime stats (clicks, conversions, renewals, revenue and commission in cents), payout buckets (pending/payable/paid/refunded cents; per-click for web_traffic, with a click_payout object of CPC + approved/payable clicks), and a payout readiness object ({ identity_verified, payouts_enabled }, also on list rows) for gating Verify / Cash out buttons.
Inferred read-onlylist_affiliatesList an app’s affiliates, newest first, cursor-paginated: the response carries has_more; pass the last row’s id as starting_after for the next page.
Inferred read-onlylist_affiliate_conversionsConversion history: date, type (subscription, one_time, renewal, trial, refund, chargeback), gross amount and commission in cents, and paid/unpaid status per row, each tagged with the earning affiliate’s ref_code.
Potential side effectsget_my_summaryThe caller’s OWN affiliate program across every business they’ve joined: affiliation counts, lifetime totals (clicks, conversions, renewals, revenue and commission in cents), payout buckets (pending/payable/paid/refunded, including per-click web_traffic earnings), and their account wallet balance.
Inferred read-onlylist_my_affiliationsEvery app the caller is an affiliate of, across all businesses, newest first: referral links (short + username + merchant-domain direct for apps with a website or landing page, plus the COUPON code when the app has Stripe discount codes on — a code typed at checkout, not a URL), lifetime stats, the caller’s EFFECTIVE commission (their personal deal when the owner set one, flagged custom: true, else the app default), a rails object saying whether that app pays on website sales, in-app purchases or both (and the ceiling each rail can actually pay), and which business it belongs to.
Potential side effectslist_my_conversionsThe caller’s own conversion history across every business they’re an affiliate of: date, type (subscription, one_time, renewal, trial, refund, chargeback), gross amount and commission in cents, payout status, and the app + business each row belongs to.
Potential side effectsget_my_walletThe caller’s account wallet balance and the buckets behind it, all in cents: total balance, withdrawable (balance minus funds still on the 7-day hold), held, pending bank deposits, locked ad budget, disputed commission, and in_transit (withdrawals already on their way to the bank), plus whether a payout destination is connected and a payout readiness object ({ identity_verified, payouts_enabled }).
Inferred read-onlylist_my_transactionsThe caller’s account wallet ledger, newest first: every money movement with its type (sale, deposit, payout/withdrawal, refund, chargeback, transfer_in/out, affiliate_commission, campaign_fund/refund/payout), a signed amount in cents, gross + fee where one applies, and the other party (buyer, business, app, platform, or transfer counterpart).
Potential side effectsget_my_transactionOne of the caller’s wallet transactions in detail, by id or short id (pay_/with_/fund_…): the same normalized row as list_my_transactions, plus, for the caller’s own deposits, the Stripe receipt URL and the card/bank used, and for affiliate-commission rows, the product and paying business.
Inferred read-onlyget_business_affiliate_summaryThe business owner’s STOREFRONT-affiliate program at a glance (people promoting the business’s products, distinct from app affiliates): affiliate counts, lifetime totals (visitors, sales, revenue, commission in cents — money net of refunds), and payout buckets.
Inferred read-onlylist_business_affiliatesThe business’s storefront affiliates, newest first: referral links (short + storefront), lifetime stats (visitors, sales, revenue, commission in cents — money net of refunds), and commission.
Inferred read-onlylist_business_affiliate_conversionsThe business’s storefront-affiliate sales: date, type, product, gross amount and commission (cents), payout status, and the earning affiliate’s ref_code.
Inferred read-onlyget_my_business_summaryThe caller’s OWN storefront-affiliate program across every business they promote: affiliation counts, lifetime totals (visitors, sales, revenue, commission in cents — money net of refunds), and payout buckets.
Inferred read-onlylist_my_business_affiliationsEvery business the caller is a STOREFRONT affiliate of (promoting its products), newest first: referral links (short + storefront), commission, lifetime stats (visitors, sales, revenue, commission in cents — money net of refunds).
Inferred read-onlylist_my_business_conversionsThe caller’s own storefront-affiliate sales across every business they promote: date, type, product, gross amount and commission (cents), payout status, and the business each row belongs to.
Inferred read-onlyget_ad_statsAggregated ad performance (spend, clicks, installs, revenue, ROAS) by campaign/adgroup/ad for one network over a date range (default: the last 30 days).
Inferred read-onlyget_ad_breakdownSplit one network’s spend and delivery by ONE dimension over a date range — the tool that answers "which placement / device / age group / hour is wasting the budget", which get_ad_stats cannot, because ad stats are flat daily totals with the day as their only segment.
Inferred read-onlylist_ad_campaignsThe live campaign tree with statuses, budgets, and bids, fetched from the ad network right now: Meta campaign→ad set→ad, Apple campaign→ad group→keyword, Google campaign→ad group→ads + keywords, TikTok campaign→ad group→ad (manual and Smart+ in one list; reading needs no write approval, so this works today and the response says whether writes do), Snapchat campaign→ad squad→ad.
Potential side effectspreview_adRender an ad that ALREADY EXISTS and hand back something a human can look at.
Inferred read-onlylaunch_ad_campaignCreate a WHOLE campaign stack in one call — campaign plus the ad set / ad group, creative and ad underneath it.
Potential side effectsupdate_ad_entityPause or resume an ad entity, change its daily budget, bid or spend cap, or archive it (Meta, ONE-WAY).
Inferred read-onlylist_ad_lookupsLook up the IDs and network-minted values that ad fields take — locations, interests, behaviours, languages, audiences, pixels, apps, catalogues, videos, images, creatives, conversion actions and more — on any of the five networks, in ONE shape: { id, label, meta }, where `id` is always the value that goes on the wire and `label` is always what a human picks from.
Inferred read-onlyget_ad_leadsThe leads a TikTok Lead Generation campaign has collected, and the Instant Forms themselves.
Inferred read-onlydescribe_ad_fieldsReturn the full catalogue of fields this network accepts on each object type, with their allowed values and whether each can be set at creation, changed afterwards, or both.
Inferred read-onlylist_ad_audiencesThe audiences on the ad account, with their size/coverage and status, plus the create/rename/delete paths.
Potential side effectswrite_ad_objectCreate or edit a single ad object using the network’s FULL field surface — every targeting, bidding, placement, scheduling and creative option it accepts.
Potential side effectssync_ad_statsPull the freshest insights from the ad network into Affiliateo right now (same as the dashboard’s Refresh button).
Inferred read-onlyget_ad_billingAd account money state on THREE networks, which answer three different questions because they bill in three different ways — the shapes are deliberately not flattened into one.
Inferred read-onlycreate_appCreate an app on a business — the four kinds businesses run: clipping (pay creators per 1k views), affiliate (commission on sales from the merchant’s website, mobile app, or both), web_traffic (pay per qualified click), collab (paid placements/content on creator channels).
Potential side effectsupdate_appEdit an existing app — same fields and clamps as the dashboard edit dialog.
Potential side effectscall_apiEscape hatch to the ENTIRE Affiliateo REST API — everything the named tools don’t cover: ad-group creation below a launched campaign, keyword and negative-keyword management, keyword ideas and bid recommendations, automated rules on Meta AND TikTok (TikTok adds an audit trail of what a rule actually did, and its update is a FULL REPLACE while Meta’s is a partial), creating/renaming/deleting custom audiences and lookalikes on FOUR networks (…/ads/audiences?network=meta|tiktok|google|snapchat, plus TikTok saved audiences under ?type=saved and TikTok-only sharing at …/ads/audiences/{audienceId}/share), custom conversions on both networks (…/ads/custom-conversions; what THAT PATH wraps on TikTok is event rules on a specific pixel, so they need a pixelId, and can be asked whether they are actually firing — TikTok also has a separate first-class /custom_conversion/ family, keyed by custom_conversion_id, which that path does NOT serve.
Potential side effectsCONNECT WITH APPROVAL
Client installation
Review this server and its permissions before adding it. Secret placeholders must be set locally.
Codex
~/.codex/config.toml
[mcp_servers.affiliateo]
url = "https://affiliateo.com/api/mcp"
enabled = true
Claude Code
.mcp.json
{
"mcpServers": {
"affiliateo": {
"type": "http",
"url": "https://affiliateo.com/api/mcp"
}
}
}
Claude Desktop
Settings → Connectors → Add custom connector
Name: affiliateo
Remote MCP URL: https://affiliateo.com/api/mcp
Add this remote URL as a custom connector in Claude Desktop. Availability depends on the user plan and workspace policy.
Cursor
.cursor/mcp.json
{
"mcpServers": {
"affiliateo": {
"url": "https://affiliateo.com/api/mcp"
}
}
}
Visual Studio Code
.vscode/mcp.json
Add to Visual Studio Code{
"servers": {
"affiliateo": {
"type": "http",
"url": "https://affiliateo.com/api/mcp"
}
}
}
Generic MCP
Client-specific MCP configuration
{
"name": "affiliateo",
"transport": "streamable-http",
"url": "https://affiliateo.com/api/mcp"
}
MCP Inspector
Run the official MCP Inspector locally and enter the indexed Streamable HTTP endpoint.
TRUST AND VERIFICATION EVIDENCE
Trust Data Available
BuiltWith Trust API v2 evidence for affiliateo.com was fetched 2026-08-20T01:35:35.951Z.
affiliateo.com is assessed as Trusted: Domain has an established technology history spanning over a year.
Evidence is source-attributed and does not guarantee that a third-party server is safe. Risk labels are conservative metadata heuristics.