Tools reference
The 18 tools an assistant gets when SourceLoop is connected, grouped by what you are trying to do, with the question each one answers.
You never call these by hand. They are what the assistant reaches for once
SourceLoop is connected at https://app.sourceloop.ai/api/mcp, and this page exists so
you know what it can do and how to ask. A tool whose permission your
connection lacks is not offered to the assistant at all.
Measure performance
The numbers themselves: what happened, split however you need it, under the attribution model you pick.
get_performance
metrics:read Get headline performance
Headline marketing numbers for a period: visitors, conversions, revenue and conversion rate. Use for "how did we do", "how many leads last month", "what is our conversion rate". For a per-channel or per-campaign split, use break_down_performance instead.
Ask How did we do last month compared with the month before?
Arguments 8
websitestringWebsite domain, e.g. "acme.com". Omit when the account has only one.
periodstringPeriod in plain language: "last 30 days", "yesterday", "July 2026", or "2026-07-01..2026-07-31". Defaults to the last 30 days.
compare_to_previousbooleanAlso return the preceding period of equal length, with the change. Ignored when granularity is set, since a series already shows the movement.
granularitystringReturn a TIME SERIES of daily buckets instead of a single total. Use for "show the trend", "conversions per day", or any chart over time. Buckets are calendar days in the website's own timezone, not UTC. Only day is available; to chart weeks or months, take days and sum conversions and revenue, but NOT visitors or sessions, which are unique counts and cannot be added across days.
Allowed values 1
day
filterstringDimension filter, "dimension:operator:value". Operators: eq, ne, in, nin, contains, not_contains. Examples: "channel:in:paid_search,paid_social", "country:eq:US", "campaign:contains:brand".
eventsstring[]Only count these conversion EVENT names, e.g. ["demo_booked"]. Takes precedence over types.
typesstring[]Only count these conversion categories, e.g. ["Web Form","Meeting"]. Coarser than events.
only_paidbooleanRestrict to paid traffic only.
break_down_performance
metrics:read Break performance down, and attribute it
Splits performance by channel, source, campaign, landing page, country, device and similar. Use for "which channels drive leads", "top landing pages", "where does our traffic come from".
Ask Break last quarter's conversions down by channel, then by campaign inside paid search.
Arguments 12
Pass SEVERAL dimensions to cross them: dimensions=["channel","source"] returns one row per pair, up to four deep.
Pass `attribution` to credit conversions across the whole visitor journey instead of counting only the touch where the conversion fired. Pass SEVERAL models to compare them side by side in one answer, which is the honest way to do it: separate calls can land on different windows and the comparison then looks fine and is wrong.
Attribution takes ONE dimension at a time, and only conversions and revenue can be attributed, because a visitor was on one channel at a time and has no credit to split.
Pass `stages` to break down CRM and lifecycle milestones instead of tracked conversions: stages=["deal_won"] with attribution credits closed-won DEAL revenue to the channels that earned it, which is the closed-loop number a B2B team cannot get from an ad platform. A deal is credited across its whole buying committee, so every contact on it contributes their journey.
THREE DIMENSIONS ANSWER SOMETHING DIFFERENT FROM THE REST: - dimension="platform" or "ad_campaign" with metrics like cac or ltv_cac joins ad SPEND to the campaign that acquired each customer, which is how to answer "is this campaign paying for itself". ltv_cac comes back null when the ad account and the ledger are in different currencies, because there is no FX layer and the wrong ratio is a number people switch campaigns off over. - dimension="resolution" splits credited outcomes by HOW their touches were tied to marketing: `click_id` (matched to a specific ad by id), `utm` (matched through UTM parameters), `unresolved` (a real visit tied to no ad), `unattributed` (no visit at all). To find out WHY outcomes went unattributed, call check_attribution_coverage instead. - dimension="cohort" returns a retention curve instead of a ranking: one row per (cohort, period_index) with revenue and customers at each age. Use analyze_ltv for that rather than this tool.
dimensionstringWhat to split by. Use `dimensions` to cross several. Ad dimensions (platform, ad_campaign) unlock cost per acquisition; `resolution` explains unattributed outcomes.
Allowed values 23
channelsourcemediumcampaignutm_termutm_contentreferrer_domainpagelanding_pagehostnamepage_titlecountryregioncitydevice_typebrowseroperating_systemresolutionplatformad_campaignad_setadcohort
dimensionsstring[]Two to four dimensions to cross, e.g. ["channel","source"]. Each row is one combination. Crossing is for traffic dimensions; attribution, ads and cohorts take one at a time.
attributionstring[]One or more attribution models. Several are compared side by side. Total credit is conserved across models, so every model sums to the same conversion count; only the split moves.
metricsstring[]Defaults to visitors, conversions and revenue. With attribution, only conversions and revenue are allowed.
websitestringWebsite domain, e.g. "acme.com". Omit when the account has only one.
periodstringPeriod in plain language: "last 30 days", "yesterday", "July 2026", or "2026-07-01..2026-07-31". Defaults to the last 30 days.
filterstringDimension filter, "dimension:operator:value". Operators: eq, ne, in, nin, contains, not_contains. Examples: "channel:in:paid_search,paid_social", "country:eq:US", "campaign:contains:brand".
eventsstring[]Only count these conversion EVENT names, e.g. ["demo_booked"]. Takes precedence over types.
typesstring[]Only count these conversion categories, e.g. ["Web Form","Meeting"]. Coarser than events.
stagesstring[]Count CRM or lifecycle milestones instead of tracked conversions, e.g. ["deal_won"] for closed-won deals, ["mql"], ["trial_started"], ["subscription_churned"]. With an attribution model this is how you credit DEAL revenue to the channels that earned it, which is the closed-loop number a B2B team is judged on. A deal is credited across its whole buying committee, so every contact on it contributes their journey.
only_paidbooleanRestrict to paid traffic only.
limitnumberRows to return. Default 10, max 100.
get_ad_performance
metrics:read Get ad platform performance
Ad spend, clicks, impressions and platform-reported conversions from the connected ad accounts. Use for "how much did we spend", "which campaigns are working", "what is our cost per click".
Ask What did we spend on Google Ads last month, and what did it produce?
Arguments 7
A level is REQUIRED because spend is stored once per grain (campaign, ad set, ad, keyword) and mixing grains multiplies it. Figures named platform_* come from the ad platform under ITS OWN attribution rules and will not match Sourceloop attributed conversions; that difference is expected and is not an error.
levelstringrequiredGrain to report at. Use "campaign" for the usual view, "account" for a per-platform total.
Allowed values 16
accountcampaignad_setadkeywordsearch_termplacementaudienceage_gendergeodeviceassetasset_groupproductlisting_groupnetwork
metricsstring[]breakdownbooleanOne row per item at that level, instead of a single total.
platformstringRestrict to one platform, e.g. google_ads or meta_ads.
filterstringNarrow to particular ad entities, "dimension:operator:value". Examples: "campaign:contains:brand", "campaign:in:Search - Brand,Search - Generic".
websitestringWebsite domain, e.g. "acme.com". Omit when the account has only one.
periodstringPeriod in plain language: "last 30 days", "yesterday", "July 2026", or "2026-07-01..2026-07-31". Defaults to the last 30 days.
get_funnel
metrics:read Get funnel conversion between stages
How many people or deals reached each stage, and the drop-off between them. Use for "what is our lead to customer rate", "where are we losing deals", "how many trials convert".
Ask Where are we losing people between demo request and closed won?
Arguments 3
Pass the stages IN ORDER, e.g. stages=["lead","mql","sql","deal_won"]. Each count is distinct entities that ENTERED that stage in the period, so somebody who reached it twice counts once.
STAGES ARE COUNTED OVER THE WHOLE POPULATION, including people the tracker never saw: imported contacts, deals closed over the phone, customers who predate the tracking script. That is deliberate, and it is why these rates are lower and more honest than a funnel computed over tracked visitors alone.
To credit a stage to the marketing that produced it, use break_down_performance with `stages` and an attribution model instead.
stagesstring[]requiredStage keys in funnel order, e.g. ["lead","mql","customer"] or ["trial_started","trial_converted"].
websitestringWebsite domain, e.g. "acme.com". Omit when the account has only one.
periodstringPeriod in plain language: "last 30 days", "yesterday", "July 2026", or "2026-07-01..2026-07-31". Defaults to the last 30 days.
get_paths
metrics:read Get the touch sequences that lead to conversions
Which SEQUENCES of touchpoints end in an outcome, and what each sequence is worth. Use for "what path do customers take", "how many touches before someone buys", "which combinations of channels work together".
Ask What are the most common touch sequences before a purchase?
Arguments 8
This is what a single-channel breakdown cannot show: that paid search rarely closes on its own, or that a particular pair of channels appears before most of the revenue.
Long journeys are capped at max_touches and marked with a trailing "..." so two long journeys that begin the same way group together instead of each becoming a row of one.
websitestringWebsite domain, e.g. "acme.com". Omit when the account has only one.
periodstringPeriod in plain language: "last 30 days", "yesterday", "July 2026", or "2026-07-01..2026-07-31". Defaults to the last 30 days.
stepstringWhat each step in the path is. Default channel.
Allowed values 4
channelsourcemediumcampaign
stagesstring[]Count CRM or lifecycle milestones instead of tracked conversions, e.g. ["deal_won"] for closed-won deals, ["mql"], ["trial_started"], ["subscription_churned"]. With an attribution model this is how you credit DEAL revenue to the channels that earned it, which is the closed-loop number a B2B team is judged on. A deal is credited across its whole buying committee, so every contact on it contributes their journey.
eventsstring[]Only count these conversion EVENT names, e.g. ["demo_booked"]. Takes precedence over types.
typesstring[]Only count these conversion categories, e.g. ["Web Form","Meeting"]. Coarser than events.
max_touchesnumberSteps kept before truncation. Default 5.
limitnumberPaths to return. Default 20, max 100.
get_products
metrics:read Get product-level sales
What each product sold, refunded and earned, for connected commerce stores. Use for "best selling products", "which products get refunded", "what is our margin by product".
Ask Which plan produced the most revenue last quarter?
Arguments 4
Revenue is NET of refunds. Margin is reported only for the share of revenue where the merchant has set a cost, and that share is returned alongside it, so a partial cost catalogue does not read as 100% margin.
websitestringWebsite domain, e.g. "acme.com". Omit when the account has only one.
periodstringPeriod in plain language: "last 30 days", "yesterday", "July 2026", or "2026-07-01..2026-07-31". Defaults to the last 30 days.
platformstringCommerce platform. Default shopify.
limitnumberDefault 25, max 200.
analyze_ltv
metrics:read Analyse lifetime value and payback
What the customers acquired in a period turn out to be worth, and how long they take to pay back. Use for "what is our LTV", "which channel brings the most valuable customers", "how long until a customer pays for themselves", "what is our repeat rate".
Ask What is the payback period on customers acquired through paid social?
Arguments 7
THIS IS A COHORT MEASURE AND IT IS NOT REVENUE. The period selects WHO was acquired, not which payments count, so it includes money those customers paid afterwards and grows as they mature. Revenue recorded IN a period is a different question: ask get_performance for that. On a subscription workspace the two differed by 3.1x, so quoting one as the other is a real error.
Set `curve: true` for the payback curve: one row per cohort per age, where period_index 0 is the month of acquisition. Set `dimension` to compare acquisition channels.
For cost per acquisition and whether a campaign has paid for itself, use break_down_performance with dimension="ad_campaign" and metrics including cac and ltv_cac, which joins the spend the ad platform reports.
websitestringWebsite domain, e.g. "acme.com". Omit when the account has only one.
periodstringPeriod in plain language: "last 30 days", "yesterday", "July 2026", or "2026-07-01..2026-07-31". Defaults to the last 30 days.
dimensionstringCompare acquisition channels. Omit for one overall figure.
Allowed values 4
channelsourcemediumcampaign
curvebooleanReturn the payback curve, one row per cohort per age, instead of a single rollup.
grainstringThe AGE unit for the curve, not a calendar bucket. Default month.
Allowed values 2
monthday
metricsstring[]Defaults to lifetime value, customers and average LTV. A curve reports lifetime value, customers and revenue per customer only.
limitnumberRows to return. Default 25, max 100.
Find people and revenue
Drop from the aggregate to the individual: which leads, which companies, which deals, and the marketing behind each one.
find_contacts
conversions:read Find the people who converted
The actual leads, not counts: who converted, when, from which channel, and what they turned out to be worth. Use for "who came in from LinkedIn last week", "show me the leads worth over 5000", "which leads has nobody followed up".
Ask Who converted last week from the LinkedIn campaign?
Arguments 23
One row per CONVERSION, so a person who converts twice appears twice; group by identity_id to count people. Channel filters match FIRST touch, because "leads from Google" almost always means "leads Google introduced us to".
Email, phone and name are MASKED unless the key carries pii:read, and the domain is kept when masking so a lead at acme.com is still recognisable.
websitestringWebsite domain, e.g. "acme.com". Omit when the account has only one.
periodstringPeriod in plain language: "last 30 days", "yesterday", "July 2026", or "2026-07-01..2026-07-31". Defaults to the last 30 days.
channelstringFirst-touch channel, e.g. "paid search".
sourcestringFirst-touch source, e.g. "google".
mediumstringFirst-touch medium, e.g. "cpc".
campaignstringFirst-touch campaign.
latest_channelstringLAST-touch channel: what they came back on, rather than what introduced them.
latest_sourcestringLast-touch source.
latest_campaignstringLast-touch campaign.
typestringConversion category, e.g. "Web Form".
event_namestringExact conversion event, e.g. "demo_booked".
statusstringlifecycle_stagestringCRM lifecycle stage as the CRM spells it.
owner_emailstringThe CRM owner the contact is assigned to.
company_domainstringMatches on company name. Not personal data, so no pii:read needed.
emailstringSearch by email. Requires pii:read, because searching by an address IS reading it.
phonestringSearch by phone. Requires pii:read.
has_valuebooleantrue returns only contacts with a sales value recorded.
min_sales_valuenumberhas_quotebooleantrue returns only contacts with a quote value recorded.
include_spambooleanDefault false. Spam is excluded unless asked for.
include_duplicatesbooleanDefault true.
limitnumberDefault 25, max 200.
get_companies
companies:read Get companies, with their pipeline and engagement
The account-based view: which companies are engaging, how many people from each, what pipeline they represent and which channel introduced them. Use for "which accounts are most engaged", "what companies visited but never converted", "show me our biggest accounts by pipeline".
Ask Which companies visited us more than five times but never converted?
Arguments 12
Sorted by pipeline value by default, or by engagement when sort="engagement". Company data is not personal data, so this needs no pii:read.
websitestringWebsite domain, e.g. "acme.com". Omit when the account has only one.
sortstringpipeline = total open + won value, engagement = number of people, recent = last seen.
Allowed values 3
pipelineengagementrecent
has_dealsbooleantrue returns only companies with at least one deal.
has_open_dealsbooleantrue returns only companies with a deal still open.
min_conversionsnumberdomainstringMatch on company domain, e.g. "acme.com".
industrystringcountrystringchannelstringFirst-touch channel that introduced the company.
sourcestringFirst-touch source.
include_personalbooleanDefault false. Personal email domains (gmail.com and similar) are not companies and are excluded.
limitnumberDefault 25, max 200.
get_deals
deals:read Get deals, and what marketing produced them
Deals from the connected CRM with the attribution Sourceloop computed for them, so revenue can be traced back to the channel that started it. Use for "what is in the pipeline", "which channel produces won deals", "how much revenue did paid search actually generate".
Ask Which open deals came from paid search, and what are they worth?
Arguments 18
Set group_by to summarise instead of listing: group_by="first_channel" returns won, open and lost value per channel.
TWO WAYS TO CREDIT A DEAL, AND THEY DIFFER: - By default a grouped answer uses the deal record's own first and latest touch, which is the ANCHOR CONTACT's journey. Simple, and it under-counts: two thirds of deals have another contact on them carrying their own browsing history. - Pass `credit` with an attribution model and the answer comes from the journey spine instead, where a deal is one outcome whose touches are the union of the WHOLE buying committee. If the champion arrived from organic search and the VP typed the URL, the anchor view credits Direct alone and this one does not. Deals average 1.83 contacts and reach 3, so this is the normal case rather than an edge one.
Deal amounts come from your CRM, not from our tracking, so they are the real numbers your sales team sees.
websitestringWebsite domain, e.g. "acme.com". Omit when the account has only one.
periodstringPeriod in plain language: "last 30 days", "yesterday", "July 2026", or "2026-07-01..2026-07-31". Defaults to the last 30 days.
statusstringRestrict to one status.
Allowed values 3
openwonlost
pipeline_idstringFrom get_deals results or the pipelines endpoint.
stage_idstringaccount_idstringOnly deals for one company, from get_companies.
owner_emailstringThe CRM owner the deal is assigned to.
min_amountnumberchannelstringFirst-touch channel that introduced the deal.
sourcestringFirst-touch source.
campaignstringFirst-touch campaign.
latest_channelstringLast-touch channel.
closed_afterstringYYYY-MM-DD. Filters on close date, not creation date.
closed_beforestringYYYY-MM-DD.
sortstringDefault amount, largest first.
Allowed values 3
amountcreatedclose_date
group_bystringSummarise by this instead of listing individual deals.
Allowed values 7
first_channelfirst_sourcefirst_campaignlatest_channellatest_sourcestatusstage
creditstringCredit won-deal value across the whole buying committee using this attribution model, from the journey spine, instead of the anchor contact's stored first touch. Only with group_by on a channel, source or campaign.
Allowed values 5
first_touchlast_touchlinearu_shapedtime_decay
limitnumberDefault 25, max 200.
get_journey
conversions:read Get the full journey behind a conversion, company or deal
The timeline the attribution numbers summarise: every session, in order, with how the person arrived each time and what they did once there. Use for "how did this lead find us", "what marketing produced this deal", "show me this account's path".
Ask Show me the full journey behind the Acme deal.
Arguments 5
Pass exactly ONE of contact_id, company_id or deal_id. A company or deal journey merges every person attached to it, because in B2B the person who first read a blog post is rarely the person who signs.
Devices are merged via the identity graph, so someone who browsed on a phone and converted on a laptop is one timeline, not two half-journeys.
When no raw events exist the timeline is reconstructed from stored first-touch and last-touch attribution and marked synthetic:true. Say so rather than presenting it as a complete history.
contact_idstringFrom find_contacts.
company_idstringFrom get_companies. Merges everyone at the company.
deal_idstringFrom get_deals. Merges everyone on the deal.
daysnumberHow far back to look. Default 365.
limitnumberMaximum rows from the event store. Default 2000.
Check and explain
Ask before you act. Whether the data can be trusted right now, how much of the revenue attribution can explain, and what a metric actually counts.
check_data_health
metrics:read Check whether the data can be trusted right now
Reports recent tracking activity so a surprising number can be sanity-checked before conclusions are drawn from it. Call this whenever a figure looks wrong, or before recommending a decision based on an unexpected drop.
Ask Is anything wrong with our tracking right now?
Arguments 1
websitestringWebsite domain, e.g. "acme.com". Omit when the account has only one.
check_attribution_coverage
metrics:read Check how much money attribution can explain, and why not
How much recorded revenue the journey spine explains, and for the rest, WHY NOT. Call this whenever a large share of revenue shows as Unattributed, BEFORE reporting that attribution or tracking is broken, because most of the time it is not.
Ask How much of last month's revenue can you actually attribute, and what is missing?
Arguments 2
The reasons are not comparable and must never be summed into one "unattributed" number: - `imported_contact`: loaded from a CRM or spreadsheet. There was never a visit to find. - `predates_tracking`: the CUSTOMER arrived before the tracker existed. Keyed on when the relationship started, not on the payment date, so renewals from customers won a year ago land here rather than reading as failures. - `no_visitor` and `no_sessions`: the tracker was running and the person still could not be tied to a visit. These are the only ones worth investigating.
The first two are permanent facts about the business. Telling a customer to fix their install because of them wastes their time and their trust.
websitestringWebsite domain, e.g. "acme.com". Omit when the account has only one.
periodstringPeriod in plain language: "last 30 days", "yesterday", "July 2026", or "2026-07-01..2026-07-31". Defaults to the last 30 days.
explain_metrics
metrics:read Explain what a metric means
Returns the exact definition of any Sourceloop metric or dimension, and the filter syntax. Use before reporting an unfamiliar metric so it is never described incorrectly, and whenever a user asks how something is calculated.
Ask What exactly does influenced_conversions count?
Arguments 1
metricstringA metric name. Omit to list everything.
list_workspaces
metrics:read List workspaces
Lists the websites this account can report on, with each one's timezone and currency. Call this first when unsure which website a question refers to, or when another tool reports that the website is ambiguous.
Ask Which SourceLoop websites can you see?
Arguments
Takes no arguments.
Write outcomes back
Close the loop. These change your data and your CRM, so they need a write permission and are simply not offered to a read-only connection.
update_contact
conversions:write Record what a lead turned out to be worth
Writes an outcome back onto a contact: the deal value, the status, notes. This is how cost-per-lead reporting becomes cost-per-revenue reporting, because until someone records that a lead closed for 14,000 the attribution has nothing to attribute.
Ask Mark the Acme lead as qualified and set its value to 12,000.
Arguments 11
Changes propagate to any connected CRM. A write that changes nothing writes nothing. Attribution is computed by Sourceloop and cannot be set.
contact_idstringrequiredThe contact id, from find_contacts.
statusstringSourceloop status, e.g. "won", "qualified".
lifecycle_stagestringCRM lifecycle stage, as the CRM spells it, e.g. "Customer". Propagates to the connected CRM.
lead_statusstringCRM lead status, as the CRM spells it. Propagates to the connected CRM.
qualifiedbooleanMark the lead qualified.
lead_scorenumbersales_valuenumberWhat the deal was actually worth.
quote_valuenumbercurrencystringnotesstringis_spambooleanMark a junk lead, excluding it from reporting.
update_deal
deals:write Move a deal, or change its value, in the CRM
Updates a deal in the customer's own CRM (HubSpot or Pipedrive). Use for "mark that deal won", "the contract came in at 22,000".
Ask Move the Acme deal to Negotiation.
Arguments 5
The CRM owns these fields, so the change is saved and QUEUED for push rather than applied instantly. The result says whether it was queued: if it was not, outbound is disabled on that connection and the next sync will overwrite the value.
status is translated per provider. HubSpot has no status field, because won and lost are stages there, so "won" is resolved to that pipeline's won stage.
deal_idstringrequiredThe deal id, from get_deals.
statusstringAllowed values 3
openwonlost
amountnumbernamestringclose_datestringYYYY-MM-DD.
update_company
companies:write Correct a company, or pin its stage
Corrects a company record: firmographics (industry, employee count, country and so on) and its lifecycle stage.
Ask Correct Acme's industry to Logistics.
Arguments 13
Most companies here were discovered from traffic rather than imported, so nobody else owns them and the write simply sticks. When a company IS linked to a CRM the write still lands, and the result says the next sync will overwrite it.
Stage is normally DERIVED from deals and contact activity. Setting it PINS the company, stopping that recomputation, and the result says so: do not pin a stage without telling the user that is what happened.
Engagement counts, deal rollups and attribution are computed and cannot be set.
company_idstringrequiredThe company id, from get_companies.
company_namestringindustrystringemployee_countnumberannual_revenuenumbersize_rangestringcountrystringregionstringcitystringwebsite_urlstringlinkedin_urlstringdescriptionstringlifecycle_stagestringA stage name from this workspace, e.g. "Customer". Pins the company: stage stops being derived.
Knowing what to ask
The tools are the easy half. Getting a trustworthy answer is mostly about how the question is phrased, so there is a library of prompts by use case: weekly reviews, diagnosing a drop, budget decisions, and the phrasing that keeps an assistant honest about what it does not know.