MCP
MCP server
The console as tools. Connect with an API key; read tools need read, write tools need manage.
Connect
/api/ads/mcpkey (read, manage)claude mcp add --transport http freebuff-ads https://freebuff.com/api/ads/mcp \
--header "Authorization: Bearer $FREEBUFF_ADS_API_KEY"{
"mcpServers": {
"freebuff-ads": {
"url": "https://freebuff.com/api/ads/mcp",
"headers": { "Authorization": "Bearer fbadv_…" }
}
}
}Streamable HTTP, stateless, plain JSON. Live keys only. Ask the agent to call get_account first.
Rules
- Cents. Pacific days. Report ranges default to 30 days, max 366.
- Lifecycle:
create_campaign→submit_campaign→ review →pause_campaign/resume_campaign. - Images by public https URL, never inline.
delete_campaignneedsconfirm: true.- No tool tops up, changes a card, or mints a key.
- 120 requests a minute; one JSON-RPC message per request, ≤ 64 KiB.
Tools (28)
Account
get_accountreadThe account this key belongs to: company, review status, balance, card on file, and the key’s scopes. Call it first.
No inputs.
Returns account, balanceCents, hasCard, apiKey.scopes, consoleUrl
get_billingreadBalance, card on file and the statement for one month, the same figures the invoice is built from.
| Input | Type | Description |
|---|---|---|
monthoptional | YYYY-MM | Statement month (Pacific). Default: this month. |
Returns balanceCents, creditLineCents, card details, autoReloadEnabled, statement, consoleUrl
Top-ups and card changes happen in the console at the returned URL. There is no tool for them.
list_api_keysreadThe keys on the account: display prefix, mode and creation date only. Keys are minted and revoked in the console.
No inputs.
Returns keys[] { prefix, mode, createdAt }, consoleUrl
get_referral_linkreadThe referral link that credits this account when another advertiser signs up through it, with the reward terms.
No inputs.
Returns code, url, rewardCents, rewardCap
list_placementsreadThe placements a campaign can run on: id, surface, and what the unit looks like. Use the ids in create_campaign.
No inputs.
Returns placements[]
Campaigns
list_campaignsreadEvery campaign with its status, caps, spend and delivery totals over a range, plus account totals: the console overview.
| Input | Type | Description |
|---|---|---|
fromoptional | YYYY-MM-DD | First day. Default: 29 days before to. |
tooptional | YYYY-MM-DD | Last day. Default: today. |
Returns account, totals, campaigns[] { id, name, status, billingModel, cpcCents, dailyCapCents, spentTodayCents, conversions, totals, reach }, deliveryBreakdowns[], reporting, consoleUrl
get_campaignreadOne campaign in full: terms, flight, placements, pricing, every creative with its review status and delivery, and why it is not serving if it is not.
| Input | Type | Description |
|---|---|---|
campaignIdrequired | string | A campaign id from list_campaigns. |
fromoptional | YYYY-MM-DD | First day. Default: 29 days before to. |
tooptional | YYYY-MM-DD | Last day. Default: today. |
includeTrackingoptional | boolean | Also return whether its conversion tracking works (Working, Not set up, Too early to tell, Stopped reporting) with the next step. Costs an extra read. Default false. |
Returns campaign { …, creatives[], pricing, tracking? }, consoleUrl
create_campaignmanageCreate a draft: name, https landing URL, daily budget, flight dates, placement ids and one or more creatives. Drafts cost nothing and do not serve until submitted and reviewed.
Needs a pending or approved account.
| Input | Type | Description |
|---|---|---|
namerequired | string | Up to 120 characters. |
landingUrlrequired | string | An https URL, up to 2000 characters. |
dailyCapCentsrequired | integer | Daily budget in cents, 5000 to 500000. |
flightStartrequired | YYYY-MM-DD | First day of delivery (Pacific). |
flightEndrequired | YYYY-MM-DD | null | Last day, on or after the start, or null for open-ended. The key must be present. |
placementIdsrequired | string[] | One or more ids from list_placements, each once. |
creativesrequired | Creative[] | At least one, up to 25. Each: title (120 chars, at most one *accent*), body (500), cta (80), and optional landingUrl, expandedBody (240), diagram (40), bullets (3 × 40), brandColor and brandInk (#rrggbb). |
conversionEventTypeoptional | string | signup_completed, demo_booked, trial_started, installation_completed or subscription_started. |
targetCpaCentsoptional | integer | null | 100 to 10,000,000. |
countryTargetingoptional | { mode, countries } | null | mode include or exclude; up to 60 uppercase ISO-3166-1 alpha-2 codes. |
frequencyCapPerHouroptional | integer | null | 1 to 3; can only lower the platform cap. |
sponsoredProcedureoptional | string | null | For an agentic campaign: the onboarding procedure (up to 8000 characters) with a tracked bfcid link. |
sponsoredTargetingoptional | string | null | Who the agentic proposal is for (up to 2000 characters). Needs sponsoredProcedure. |
Returns id, status: "draft", consoleUrl, next
Unknown fields are rejected. Validation is the console’s, with the same messages.
update_campaignmanageReplace a campaign’s configuration with a full new one, the same fields as create_campaign. A draft stays a draft; an approved campaign takes the edit immediately.
Needs a pending or approved account.
| Input | Type | Description |
|---|---|---|
campaignIdrequired | string | A campaign id from list_campaigns. |
campaignrequired | object | The full configuration. Include each existing creative’s id to keep it and its history; a creative without an id is new. |
Returns id, consoleUrl
A full replacement, not a patch. Refused with conflict once a campaign is no longer editable.
set_campaign_imagemanageAttach a logo or a sponsor-break hero from a public https URL. Freebuff downloads it once, validates it as the console upload does, and keeps its own copy.
Needs a pending or approved account.
| Input | Type | Description |
|---|---|---|
campaignIdrequired | string | A campaign id from list_campaigns. |
kindrequired | "logo" | "hero" | logo: a static square PNG or JPEG, 64 to 512 px per side, re-encoded to PNG. hero: a static 16:10 PNG, JPEG or WebP, at least 1024×640. Each at most 1024 KB. |
imageUrlrequired | string | A direct https link on port 443 to a public hostname that answers 200 with an image within 10 seconds; at most three redirects, only to public https hosts. No IP addresses, localhost or internal names. |
Returns id, kind, mediaType, consoleUrl
Works on draft, paused and live campaigns, not while one is in review. On a draft the creatives return to pending review; on a live campaign the image applies at once and is reviewed afterwards.
submit_campaignmanageSend a draft for review. Once the account is approved, a later campaign may be approved automatically and go live on submit.
Needs a pending or approved account.
| Input | Type | Description |
|---|---|---|
campaignIdrequired | string | A campaign id from list_campaigns. |
Returns id, consoleUrl
resume_campaignmanageStart or restart delivery of an approved, paused campaign.
Needs a approved account.
| Input | Type | Description |
|---|---|---|
campaignIdrequired | string | A campaign id from list_campaigns. |
Returns id, consoleUrl
pause_campaignmanageStop delivery immediately. The campaign keeps its approval and its reporting; resume_campaign restarts it.
Needs a approved account.
| Input | Type | Description |
|---|---|---|
campaignIdrequired | string | A campaign id from list_campaigns. |
reasonCodeoptional | string | too_expensive, too_few_results, wrong_audience, editing, budget, finished or other. |
reasonNoteoptional | string | Free text, up to 1000 characters. |
Returns id, consoleUrl
delete_campaignmanageDelete a campaign permanently. Ask the person first.
Needs a pending or approved account.
| Input | Type | Description |
|---|---|---|
campaignIdrequired | string | A campaign id from list_campaigns. |
confirmrequired | boolean | Must be true. Without it the tool answers confirmation_required and does nothing. |
Returns id, deleted: true
preview_adsreadHow the account’s creatives render on each surface, as the console’s preview shows them. Optionally one campaign.
| Input | Type | Description |
|---|---|---|
campaignIdoptional | string | A campaign id from list_campaigns. |
Returns creatives[] { id, campaignId, title, body, cta, landingUrl, imageUrl, heroImageUrl, status, placements }
Image URLs open with a signed-in console session, not with the API key.
list_activationsreadCharged activations for one campaign: when each happened, on which placement and creative, and the click it came from.
| Input | Type | Description |
|---|---|---|
campaignIdrequired | string | A campaign id from list_campaigns. |
Returns activations[] { occurredAt, occurredDay, placementId, creativeTitle, clickedAt }, consoleUrl
Answers too_large when there are too many to return; export from the console instead.
create_test_clickmanageMint a test click id for a campaign so a conversion postback can be exercised end to end. Test conversions never bill or count.
| Input | Type | Description |
|---|---|---|
campaignIdrequired | string | A campaign id from list_campaigns. |
Returns clickId, campaignId, eventType, expiresAt, eventId, conversionPayload, requiredTrackingRail
Valid for about 30 minutes. Rate limited; answers rate_limited when exceeded.
Pixel and goal rules
get_pixel_setupreadThe pixel for this account: the install snippets and tag id, where the tag has been seen, the data level and other settings, analytics events seen in the last 7 days, and each campaign with whether goal rules may be added.
No inputs.
Returns tagId, snippet, snippetWithInlineCalls, pinnedSnippet, cspLines, settings, registeredDomains, tagSeen, analyticsEventsSeen, campaigns, consoleUrl
update_pixel_settingsmanageChange what the pixel collects, the linked domains the click follows visitors to, extra site-search keys and extra query parameters to keep.
Needs a pending or approved account.
| Input | Type | Description |
|---|---|---|
dataLeveloptional | string | conversions, standard or full. |
linkedDomainsoptional | string[] | Up to 10 registrable domains. Replaces the stored list. |
searchKeysoptional | string[] | Query keys read as site search. Replaces the stored list. |
queryAllowlistoptional | string[] | Query keys kept on page views. Replaces the stored list. |
Returns settings
list_conversion_rulesreadThe no-code goal rules on one campaign, and whether new rules are refused on it and why.
| Input | Type | Description |
|---|---|---|
campaignIdrequired | string | A campaign id from list_campaigns. |
Returns campaign, rules[]
create_conversion_rulemanageCount an analytics event or a page visit as a conversion goal on a campaign. At most 5 active rules per campaign. Pixel conversions are never charged.
Needs a pending or approved account.
| Input | Type | Description |
|---|---|---|
campaignIdrequired | string | A campaign id from list_campaigns. |
kindrequired | "analytics_event" | "url" | What to match. |
matchrequired | string | The event name exactly as sent (a letter, then up to 63 letters, digits, spaces or _ . : -), or a path with * for one segment. gtm.* never matches. |
goalEventTyperequired | string | signup_completed, trial_started, demo_booked, installation_completed or subscription_started. |
Returns rule
Refused with conflict on a campaign billed per conversion, or whose server postback reported a live goal conversion in the last 30 days.
disable_conversion_rulemanageTurn off one goal rule. Conversions it already recorded stay. To change a rule, turn it off and create another.
Needs a pending or approved account.
| Input | Type | Description |
|---|---|---|
campaignIdrequired | string | A campaign id from list_campaigns. |
ruleIdrequired | string | From list_conversion_rules. |
Returns rule
Reporting
get_delivery_reportreadImpressions, clicks, spend and conversions per day over a range, optionally grouped by campaign, creative, placement, surface or country and filtered by any of those.
| Input | Type | Description |
|---|---|---|
fromoptional | YYYY-MM-DD | First day. Default: 29 days before to. |
tooptional | YYYY-MM-DD | Last day. Default: today. |
groupByoptional | string | campaign, creative, placement, surface or country. |
campaignIdsoptional | string[] | Limit to these ids, at most 50. |
creativeIdsoptional | string[] | Limit to these ids, at most 50. |
placementIdsoptional | string[] | Limit to these ids, at most 50. |
surfacesoptional | string[] | Limit to these ids, at most 50. |
countriesoptional | string[] | Limit to these ids, at most 50. |
maxRowsoptional | integer | 1 to 5000. Default 1000. |
Returns range, groupBy, reporting, rows[] { day, settled, campaignId, …, totals, conversions, conversionsByEvent, traffic }, conversionEventTypes, reach
The same data as the reporting API’s delivery endpoint. Answers too_large when the selection exceeds maxRows.
list_conversionsreadConversion events reported back to Freebuff over a range, newest first, with a cursor for the next page. Test conversions are excluded unless includeTest is true.
| Input | Type | Description |
|---|---|---|
fromoptional | YYYY-MM-DD | First day. Default: 29 days before to. |
tooptional | YYYY-MM-DD | Last day. Default: today. |
campaignIdsoptional | string[] | Limit to these ids, at most 50. |
eventTypesoptional | string[] | Limit to these ids, at most 50. |
includeTestoptional | boolean | Default false. |
limitoptional | integer | 1 to 500. Default 100. |
cursoroptional | string | The nextCursor of the previous page. |
Returns range, conversions[] { eventId, campaignId, campaignName, eventType, occurredAt, settlementStatus, chargedCents, isTest, reportedVia, matchedVia }, nextCursor
Team
list_teamreadMembers of the account and the invitations still open.
No inputs.
Returns members[] { id, email, name, role, createdAt }, invites[] { id, email, role, createdAt, expiresAt }, consoleUrl
invite_membermanageEmail an invitation to join the account as a member, sent in the owner’s name.
Needs a approved account.
| Input | Type | Description |
|---|---|---|
emailrequired | string | An email address, up to 320 characters. |
Returns invite { id, email, role, createdAt, expiresAt }, consoleUrl
Sends a real email.
revoke_invitemanageWithdraw an open invitation by its id.
Needs a approved account.
| Input | Type | Description |
|---|---|---|
inviteIdrequired | string | From list_team. |
Returns inviteId, revoked: true
remove_membermanageRemove a member by id. An owner cannot be removed this way.
Needs a approved account.
| Input | Type | Description |
|---|---|---|
memberIdrequired | string | From list_team. |
Returns memberId, removed: true
Errors
A refusal is a tool result with isError and a stable code, never a JSON-RPC error. Do not retry it.
{
"isError": true,
"content": [{ "type": "text", "text": "insufficient_scope: create_campaign needs an API key with the \"manage\" scope." }],
"structuredContent": { "error": "insufficient_scope", "message": "…" }
}| Code | Meaning |
|---|---|
insufficient_scope | Key lacks the scope. |
not_allowed | Account state forbids it, or owner-only. |
not_found | Not on this account. |
conflict | Lifecycle refused: no longer editable, rule limit, open invite. |
invalid_input | Validation, bad range, bad cursor, bad image. |
confirmation_required | delete_campaign without confirm: true. |
not_available | Feature not on for this account. |
too_large, fetch_failed, rate_limited, internal_error | As named. |
HTTP-level answers
| Status | Body | Meaning |
|---|---|---|
| 400 | invalid request | Not one JSON-RPC object, or invalid JSON. |
| 401 | unauthorized | No key, revoked, or a test key. |
| 404 | Not found. | MCP not enabled for this account. |
| 405 | … | GET and DELETE: no event stream, no session. |
| 413 | request too large | Over 64 KiB. |
| 429 | rate limited | Retry-After is set. |