Atribu
MCP Server

Available Tools

52 MCP tools: the onboarding golden path (provision, tracking install, connect hand-offs, conversion definitions, Meta CAPI setup), performance summaries, funnel analysis, traffic breakdowns, campaign drill-downs, customer journeys, workspace creative intelligence, recommendations, and Meta CAPI write-back

The Atribu MCP server exposes 52 tools. Every tool returns a structured response with a data object and a meta object containing request metadata, data freshness, and attribution context.

Most of them READ. The Onboarding tools are the exception: they set a workspace up from nothing, and each one is a thin wrapper on a single /api/v1 route that forwards your own token — so anything an agent can do here, a REST client can do too, under the same gates.

Shared parameters

Most tools accept these common parameters:

ParameterTypeRequiredDescription
workspace_idUUIDNoWhich workspace to query. Inferred if you have exactly one.
profile_idUUIDNoWhich profile to query. Inferred if the workspace has exactly one.
window_startYYYY-MM-DDYesStart of the date range
window_endYYYY-MM-DDYesEnd of the date range
modelstringNoAttribution model. Defaults to the profile's configured model — the one its dashboard uses.

Available attribution models: last_touch, first_touch, linear, time_decay, last_non_direct

The default is the profile's own setting, not last_touch. Omit model and the tool resolves whatever that profile is configured with, so an answer from an agent matches the number on the dashboard for the same question. Pass model explicitly only when you mean to compare — compare_attribution_models is built for that, and reports profile_configured_model so you can say which row the customer is looking at.

Every response reports the model it actually used, in meta.attribution_model and as attribution_model in the payload. Quote it when you narrate the number.

Two scope grains. Most tools are profile-scoped: they operate on one profile (workspace_id + profile_id, both inferred when unambiguous). The workspace creative-intelligence and recommendation tools below are workspace-scoped: they take only workspace_id and read across every profile you can access in that workspace. Workspace-scoped tools are marked with Scope: workspace. Instead of window_start/window_end, they use a pre-aggregated score_window (7d | 14d | 28d | lifetime, default 28d).


Tool reference

Every registered tool, generated from registry.ts so this table cannot drift from what the server actually serves. Scope, unit cost and the MCP annotations a generic host uses to decide whether a call needs confirmation. See each tool's own section below for its parameters and return shape.

ToolScopeCostRead-onlyDestructiveRequired providers
list_workspacesprofile0YesNo—
list_profilesprofile0YesNo—
whoamiprofile0YesNo—
get_performance_summaryprofile1YesNo—
compare_periodsprofile1YesNo—
top_campaignsprofile1YesNo—
top_ad_setsprofile1YesNometa_ads
top_creativesprofile1YesNometa_ads
top_workspace_performersworkspace1YesNo—
top_workspace_creative_patterns_v2workspace1YesNo—
top_workspace_archetype_summaryworkspace1YesNo—
top_workspace_experimentsworkspace1YesNo—
top_workspace_fatigue_riskworkspace1YesNo—
ad_funnel_diagnosisprofile1YesNo—
get_funnelprofile1YesNo—
get_tracking_breakdownprofile1YesNo—
get_attribution_qualityprofile1YesNo—
get_cash_attribution_coverageprofile1YesNo—
list_conversionsprofile1YesNo—
list_visitorsprofile1YesNo—
explain_campaignprofile2YesNo—
explain_customer_journeyprofile2YesNo—
compare_attribution_modelsprofile5YesNo—
creative_fatigue_checkprofile2YesNometa_ads
find_anomaliesprofile2YesNo—
whatsapp_attribution_summaryprofile2YesNowhatsapp
send_meta_conversionsprofile10NoYesmeta_ads
list_workspace_recommendationsworkspace1YesNo—
diagnose_recommendationworkspace1YesNo—
apply_recommendationworkspace10NoYesmeta_ads
top_dm_adsprofile1YesNo—
experiment_power_calcworkspace1YesNo—
workspace_historical_lift_bandworkspace1YesNo—
workspace_pattern_gapsworkspace1YesNo—
generate_creative_briefprofile5YesNo—
get_readinessprofile1YesNo—
start_plan_upgradeworkspace10NoNo—
create_workspaceprofile5NoNo—
create_profileworkspace5NoNo—
create_demo_profileworkspace5NoNo—
delete_demo_profileworkspace1NoYes—
issue_tracking_keyprofile1NoNo—
get_tracker_installerprofile1YesNo—
start_connectprofile1NoNo—
get_handoffprofile0YesNo—
list_outcome_eventsprofile2YesNo—
suggest_conversion_definitionsprofile2YesNo—
create_conversion_definitionprofile2NoNo—
set_attribution_windowsprofile2NoNo—
configure_meta_capiprofile2NoNo—
send_test_eventprofile10NoYes—
sign_dpaprofile1NoNo—

Discovery tools

whoami

Identity, usage, and effective settings for the current MCP token. Returns scopes, units used vs cap, your workspaces, and (when scope is unambiguous) active workspace settings (pii_mode, mcp_writeback_enabled, your role) and the active profile's currency. Always call once per session — it tells you upfront whether PII unmask or write-back will work, so the AI tool can guide the user correctly without trial-and-error calls.

ParameterTypeRequiredDescription
workspace_idUUIDNoOptional. If passed (or you have one workspace), active_workspace is populated.
profile_idUUIDNoOptional. If passed (or workspace has one profile), active_profile is populated.

Cost: 0 units

Returns: { token, usage, user, workspaces[], active_workspace, active_profile, server_version }. active_workspace and active_profile are null when scope is ambiguous.


list_workspaces

List all workspaces you have access to. Use the returned id values as workspace_id in subsequent calls.

ParameterTypeRequired
———

Cost: 0 units

Returns: Array of { id, name, role } objects.


list_profiles

List all profiles within a workspace. Use the returned id values as profile_id in subsequent calls.

ParameterTypeRequiredDescription
workspace_idUUIDNoInferred if you have one workspace

Cost: 0 units

Returns: Array of { id, name, workspace_id } objects.


get_readiness

Setup checklist for one profile: which steps of the golden path are done, blocked or not started, WHY each is in that state, and the exact next call or hand-off to unblock it. Read this before concluding that a profile has no data — an empty dashboard is usually a missing connection, not a missing result.

ParameterTypeRequiredDescription
workspace_idUUIDNoInferred if you have one workspace
profile_idUUIDNoInferred if the workspace has one profile

Cost: 1 unit

Returns: { steps[], summary: { done, total, next_step }, wizard_finished }. Each step is { key, status, why, next, docs_url }, where status is one of done / missing / degraded / blocked and next is either { method, path } (a call you can make now) or { handoff_kind } (something only a person can do: connect, sign_dpa, checkout, contact_support). Only done counts toward summary.done.

This tool calls the REST API as you

get_readiness is a thin wrapper on GET /api/v1/profile/readiness (#1084). It forwards your own token rather than running on Atribu's service role, so tenancy and scope are decided once, by the API — the same answer you would get calling that endpoint directly. It needs mcp:read. See API authentication for the MCP-scope → API-scope map.


Performance tools

get_performance_summary

Headline KPIs for a date window plus the equivalent prior period. Returns three buckets: cash (real revenue, drives ROAS), pipeline (CRM deal counts + raw value_amount sum — NOT revenue), and leads (top of funnel).

ParameterTypeRequiredDefault
window_startdateYes—
window_enddateYes—
modelstringNothe profile's configured model

Cost: 1 unit

Returns:

Example response (data, current_period)
{
  "spend": { "amount": 1032.42 },
  "cash": {
    "attributed_revenue": { "amount": 4000 },
    "attributed_conversions": 6,
    "total_conversions": 17,
    "collected": { "amount": 9749 },
    "roas": { "value": 3.87, "formatted": "3.87x" }
  },
  "pipeline": {
    "appointments_booked": 38,
    "closed_won": 0,
    "crm_value_amount_sum": {
      "attributed": { "amount": 76600 },
      "total": { "amount": 79600 }
    },
    "note": "Counts are concrete events. crm_value_amount_sum is the raw sum of whatever the agency entered in their CRM's deal value field — NOT revenue. Never present as a revenue claim without confirming the CRM convention."
  },
  "leads": { "count": 409, "note": "Top of funnel events with no revenue." },
  "traffic": { "visitors": 1550, "pageviews": 2386, "bounce_rate_percent": 87.3 }
}

Pipeline crm_value_amount_sum is NOT revenue

The CRM value_amount field means whatever the agency configured it to mean — deal target, customer lifetime value estimate, retainer size, or arbitrary. Use counts (appointments_booked, closed_won) as the primary signal. Only mention the sum with explicit context. Never include crm_value_amount_sum in ROAS calculations or call it "revenue" or "projected revenue".


compare_periods

Compare two arbitrary date windows side by side. Useful for year-over-year, pre/post campaign launch, or seasonal comparisons.

ParameterTypeRequiredDefault
window_startdateYes—
window_enddateYes—
prev_window_startdateYes—
prev_window_enddateYes—
modelstringNothe profile's configured model

Cost: 1 unit

Returns: Same structure as get_performance_summary with current and previous objects representing the two windows.


Campaign tools

top_campaigns

Top campaigns ranked by attributed revenue within a date window.

ParameterTypeRequiredDefault
window_startdateYes—
window_enddateYes—
modelstringNothe profile's configured model
limitnumberNo10 (max 50)

Cost: 1 unit

Returns: Array of campaigns with name, platform_id, spend, revenue, roas, conversions, clicks, impressions.

platform_id

Each campaign includes a platform_id which you can pass to explain_campaign for a deep dive.


top_ad_sets

Top ad sets ranked by attributed cash revenue. Sits between top_campaigns (broader) and top_creatives (narrower) — answers "which audience/placement is delivering?" before drilling into individual creatives.

ParameterTypeRequiredDefault
window_startdateYes—
window_enddateYes—
modelstringNothe profile's configured model
limitnumberNo10 (max 50)

Cost: 1 unit

Requires: Meta Ads connection

Returns: Array of ad sets with ad_set_name, campaign_name (parent), spend, attributed_revenue, roas, attributed_conversions, cac, ctr_percent, impressions, clicks, avg_engagement_score.


top_creatives

Top individual ads ranked by ROAS, with creative details (thumbnail URLs, headlines, body text).

ParameterTypeRequiredDefault
window_startdateYes—
window_enddateYes—
modelstringNothe profile's configured model
limitnumberNo10 (max 50)

Cost: 1 unit

Requires: Meta Ads connection

Returns: Array of ads with name, spend, revenue, roas, ctr, thumbnail_url, headline, body.


explain_campaign

Deep dive into a single campaign: headline metrics, daily trend, and constituent ads.

ParameterTypeRequiredDefault
campaign_idstringYes—
window_startdateYes—
window_enddateYes—
modelstringNothe profile's configured model

Cost: 2 units

The campaign_id can be a UUID (internal ID), a Meta platform_id, or a campaign name. The tool resolves it automatically.

Returns:

Example response (data)
{
  "summary": {
    "name": "Summer Sale - Retargeting",
    "platform_id": "23851234567890",
    "spend": 1200.00,
    "revenue": 5400.00,
    "roas": 4.50,
    "conversions": 28,
    "clicks": 1840,
    "impressions": 42000
  },
  "daily_trend": [
    { "date": "2026-04-01", "spend": 180, "revenue": 720, "clicks": 260 }
  ],
  "ads": [
    { "name": "Video - Testimonial", "spend": 600, "revenue": 3200, "roas": 5.33 }
  ]
}

Customer tools

explain_customer_journey

Full event timeline for a single customer: ad clicks, page views, form fills, conversions, and payments.

ParameterTypeRequiredDefault
customer_profile_idUUIDYes—
include_sensitivebooleanNofalse
cursor_timestringNo—
cursor_idUUIDNo—
limitnumberNo50 (max 100)

Cost: 2 units

Pagination is cursor-based: pass the cursor_time and cursor_id from the previous response's next_cursor to get the next page. There is no date-window filter — the tool walks the customer's full timeline.

Returns: Customer info (name, masked email/phone) plus an array of timeline events ordered chronologically.

PII masking

By default, email and phone are masked (e.g. j***@e****.com). To see unmasked values, pass include_sensitive: true -- requires mcp:read_pii scope and workspace PII mode set to full_default. See Privacy & PII.


Analysis tools

compare_attribution_models

Run the same date window through multiple attribution models to see how credit distribution shifts.

ParameterTypeRequiredDefault
window_startdateYes—
window_enddateYes—
modelsstring[]No["last_touch", "first_touch", "linear", "time_decay", "last_non_direct"]
top_campaigns_limitnumberNo5 (max 20)

Cost: 5 units

Returns: One result set per model, each with the same campaign-level metrics. Compare to understand which channels are undervalued by single-touch models.


creative_fatigue_check

Detect ads showing fatigue signals: declining CTR, rising CPM, or audience saturation compared to the prior equal-length window.

ParameterTypeRequiredDefault
window_startdateYes—
window_enddateYes—
modelstringNothe profile's configured model

Cost: 2 units

Requires: Meta Ads connection

Returns: Array of ads with current vs prior period metrics and fatigue indicators.


find_anomalies

Flag unusual daily spikes or drops in spend, revenue, or traffic using z-score analysis (median + MAD).

ParameterTypeRequiredDefault
window_startdateYes—
window_enddateYes—
metricsstring[]No["spend", "cash_revenue", "visitors"]
threshold_sigmanumberNo2.0
modelstringNothe profile's configured model

Cost: 2 units

Returns: Array of anomalous days with the metric name, observed value, expected range, and z-score magnitude.


get_funnel

Goal-driven conversion funnel for the date window. Walks the canonical lead-gen progression lead_created → appointment_booked → showed → qualified → closed_won with per-stage counts, stage-to-stage conversion rates, and drop-off counts. Also returns negative-outcome buckets (disqualified, no_show, closed_lost).

ParameterTypeRequiredDefault
window_startdateYes—
window_enddateYes—
scopeall | pipeline_only | contact_onlyNoall

Cost: 1 unit

scope=pipeline_only restricts to events linked to a CRM pipeline (deals in a stage). scope=contact_only restricts to events without one (lead-only / form fills).

Returns:

Example response (data)
{
  "scope": "all",
  "window": { "start": "2026-04-01", "end": "2026-04-30" },
  "main_chain": [
    { "key": "lead_created", "label": "Leads", "count": 409, "conversion_rate_from_previous_pct": null, "drop_off_from_previous_count": 0 },
    { "key": "appointment_booked", "label": "Booked", "count": 38, "conversion_rate_from_previous_pct": 9.29, "drop_off_from_previous_count": 371 },
    { "key": "showed", "label": "Showed", "count": 24, "conversion_rate_from_previous_pct": 63.16, "drop_off_from_previous_count": 14 },
    { "key": "qualified", "label": "Qualified", "count": 12, "conversion_rate_from_previous_pct": 50.00, "drop_off_from_previous_count": 12 },
    { "key": "closed_won", "label": "Closed Won", "count": 0, "conversion_rate_from_previous_pct": 0, "drop_off_from_previous_count": 12 }
  ],
  "drop_off_outcomes": [
    { "key": "disqualified", "label": "Disqualified", "count": 7 },
    { "key": "no_show", "label": "No Show", "count": 5 },
    { "key": "closed_lost", "label": "Closed Lost", "count": 3 }
  ],
  "totals": {
    "all_events": 498,
    "leads": 409,
    "closed_won": 0,
    "lead_to_closed_won_rate_pct": 0
  }
}

Counts are events, not unique customers

One customer can appear at multiple stages. Don't compute customer-level rates from these event counts. Stage-to-stage conversion rates assume customers progress linearly — in reality customers can skip stages or be re-classified.

Stages only show events that are also conversions

Counts include only events whose event_key is mapped under Settings → Outcomes → Conversion Definitions. If a CRM stage like disqualified shows 0 but your team uses it (e.g. labels "Descualificado", "Lead Abandonado"), the conversion definition is missing. Map the key in Conversion Definitions to make those events appear in the funnel.


get_tracking_breakdown

Top values per traffic-dimension (channel, country, page, browser, device, OS, referrer, campaign, etc.) ranked by visitors. Returns up to 6 dimensions in one call.

ParameterTypeRequiredDefault
window_startdateYes—
window_enddateYes—
dimensionsstring[]No["channel", "country", "page", "browser"]
limitnumberNo10 (max 50)

Available dimensions: channel, referrer, campaign, page, entry_page, exit_page, country, region, city, device, browser, os

Cost: 1 unit

Returns:

Example response (data)
{
  "breakdowns": {
    "channel": [
      { "dimension_value_key": "paid_social", "label": "Paid Social", "visitors": 820, "visits": 1120, "pageviews": 2400, "bounce_rate_pct": 62.4, "conversions": 14, "conversion_rate_pct": 1.71, "cash_revenue": { "amount": 3200 } }
    ],
    "country": [
      { "dimension_value_key": "US", "label": "United States", "visitors": 540, "visits": 720, "pageviews": 1820, "bounce_rate_pct": 58.1, "conversions": 12, "conversion_rate_pct": 2.22, "cash_revenue": { "amount": 2800 } }
    ]
  },
  "window": { "start": "2026-04-01", "end": "2026-04-30" },
  "dimensions_requested": ["channel", "country"],
  "limit_per_dimension": 10
}

Revenue is cash only

The cash_revenue field uses revenue_type='cash' only — same definition the dashboard uses for ROAS. Pipeline / CRM deal values are NOT included here.


list_conversions

Paginated list of conversions for a date window, ordered by conversion_time DESC. Each row carries the customer (masked PII by default), first-touch channel, time-to-complete, revenue, plus the customer's lifetime totals.

ParameterTypeRequiredDefault
window_startdateYes—
window_enddateYes—
goalstringNopayment_received
searchstringNo—
cursor_timestringNo—
cursor_idstringNo—
limitnumberNo25 (max 100)
include_sensitivebooleanNofalse

Cost: 1 unit

goal filters by event_type (e.g. payment_received, lead_created, appointment_booked, closed_won). Pass all to include every conversion type. Use next_cursor from the response for the next page.

For a SINGLE customer's full event timeline, use explain_customer_journey instead.


list_visitors

Paginated visitor roster (identified + anonymous) for a date window, ordered by last_seen_at DESC. Each row carries last-seen timestamp, session count, total cash revenue, channel/source from the latest session, and device/geo.

ParameterTypeRequiredDefault
window_startdateYes—
window_enddateYes—
searchstringNo—
cursor_timestringNo—
cursor_idstringNo—
limitnumberNo25 (max 100)
include_sensitivebooleanNofalse

Cost: 1 unit

is_identified=true means the visitor has a customer_profile_id (matched via identify() or a server-side event). False means anonymous-only. total_revenue is cash only (revenue_type='cash') and reflects the customer's lifetime sum.


get_attribution_quality

Diagnostic — how trustworthy is attribution data for this date window? Returns the share of conversion-related events with full UTMs (best), with fbclid only (Meta click but no UTMs), and with no tracking at all (worst).

ParameterTypeRequiredDefault
window_startdateYes—
window_enddateYes—

Cost: 1 unit

Returns:

Example response (data)
{
  "window": { "start": "2026-04-01", "end": "2026-04-30" },
  "totals": {
    "total_events": 409,
    "with_full_utms": 248,
    "with_fbclid_only": 102,
    "with_no_tracking": 59
  },
  "percentages": {
    "full_utms_pct": 60.6,
    "fbclid_only_pct": 24.9,
    "no_tracking_pct": 14.4,
    "attributable_coverage_pct": 85.5
  }
}

Heuristic

attributable_coverage_pct above 80% is healthy. Between 50–80% means attribution numbers should be treated cautiously. Below 50% indicates UTM tagging gaps — recommend an audit before trusting campaign-level reports.


get_cash_attribution_coverage

Honesty check: what share of this profile's CASH actually traces to a specific ad? Splits cash conversions into mutually-exclusive buckets by count and value — ad_attributed (credited to a navigable ad), inherited (recovered via the lead→cash bridge — real ad-driven cash, but a distinct, lower-confidence tier), dangling_ad (credited to an ad id that resolves to no known ad — a fixable tracking gap), organic_direct (has touches but no ad signal), unattributed (no ad touch at all — the dark cash).

ParameterTypeRequiredDefault
window_startdateYes—
window_enddateYes—
modelstringNoProfile's default model

Cost: 1 unit

Returns: total and buckets.{ad_attributed,inherited,dangling_ad,organic_direct,unattributed} (each {count, value}, value an exact decimal string), traceable_pct_by_count, traceable_pct_by_value, notes[] explaining each bucket.

A fourth kind of number

This is a COVERAGE measure — distinct from composite_score, top_performer_likelihood, and attributed_revenue/roas (see top_workspace_performers below). It is neither a score, a probability, nor ROAS. Call it before trusting ROAS if cash coverage looks low: a low traceable_pct means either strong organic performance OR broken checkout instrumentation, and the two demand opposite responses. inherited cash is real ad-driven revenue but is never counted in traceable_pct or ROAS — it is a separate, lower-confidence tier.


ad_funnel_diagnosis

Per-ad funnel diagnosis — "where in this ad's funnel is the problem?" Returns one row per funnel layer (delivery → attention → retention → click_intent → postclick_messaging → attributed_revenue → platform_diagnostics, the same layers the composite score is built from).

ParameterTypeRequiredDefault
profile_idUUIDNoInferred if the workspace has exactly one
ad_external_idstringYes—
score_window7d | 14d | 28d | lifetimeNo28d

Cost: 1 unit

Returns: Per layer: the ad's percentile within its cohort, the underlying smoothed_metric, sample_n, is_weakest_layer (the bottleneck — lowest percentile when below the 25th), machine-readable diagnostic_codes[], and a recommended_priority — one of fix_offer_or_audience, fix_landing_or_offer, fix_hook, improve_creative_clarity, increase_spend, rework_offer_or_landing, or maintain.

Daily freshness

meta.data_as_of reflects the daily creative-score rebuild, not the connector sync time — funnel percentiles update once per day even when Meta data synced minutes ago.


Messaging tools

whatsapp_attribution_summary

Revenue and conversions attributed to WhatsApp touchpoints (Click-to-WhatsApp ads or WhatsApp-sourced traffic).

ParameterTypeRequiredDefault
window_startdateYes—
window_enddateYes—

Cost: 2 units

Requires: WhatsApp integration

Returns: Conversation counts, conversion counts, revenue attributed to WhatsApp touchpoints.


top_dm_ads

Messaging-ad (DM Ads) leaderboard for a profile, ranked by cost per high-intent conversation. Covers Click-to-Message channels: ig_ctm, wa_ctwa, lead_dm.

ParameterTypeRequiredDefault
profile_idUUIDYes—
date_fromYYYY-MM-DDNo28 days ago
date_toYYYY-MM-DDNotoday
sort_bystringNocost_per_high_intent (high_intent_rate, composite_score, spend, high_intent_conversations)
limitnumberNo10 (max 50)

Cost: 1 unit

Returns: Array of ads with high_intent_conversations (pricing / consultation / demo intents), high_intent_rate + high_intent_band (Wilson sample confidence: low / medium / high), cost_per_high_intent (the hero metric), cost_per_conversation, reply-depth rates, attributed revenue/ROAS, booked appointments, closed deals, data_quality_tier, intent breakdown, and any open recommendations targeting the ad. PII in sample conversations is server-side redacted.


Workspace creative intelligence

The tools in this section (and the recommendation section below) are workspace-scoped: pass workspace_id (inferred when you have exactly one) and they read across every profile you can access in the workspace. They are backed by the creative feature store, which is re-scored daily — meta.data_as_of reflects the last workspace re-score, not connector sync time.

top_workspace_performers

The best-performing ads across ALL client profiles in a workspace, scored against comparable creative (cohort-normalized, smoothed, maturity-staged).

ParameterTypeRequiredDefault
score_window7d | 14d | 28d | lifetimeNo28d
limitnumberNo10 (max 50)
profile_idsUUID[]No—
objectivesstring[]No—
truth_gradesstring[]No— (predicted, attributed, lift)
outcome_kindsstring[]No— (cash, pipeline, messaging, meta, none)
formatsstring[]No—
min_spendnumberNo—
has_videobooleanNo—
fatigue_risk_tiersstring[]No— (low, medium, high, critical)
fatigue_statesstring[]No— (active, paused, degraded)

Cost: 1 unit

Scope: workspace

Returns: Each ad carries three distinct measures — never merge them:

  1. composite_score (0–100) — a transparent rule-based blend of cohort percentiles
  2. top_performer_likelihood (0–1) — a probability (a likelihood, not a guarantee): the calibrated ML-ranker output when score_source='model', otherwise derived from composite_score. Never ROAS.
  3. attributed_revenue / roas — real cash attribution (present when truth_grade='attributed')

Plus primary_outcome_kind (which metric to headline per ad: cash → revenue/ROAS, pipeline → attributed pipeline outcomes, messaging → conversations started, meta → Meta's own reported conversions — always labeled as Meta's attribution, none → composite score only), maturity_stage (cold → early → mature → calibrated), reason_codes[] explaining why the ad ranks, and a fatigue block (NULL until the prediction pass has scored the ad). There is no forecast block: Atribu does not predict an ad's next week, and the fields that used to claim it were NULL on every row (#1493).


top_workspace_creative_patterns_v2

Workspace-level creative pattern miner: which structural patterns (archetype cluster, narrative arc, dominant-third role combo, role presence, social-proof duration bucket, hook×claim×CTA) win MORE than the workspace baseline.

ParameterTypeRequiredDefault
score_window7d | 14d | 28d | lifetimeNo28d
min_cluster_sizenumberNo3 (min 3, max 50)
profile_idsUUID[]No—
objectivesstring[]No—
truth_gradesstring[]No—
outcome_kindsstring[]No—
formatsstring[]No—
min_spendnumberNo—
has_videobooleanNo—

Cost: 1 unit

Scope: workspace

Returns: One row per (pattern_dim, pattern_value) with sample_n, winner_n, win_rate, workspace_baseline_win_rate, lift_vs_workspace, Wilson 95% confidence-interval bounds, and exemplar_ad_external_ids (top 5 ads embodying the pattern). A pattern whose confidence interval straddles the baseline is sampling noise, not signal. This is a pattern-level win-rate signal, NOT cash ROAS — pair with top_workspace_performers for per-ad cash impact.


top_workspace_archetype_summary

The workspace's creative archetype clusters — named via LLM from exemplars — ranked by lift over the workspace baseline. Use as the "replicate these patterns" headline; use top_workspace_creative_patterns_v2 for finer-grain dimensions.

ParameterTypeRequiredDefault
score_window7d | 14d | 28d | lifetimeNo28d

Cost: 1 unit

Scope: workspace

Returns: One row per archetype cluster with label, description, winning_signal, n_ads, n_winners, win_rate, workspace_baseline_win_rate, lift_vs_workspace, Wilson 95% CI bounds, and top-5 exemplar ads.


top_workspace_experiments

Meta Conversion-Lift / Split-Test experiments tracked for the workspace.

ParameterTypeRequiredDefault
experiment_idUUIDNo— (pass to get the full detail document for one experiment)

Cost: 1 unit

Scope: workspace

Returns (list mode): One row per experiment with type (LIFT, CONTINUOUS_LIFT_CONFIG, GEO_LIFT, SPLIT_TEST, SPLIT_TEST_V2), derived_state (scheduled / running / observing / results_pending / complete / canceled — derived from Meta timestamps), best_grade (A/B/C), and the primary lift point estimate + CI bounds. Detail mode (with experiment_id) adds cells, objectives, latest results, and the top-10 modeled creative attributions.

Lift is cell-level; split tests are Atribu-computed

Meta returns lift only at the cell level — per-creative lift is always modeled. SPLIT_TEST results are computed by Atribu from its own fact tables (Meta exposes no results endpoint for split tests).


top_workspace_fatigue_risk

Which ads are most likely to pause in the next 30 days, ranked by hazard_30d_pause × spend — the budget at risk if the ad pauses. Only active ads in high / critical fatigue tiers appear.

ParameterTypeRequiredDefault
score_window7d | 14d | 28d | lifetimeNo28d
top_nnumberNo20 (max 50)
min_spendnumberNo100 (pass 0 to include all)

Cost: 1 unit

Scope: workspace

Returns: Ranked ads with hazard_30d_pause and hazard_30d_degradation (Cox survival-model 30-day probabilities — likelihoods, not deterministic timers), expected_lifespan_days, fatigue_risk_tier, plus workspace totals total_budget_at_risk (spend across the listed ads) and total_expected_pause_loss_30d (sum of hazard × spend — a distinct quantity, don't conflate the two).


workspace_pattern_gaps

Cross-profile pattern-coverage analysis: for each winning workspace pattern, which profiles have NO ad expressing it. Answers "what test should I run next?" with concrete cross-profile replication opportunities.

ParameterTypeRequiredDefault
score_window7d | 14d | 28d | lifetimeNo28d
limitnumberNo10 (max 50)
min_liftnumberNo0.05 (minimum lift_vs_workspace to qualify; 0–2)

Cost: 1 unit

Scope: workspace

Returns: Rows ordered by lift_vs_workspace DESC, each with pattern_dim / pattern_value, the lift + CI lower bound, source_profile_ids (where the pattern lives), gap_profile_ids (profiles missing it), and exemplar ads.


workspace_historical_lift_band

Historical lift distribution from graduated Conversion-Lift / Split-Test experiments in the workspace. Anchors expectations before committing to a new lift study.

ParameterTypeRequiredDefault
profile_idUUIDNo— (narrow to one client's history)
lookback_daysnumberNo365 (min 30, max 1095)

Cost: 1 unit

Scope: workspace

Returns: min / p25 / p50 / p75 / max of relative_uplift (% lift over the comparison cell) across experiments with derived_state complete or results_pending, plus the 5 most recent exemplars. Returns n_experiments: 0 when there's no graduated history.


experiment_power_calc

Statistical power calculator for a Conversion-Lift study (one cell + holdout). Pure math — reads no data.

ParameterTypeRequiredDefault
baseline_ratenumberYes— (0 < p < 1, e.g. 0.024 for 2.4%)
daily_spendnumberYes—
cost_per_samplenumberYes—
holdout_pctnumberNo0.2 (0.05–0.5)
days_to_runnumberNo14 (1–180)

Cost: 1 unit

Scope: workspace

Returns: The minimum detectable effect (MDE) at 80% power / two-sided α = 0.05, days-to-read, 0.5× and 2× spend comparison scenarios, and a recommendation (keep spend / double spend / cohort too sparse for this design).


Recommendation tools

list_workspace_recommendations

The AI media buyer's recommendations across all profiles in a workspace, ranked by expected impact.

ParameterTypeRequiredDefault
profile_idsUUID[]No—
statusesstring[]No["open"] (open, applied, dismissed, superseded, expired, rolled_back)
kindsstring[]No— (scale_winner, pause_underperformer, budget_reallocate_winners, creative_refresh_pre_fatigue)
risk_tiersstring[]No— (safe, medium, manual_only). Accepted but not applied by the read today; filter on the returned risk_tier instead.
cohort_objectivestringNo— (messaging, sales, leads, traffic, awareness)
score_window7d | 14d | 28d | lifetimeNo28d
limitnumberNo20 (max 100)

Cost: 1 unit

Scope: workspace

Returns: Recommendations with kind, risk_tier (safe = auto-applicable if the workspace opted in, medium = always requires explicit confirm, manual_only = no Meta call), ad_name (render this, never target.ad_id), structured rationale_jsonb — the cash kinds carry window_days, attributed_cash_outcomes (cash SALES, not leads), cost_per_cash_outcome, profile_median_cost_per_cash_outcome, cost_delta_pct, spend, currency — plus an optional rationale_text commentary line, suggested_modifications (action + params; quote params.budget_change_pct, never a literal), expected_impact_dollars (arithmetic on the ad's own observed unit economics, phrased with "if" — not a forecast), confidence (0–1), and lifecycle timestamps. A recommendation ATTESTS: it exists because the ad already closed at least 3 attributed sales below its account's median cost per sale.


diagnose_recommendation

Full diagnostic for a single recommendation — "what happened with this recommendation?" Use before apply_recommendation to understand prior attempts.

ParameterTypeRequiredDefault
recommendation_idUUIDYes—

Cost: 1 unit

Scope: workspace

Returns: The recommendation row (kind / risk tier / status / rationale / suggested modifications / target / impact / confidence / lifecycle), the most recent application if any (with pre-change and post-change Meta state, the Meta API result, verification and rollback timestamps), and the related write-back audit chain, most recent first.


apply_recommendation

Apply a recommendation against Meta. This is a write tool with the same three-step safety flow and gates as send_meta_conversions — see the Write-Back guide.

ParameterTypeRequiredDefault
recommendation_idUUIDYes—
modepreview | dry_run | confirmYes—
idempotency_keystringNoAuto-derived per (user, recommendation, day) when omitted
applied_byUUIDNo—

Cost: 10 units

Scope: workspace

Requires (for dry_run and confirm): mcp:write scope, workspace write-back enabled, workspace owner/admin role. preview needs none of these. confirm additionally requires the recommendation to be open and unexpired.

preview shows what the worker would do (target ad/ad-set, planned Meta calls, budget % delta) with no side effects. dry_run records an audit row capturing intent. confirm enqueues the job for the pipeline worker, which captures pre-change Meta state, executes the write, and verifies the change landed ~5 minutes later. Replaying confirm with the same idempotency key returns the existing application — no double-write.


Creative generation

generate_creative_brief

Generate an OPINIONATED creative brief for one profile's next ad batch — grounded in that profile's own Top Performers + winning creative patterns, and (when the marketingskills reference frameworks are available on the server) the ad-creative/copywriting/paid-ads sections. Applies the 70/20/10 rule: 70% of the returned briefs are proven (a concrete angle lifted from an ad already winning for this profile, restated as fresh creative), 20% are adjacent (a deliberate variation on a proven winner), 10% are bet (a genuinely new angle not tied to an existing winner).

ParameterTypeRequiredDefault
objectivecash | pipeline | messaging | metaNoThe profile's own headline outcome kind — its top-ranked ad's primary_outcome_kind (falls back to cash if there are no ads yet)
countnumberNo3 (1–5)
formatvideo | image | carousel | anyNoany
constraintsstringNo— (free text, max 500 chars — brand voice, compliance, exclusions; respected verbatim)

Cost: 5 units, plus one LLM generation call charged against the workspace's shared monthly AI budget — the same budget the in-app AI copilot and the daily morning brief draw from.

Scope: profile

Returns: briefs[], each carrying bucket (proven | adjacent | bet), hook, angle, format, primary_text, headline, cta, grounded_in (the ad(s) it derives from, by ad_external_id/ad_name), and why — a one-sentence citation computed from the cited ad's own numbers (composite_score, top_performer_likelihood, headline outcome), never the model's own claim. bucket_counts shows the actual 70/20/10 split used (largest-remainder apportionment: count=3 → 2 proven/1 adjacent/0 bet, count=5 → 3/1/1 — ties break toward the smaller bucket so the 10% "bet" allocation is never rounded away). frameworks_used[] lists which marketingskills sections grounded the call; when the submodule isn't available on the server, frameworks_used is empty and degraded_reason explains why — the brief still generates, grounded in Top Performers data alone.

No images, no video, no Meta writes — copy only. Errors data_unavailable if the AI provider isn't configured, or rate_limited if the workspace's monthly AI budget is exhausted (resets next month, or a workspace admin can raise the cap).

Example call:

{
  "tool": "generate_creative_brief",
  "arguments": {
    "profile_id": "0f2a1c3e-...",
    "count": 3,
    "format": "video",
    "constraints": "No before/after imagery; Spanish only."
  }
}

Onboarding tools

The golden path from an empty account to attributed revenue, one tool per step, in the order you call them. Every tool here is a thin wrapper on one /api/v1 route: it forwards your own MCP token, and tenancy, scopes and the audit trail are decided once, by the API. There is no second implementation of any of them.

Three things to know before you use any of them.

  1. Every write is preview then confirm. preview describes the exact call it would make — route, body, and where the API can answer it, a real dry-run count — and has no side effect and no gates. confirm performs it. Always preview, show the user, then confirm. mode is required: there is no safe default.
  2. Writes need three things, not one. The token's mcp:write scope, the workspace's MCP write-back setting (Settings → Privacy & MCP, off by default), and an owner/admin role. A missing scope answers insufficient_scope; a missing role answers insufficient_role — a different code because re-consenting cannot raise a role. Reads and previews keep working regardless.
  3. The hand-off pattern is how a human does their part. Some steps only a person can take: granting an OAuth consent, signing a DPA, paying. For those you mint a hand-off, give the user the url verbatim, and poll get_handoff({ id }) until status leaves pending. You never open the URL yourself.

Start with whoami

whoami now returns a readiness summary — how many golden-path steps are done, which is next, and every step that is not done with the reason. It costs 0 units. When a read comes back empty, the answer is almost always there: a missing connection, not a missing result.

create_workspace

Create a new Atribu workspace owned by you. The first call when whoami shows no workspaces.

ParameterTypeRequiredDescription
modepreview | confirmYes—
namestringYes1–80 characters
timezonestringNoIANA zone; defaults to UTC
idempotency_keystringNoDerived from the payload when omitted

Cost: 5 units · Wraps: POST /api/v1/workspaces · Scope: mcp:write

The only write with a relaxed gate

This is the one write that does not require workspace write-back or an admin role: both live on a workspace, and this call creates one. You become its owner. Since your token needs mcp:write just to call this, the new workspace starts with write-back already on — call create_profile right after with no settings step in between.

A replay of the same name by the same caller within a short window returns the SAME workspace with created: false rather than minting a second one; a different name, or the same name once the window has passed, always creates.

create_profile

Create a Profile — one advertiser, brand or client inside a workspace, and the unit every other tool is scoped to.

ParameterTypeRequiredDescription
modepreview | confirmYes—
workspace_idUUIDNoInferred if you have one workspace
namestringYes—
timezonestringNoDefaults to the workspace's
currencystringNoISO-4217; defaults to USD
site_domainstringNoWhere the tracker will run

Cost: 5 units · Wraps: POST /api/v1/profiles · Requires: mcp:write, write-back enabled, owner/admin

The plan's active_profiles limit is enforced by the API, so a refusal here may be a billing answer rather than a permission one. Read the message.

create_demo_profile

Seed a fully-populated demo profile: ad spend, touches, leads, appointments, payments and a recording CAPI destination — so every read tool has real shapes to return before the customer has connected anything.

ParameterTypeRequiredDescription
modepreview | confirmYes—
workspace_idUUIDNoInferred if you have one workspace
timezonestringNo—
currencystringNoISO-4217; defaults to USD

Cost: 5 units · Wraps: POST /api/v1/profiles/demo · Requires: mcp:write, write-back enabled, owner/admin

Exactly one demo per workspace: a second confirm re-seeds it and answers created: false. Its exports are recorded, never sent to Meta.

delete_demo_profile

Delete the workspace's demo profile and everything seeded under it. Irreversible, and marked destructiveHint. Only a profile flagged is_demo is reachable from here — a real profile never is.

ParameterTypeRequired
modepreview | confirmYes
workspace_idUUIDNo

Cost: 1 unit · Wraps: DELETE /api/v1/profiles/demo · Requires: mcp:write, write-back enabled, owner/admin

issue_tracking_key

Mint a trk_live_ tracking key — what the browser tracker authenticates with.

ParameterTypeRequiredDescription
modepreview | confirmYes—
namestringNoLabel, e.g. Main site
idempotency_keystringNoA repeated confirm with the same key returns the FIRST key rather than minting a second

Cost: 1 unit · Wraps: POST /api/v1/tracking/keys · Requires: mcp:write (→ tracking:write), write-back enabled, owner/admin

The public key is not a secret: the console's own installers already embed the identical value in a <script> tag that ships to every visitor.

get_tracker_installer

The ready-to-paste install for one of three surfaces. Read-only — it renders an installer, a human still pastes it.

ParameterTypeRequiredDescription
surfacesnippet | gtm | shopify_pixelNoDefault snippet. Ask the customer how their site is built
key_idstringNoThe profile's active key when omitted
domain_idstringNo—
include_meta_pixelbooleanNosnippet only

Cost: 1 unit · Wraps: GET /api/v1/tracking/snippet, GET /api/v1/tracking/installers/gtm, GET /api/v1/tracking/installers/shopify-pixel

Give the output to the user verbatim. Re-typing a script tag introduces a typo they then have to debug.

start_connect

Start an OAuth connection and get a URL to hand to your user. You cannot complete this — a consent needs a browser and a person.

ParameterTypeRequiredDescription
modepreview | confirmYes—
providermeta_ads | google_ads | google_search_console | gohighlevel | stripe | mercadopagoYesAsk which platform they actually advertise on. shopify is not offered — a Shopify install begins inside Shopify, so there is no consent Atribu can start for the merchant
return_toURLNoWhere to send them afterwards

Cost: 1 unit · Wraps: POST /api/v1/connections/{provider}/handoff · Requires: mcp:write, write-back enabled, owner/admin

The flow: confirm → give the user url exactly as it is → poll get_handoff({ id }) → read result. The link is session-less (they do not need an Atribu account, and it works on a phone), single-use, and expires in ~45 minutes — mint it when the person is ready rather than in advance; minting twice is two hand-offs, not an error.

On completed, result.connection_id names the connection that was written — unless result.pending_selection is set, which means the consent exposed several accounts and a human still owes a choice (GET /api/v1/connections/pending/{provider} then POST …/finalize). On failed, result.reason is provider_denied, no_candidates (not retryable — they must be granted access at the provider first) or origin_not_allowed.

Shopify is not offered, and never will be

The six providers above are the route's whole enum. A Shopify install begins inside Shopify — the App Store listing, HMAC-verified — so there is no consent Atribu can start on the merchant's behalf, and a hand-off for it could never be completed. The tool rejects shopify at parse time rather than letting the API 400.

get_handoff

Poll a hand-off you gave a human. Works for every kind — connect, pick, sign_dpa, checkout, approve — because they all settle the same way.

ParameterTypeRequiredDescription
idUUIDYesThe id the minting tool returned — not the handle the URL carries

Cost: 0 units · Wraps: GET /api/v1/handoffs/{id}

Returns: { id, kind, status, url, expires_at, created_at, completed_at, result, still_pending, note }. status is pending until they finish, then completed / expired / cancelled / failed.

An expired hand-off answers status: "expired", never a 404 — so a 404 here means the id is wrong, not that the link lapsed. Poll no faster than every few seconds; do not mint a second hand-off while one is pending.

list_outcome_events

Which raw outcome_events event types this profile has actually received in a window, and how many of each.

ParameterTypeRequired
date_fromYYYY-MM-DDYes
date_toYYYY-MM-DDYes

Cost: 2 units · Wraps: GET /api/v1/goals/outcome-counts

The first call before proposing any conversion definition: it tells you what the customer's CRM and tracker really emit, rather than what they are called in a brochure. An empty result means nothing is arriving — check get_readiness before assuming the window is wrong.

suggest_conversion_definitions

Atribu's own proposal for this profile's conversion definitions, derived from the events it has received — including a suggested revenue_type, whether the conversion should be attribution-eligible, and the Meta event it maps to.

ParameterTypeRequiredDescription
daysnumberNoLookback; outcome_events is durable, so a year is allowed
min_eventsnumberNoIgnore event types below this count
limitnumberNoMaximum suggestions

Cost: 2 units · Wraps: GET /api/v1/goals/definitions/suggestions

Propose from this, show the user, create what they confirm. Do not invent a definition when this returns nothing.

create_conversion_definition

Create a conversion_definitions row — what this profile counts as a conversion, what kind of revenue it carries, and how far back a touch may be credited.

ParameterTypeRequiredDescription
modepreview | confirmYes—
conversion_keystringYesEquals conversions.conversion_type; taken verbatim
display_namestringYes—
source_event_namesstring[]YesFrom list_outcome_events; must not overlap another definition
revenue_typecash | pipeline | grossYesSee the callout
attribution_eligiblebooleanYes—
lookback_window_daysnumberYes1–365
is_defaultbooleanNoMake it the headline conversion

Cost: 2 units · Wraps: POST /api/v1/goals/definitions/preview, POST /api/v1/goals/definitions · Requires: mcp:write (→ goals:write), write-back enabled, owner/admin

preview runs the API's own dry run and returns how many events and how much revenue the definition would match, so you can show a real number before writing.

Only cash counts for ROAS

If the customer said "sales" or "revenue", they mean cash. pipeline is forecast value and gross is pre-refund; a definition created as pipeline fills the funnel and contributes nothing to any ratio — a dashboard of zeroes, with no error anywhere.

set_attribution_windows

Read or change this profile's attribution windows. A partial update: an omitted field is left untouched.

ParameterTypeRequiredDescription
modepreview | confirmYespreview is also how you simply read them
click_window_daysnumberNo1–365. Default 30
view_window_hoursnumberNo1–168. Default 24
first_touch_window_daysnumberNo1–365. Default 90
cash_window_daysnumber | nullNoCash-only override; null clears it

Cost: 2 units · Wraps: GET/PATCH /api/v1/profile/attribution-settings · Requires: mcp:write, write-back enabled, owner/admin

This rewrites history

A window decides which touches attach to which conversion, so a save queues a full-profile replay and already-reported numbers change. The result carries replay_queued; when it is false, the numbers still reflect the old windows until a replay runs, and the tool says so.

sign_dpa

Get a signing link for the Data Processing Agreement — or the combined DPA & HIPAA BAA — and hand it to your user. Conversion Sync will not export anything until the DPA is accepted, so this usually comes before configure_meta_capi.

ParameterTypeRequiredDescription
modepreview | confirmYes—
documentdpa | baaNoDefault dpa. baa is the combined DPA & HIPAA BAA click-wrap that Healthcare Mode requires — ask before choosing it, it is a bigger commitment rather than a superset anyone wants by default

Cost: 1 unit · Wraps: POST /api/v1/legal/dpa/handoff, POST /api/v1/legal/baa/handoff · Requires: mcp:write (→ exports:write), write-back enabled, owner/admin

Already signed is a success, not a failure. A document that has already been accepted comes back already_signed: true, status: "completed" and no url. Say so and move on — re-running a checklist must never ask a customer to sign twice, and no second signature is collected.

You cannot sign it, and that is deliberate

There is a second route, POST /api/v1/legal/{document}/accept, that records an acceptance directly. This tool does not wrap it, and the API refuses an MCP token there with a 403 on purpose: your token names a real person, but it is the credential they minted for an agent — so accepting with it is an agent signing on their behalf. A signature an agent could produce is worth nothing, which is the entire reason the hand-off exists. Give the human the url and poll get_handoff({ id }).

Two documents, two routes

dpa and baa are separate routes, not a body field — the document is the path segment, and the signer's identity comes from whoever opens the URL rather than from the caller. Pick baa only when the customer runs Healthcare Mode.

configure_meta_capi

Configure Meta Conversions API: the dataset conversions are sent to, plus one signal rule per conversion you want Meta to receive. This is what send_meta_conversions means by "configure Conversion Sync first".

ParameterTypeRequiredDescription
modepreview | confirmYes—
dataset_idstringYesThe Meta dataset / pixel id from Events Manager. Not the ad account id
connection_idUUIDNoResolved automatically when the profile has exactly one connected Meta account
mappingsarrayYes{ conversion_definition_id, meta_event_name, value_mode?, name? }, at least one

Cost: 2 units · Wraps: GET /api/v1/connections, POST /api/v1/exports/destinations, POST /api/v1/exports/rules · Requires: mcp:write (→ exports:write), write-back enabled, owner/admin, a connected Meta Ads account

Show the preview to the user before confirming: a wrong dataset id sends their conversions to somebody else's pixel. When the profile has several connected Meta accounts the tool refuses and lists them rather than guessing.

confirm writes the destination and each rule separately and reports each outcome. A partial result is reported as complete: false with failed_rules — never as success, and never rolled back.

send_test_event

Send one synthetic conversion through the profile's Meta CAPI configuration so the customer can watch it arrive in Events Manager → Test Events.

ParameterTypeRequiredDescription
modepreview | confirmYes—
test_event_codestringYesFrom Events Manager → Test Events
destination_idUUIDNoOldest enabled destination when omitted
rule_idUUIDNoBorrow this rule's Meta event name and privacy override
channelwebsite | business_messagingNo—

Cost: 10 units · Wraps: POST /api/v1/exports/test · Requires: mcp:write (→ exports:write), write-back enabled, owner/admin

The only tool in this family that talks to a third party, which is why it is marked destructiveHint: it puts an event on the customer's real dataset. Always pass test_event_code — without it the event is indistinguishable from production traffic. It is deliberately not deduplicated: a second send is a legitimate second test.


Write-back tools

send_meta_conversions

Queue attributed conversions for delivery to Meta Conversions API (CAPI) through Atribu's export ledger. This tool has a three-step safety flow: preview, dry-run, confirm.

The tool does not call Meta. confirm writes conversion_exports ledger rows and enqueues one export job; the pipeline worker performs the send under the booking cluster's canonical event_id, which is what lets Meta's 48-hour dedup collapse it against the Pixel and against any other ingestion path. Poll GET /api/v1/exports/{batch_id} or GET /api/v1/exports/ledger for the delivery outcome.

See the dedicated Write-Back guide for the full flow, safety rails, and examples. apply_recommendation (above) is the second write tool — same gates, same audit trail.

ParameterTypeRequiredDefault
modepreview | dry_run | confirmYes—
window_startdateYes—
window_enddateYes—
event_typesstring[]Yes—
destinationmeta_capiNometa_capi (the only value; Google destinations run through POST /api/v1/exports)
pixel_idstringNo— (validated against the configured destinations; never used to route)
idempotency_keystringFor confirm—
test_event_codestringNo— (inert; the tool no longer makes a Meta test-event call)
max_eventsnumberNo500 (max 500)

Cost: 10 units

Requires: mcp:write scope, a connected Meta Ads account, an enabled Conversion Sync meta_capi export destination, workspace write-back enabled, workspace admin role.


start_plan_upgrade

Start a paid plan upgrade. You cannot enter a card, so this mints a hand-off: a URL to give the human, plus a handoff_id to poll. The human pays in any browser; Stripe's webhook settles the hand-off, and GET /api/v1/workspaces/{id}/subscription then shows the new tier.

ParameterTypeRequiredDefault
plangrowth | agencyYes—
intervalmonthly | annualNomonthly

Cost: 10 units

Scope: workspace

Requires: mcp:write scope, workspace write-back enabled, workspace owner/admin role. An analyst is refused: reading numbers is not committing the workspace to a recurring charge.

Returns: handoff_id, status, url (present only while status is pending), expires_at, no_change, and a poll block naming GET /api/v1/handoffs/{id}.

A workspace already on that plan comes back status: "completed" with no_change: true and no URL — never a second subscription. A workspace mid-trial on that plan counts as being on it: buying again would create a second subscription rather than convert the trial.

Downgrades are not a checkout — they go through the Stripe Customer Portal, because opening a second subscription for a lower tier would double-bill. Read GET /api/v1/workspaces/{id}/subscription first: its limits tell the human why they need the upgrade, and its upgrade_available lists exactly what this workspace can be sold.

Example call:

{
  "tool": "start_plan_upgrade",
  "arguments": {
    "workspace_id": "0f2a1c3e-...",
    "plan": "growth"
  }
}

Response envelope

Every tool returns a consistent envelope:

Response structure
{
  "data": { },
  "meta": {
    "request_id": "01968a3b-...",
    "workspace_id": "...",
    "profile_id": "...",
    "window_start": "2026-04-01",
    "window_end": "2026-04-14",
    "attribution_model": "first_touch",
    "currency": "USD",
    "data_as_of": "2026-04-14T18:30:00Z",
    "freshness_by_provider": {
      "meta": "2026-04-14T18:30:00Z",
      "ghl": "2026-04-14T17:00:00Z"
    },
    "data_freshness_warning": null,
    "record_count": 12,
    "pii_level_applied": "masked"
  }
}
Meta fieldDescription
request_idUnique ID for debugging and support
data_as_ofOldest sync timestamp across required providers
freshness_by_providerPer-provider last sync time
data_freshness_warningHuman-readable warning if data is stale (>6 hours)
pii_level_appliedmasked or full -- what PII level was actually used

On this page