Skip to content New SourceLoop MCP: chat with your attribution data in Claude, ChatGPT & Cursor
SourceLoop
MCP server

Tools reference

The 23 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.

Start here

What the connection can reach, what every number means, and whether the data can be trusted right now. The assistant calls these first, so its later answers use your own stage names, currency and time zone.

get_context

metrics:read

What this connection can reach

Call this FIRST. Lists the websites (workspaces) this connection can report on with time zone and currency, and for one website: its outcome vocabulary (conversion types, the CRM stage ladder, deal and subscription statuses, under the workspace's own names), its ad accounts with platform, currency and last sync, the attribution models, and the limits of the tools. Pass `website` when the account has several.

Ask Which websites can you see, and what do you call our CRM stages?

Arguments 1
websitestring

Website domain, e.g. "acme.com". Omit when the account has only one.

describe_data

metrics:read

What the data means

The vocabulary: every metric (with its definition, unit and how it may be aggregated), every dimension and where it can be used, the record kinds of get_records, the rules for SQL over results, and playbooks for common investigations. Without arguments it returns the compact catalog; pass `names` for full definitions of specific metrics or dimensions, or `topic` for one section.

Ask What exactly does influenced_conversions count?

Arguments 2
topicstring

One section only.

Allowed values 6

  • metrics
  • dimensions
  • records
  • sql
  • playbooks
  • filters

namesstring[]

Metric or dimension names to define in full.

check_data_health

metrics:read

Can the data be trusted right now

Call BEFORE explaining any drop or surprise: a broken form tag, an expired ad token or a stalled CRM sync looks exactly like a marketing problem. Reports every data source of the website (tracking script, page views, leads by type, each ad account, each CRM and payment connection, webhooks, conversion syncs) as healthy, quiet, stale, failing or waiting, with when data last arrived and what is wrong, plus visitors, sessions and conversions per day for the last 14 days. The same checks as the Data health page.

Ask Is anything wrong with our tracking or ad syncs right now?

Arguments 1
websitestring

Website domain, e.g. "acme.com". Omit when the account has only one.

Prepare data

Ask in plain parameters and the whole answer, up to 200,000 rows, is saved on Sourceloop's side with exact totals, using the same engine as your dashboards. The assistant gets an id and a preview, not megabytes.

query_metrics

metrics:read

Prepare metrics as a result

The main analytics tool. Computes metrics by up to four dimensions and an optional time granularity, with the same engine and definitions as the dashboards, and SAVES the full answer (up to 200,000 rows) as a result you then analyze with query_result. Returns the result id, each column's role, the engine's exact totals and a preview.

Ask Spend, leads and cost per lead by campaign, by day, for the last 365 days.

Arguments 23

Covers traffic (visitors, sessions, pageviews), outcomes (conversions, leads, CRM stages, deals, subscriptions, revenue) credited by any attribution model, ad platform numbers (spend, impressions, clicks, ctr, cpc, platform_conversions, search impression share...) beside credited outcomes (cost_per_conversion, roas), subscriptions (mrr, churn, trials) and costs. Ad entities break down by ad_platform, ad_account, ad_campaign, ad_set, ad, keyword, ad_search_term and more; the platform id of each entity is in <dimension>_id.

Prepare WIDE, then narrow with SQL: e.g. spend, conversions and cost_per_conversion by ad_campaign by day for 365 days in one call, then query_result for "campaigns whose CPA rose 30% quarter over quarter". Ratios come with their parts so SQL can recompute them. Call describe_data for metric and dimension names, and get_context for the outcome keys this workspace has.

Limits: windows up to 400 days; one ad dimension per query; ad metrics by day or coarser.

websitestring

Website domain, e.g. "acme.com". Omit when the account has only one.

metricsstring[]

Metric names. Defaults depend on the first dimension (traffic, outcomes or ad numbers).

bystring[]

Dimensions to break down by, crossed, e.g. ["ad_campaign"] or ["channel","landing_page"].

granularitystring

Add a time column: one row per bucket (per dimension value), in the workspace time zone.

Allowed values 5

  • hour
  • day
  • week
  • month
  • quarter

periodstring

Period in plain language: "last 30 days", "yesterday", "July 2026", or "2026-07-01..2026-07-31". Defaults to the last 30 days.

fromstring

Start day (YYYY-MM-DD), instead of period.

tostring

Last day, INCLUSIVE (YYYY-MM-DD).

filtersstring[]

Dimension 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". Several are ANDed unless match="any".

matchstring

Combine filters with AND (all, default) or OR (any).

Allowed values 2

  • all
  • any

attributionstring[]

Attribution models crediting conversions and revenue to the dimensions. Several give one column per model (conversions__linear). Ad dimensions default to last_non_direct.

outcomestring

What counts as an outcome. Defaults to `conversions`. ONE request counts ONE kind: 2 MQLs is not a slice of 39 conversions, it is a different object, so never blend them or present a total across them.

  • conversions (All conversions, from tracking): Every conversion your tracking script recorded: form submissions, meetings booked, chats, payments. One total, no split.
  • conversion_type (Conversions by type, from tracking): The same conversions, split by what kind they were: form fills, signups, meetings, chats, payments and the rest. Which kinds appear depends on what this workspace records. These add up to the conversions total.
  • contact_stage (Contacts by stage, from crm): People who reached each stage of your funnel, from your CRM. Counts PEOPLE, not conversions, so these do not add up to the conversions total. Someone now at Customer still counts once under every stage they passed through.
  • deal_status (Deals by status, from crm): Deals from your CRM, with the money on them. Counts DEALS, not conversions. Pipeline is the amount on deals created; Won revenue is the amount on deals closed won.
  • subscription_status (Subscriptions by status, from payments): Trials, upgrades to paid, and cancellations from your payment provider, with the recurring revenue each one added or removed. Counts subscriptions, not conversions.
  • payment_revenue (Payments, from payments): Individual payments you actually received, through Stripe, Shopify or whichever provider you connected. Subscription renewals are not counted here.
  • revenue (Revenue, from payments): Every money movement we hold for this workspace: payments and refunds from your payment provider, won deals from your CRM, values you typed on a contact, and anything your tracking script reported. Deduplicated so the same money is never counted twice, and credited to channels by the selected attribution model.

Call get_context first to learn which of these this workspace has data for, and the stage keys to pass as outcome_values.

Allowed values 7

  • conversions
  • conversion_type
  • contact_stage
  • deal_status
  • subscription_status
  • payment_revenue
  • revenue

outcome_valuesstring[]

Narrow the outcome to specific stages, e.g. ["mql"] or ["deal_won"]. Keys come from get_context. Omit to count every value of that kind, which is what "show me our funnel" means.

grainstring

WHO the outcome is counted over. Only contact_stage offers companies. The company view is SMALLER than the contact view rather than a slice of it: a company sits at the stage of its most advanced contact, so three people at one account are one company. Use it when the question is about accounts rather than people.

Allowed values 2

  • contacts
  • companies

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_paidboolean

Restrict to paid traffic only.

journeystring

Whose visits credited conversions and revenue are spread over. person: the converting person's own visits (plus a deal's buying committee). company: also every colleague at their company, so a teammate who found the site first shares the credit. Each outcome still counts once. Omit for the workspace setting. Pair with the "companies" metric to count companies instead of people.

Allowed values 2

  • person
  • company

compare_to_previousboolean

Add each metric's previous-period value and change, and the previous totals.

sort_bystring

Rank rows by this metric, largest first. Matters only when more rows exist than `limit`.

limitnumber

Rows to keep (series, with granularity). Default and max: as many as fit 200,000 rows.

include_creativeboolean

With by=["ad"], also keep each ad's thumbnail URL, headline, landing page and format.

queryobject

Instead of the flat arguments, a whole engine query (any kind): "metrics" { window: { period } | { from, to }, metrics: [{ id, as?, events?, types?, models? } | { formula: "demos / visitors", as }], groupBy?, granularity?, compare?: { to: "previous_period" | "previous_year" }, filters?: "dim:op:value" strings or { and|or: [...] }, outcome?: { name, values?, grain? }, sort?, limit? }; "products" { window, compare? } (product sales, refunds, margin); "statement" { window, granularity? } (the P&L); "cohorts" { window, grain?, periods?, groupBy?, spend? }; "paths" { window, step?, outcome? }. Use it for formulas, per-metric events or models, nested filters, previous_year comparisons, products and the P&L.

preview_rowsnumber

Rows shown inline (default 50 for metrics, 30 for records, at most 500). The full result is saved either way.

get_records

metrics:read

Prepare records as a result

Row-level records with their attributes, saved as a result (up to 200,000 rows) for query_result: - ad_entities: campaigns, ad groups, ads, keywords, budgets, bid strategies, audiences... with SETTINGS (status, budget, bid strategy and targets, match type, final URL, labels, targeting, parent ids), now or as_of a day. Filter by entity_type (one or several), status, campaign_id, platform, text. - ad_changes: what changed in the ad accounts, when and who (budget, bid, status, targeting, names), with old and new values. - ad_search_terms: search terms totalled over the window with spend, clicks and the platform's conversions; min_spend, zero_conversions. - ad_pacing: spend against the daily budget in force, per campaign per day. - contacts: the people who converted, one row per conversion, with first and latest touch, value, stage (needs conversions:read; PII masked without pii:read). - companies: accounts with people, conversions, deals and pipeline (needs companies:read). - deals: CRM deals created in the window with amount, status and first touch (needs deals:read). - subscriptions: customer subscriptions as they stand now (status, MRR, plan, trial and cancel dates); `period` narrows to those started in it. - payments: payment-provider events (charges, refunds, subscription changes) with the stitched person and first and last touch. - sessions: every visit (window up to 31 days) with channel, source, campaign, landing page, click ids, device, pageviews, conversions; filters channel, source, campaign, text (landing page contains), country, device_type, anonymous_id. - events: every tracked event (window up to 7 days) with page, UTMs, visit channel and custom properties (JSON); filters event_type_filter, event_name, text (path contains), anonymous_id, conversions_only. Use for custom-property analysis and event sequences in query_result.

Ask Every keyword we bid on, with its match type, status and campaign.

Arguments 41

Join records to query_metrics results in query_result on ids (campaign_id, ad_group_id, keyword ids are ad_group_id~criterion_id).

kindstringrequired

Allowed values 11

  • ad_entities
  • ad_changes
  • ad_search_terms
  • ad_pacing
  • contacts
  • companies
  • deals
  • subscriptions
  • payments
  • sessions
  • events

websitestring

Website domain, e.g. "acme.com". Omit when the account has only one.

periodstring

Period in plain language: "last 30 days", "yesterday", "July 2026", or "2026-07-01..2026-07-31". Defaults to the last 30 days. For ad_changes, ad_search_terms, ad_pacing (default last 7 days), contacts, deals.

entity_typestring[]

ad_entities, ad_changes: which kinds of entity.

statusarraystring

ad_entities: platform statuses, e.g. ["ENABLED"]. deals: open, won or lost.

actor_typestring[]

ad_changes: who made the change.

campaign_idstring

Ad kinds: one campaign (platform id) and what sits under it.

platformstring

Ad kinds: e.g. google_ads, meta_ads, microsoft_ads.

as_ofstring

ad_entities: settings as they stood at the end of this day (YYYY-MM-DD).

textstring

ad_entities: name or keyword contains; companies: name or domain contains; deals: name contains.

min_spendnumber

ad_search_terms: spent at least this much (workspace currency).

zero_conversionsboolean

ad_search_terms: only terms with no platform conversion.

channelstring

contacts, companies, deals: first-touch channel.

sourcestring

mediumstring

campaignstring

contacts: first-touch campaign.

latest_channelstring

latest_sourcestring

latest_campaignstring

typestring

contacts: conversion category.

event_namestring

contacts: exact conversion event.

lifecycle_stagestring

owner_emailstring

company_domainstring

emailstring

contacts: needs pii:read.

phonestring

contacts: needs pii:read.

include_spamboolean

min_valuenumber

contacts: sales value at least; deals: amount at least.

pipeline_idstring

account_idstring

deals: one company's deals.

industrystring

countrystring

has_dealsboolean

include_personalboolean

event_typestring[]

payments: provider event types to keep.

event_type_filterstring

events: pageview, click, form_submit, custom...

device_typestring

sessions: desktop, mobile, tablet.

anonymous_idstring

sessions, events: one visitor (device).

conversions_onlyboolean

events: only events that are conversions.

limitnumber

Rows at most. Default and max 200,000.

preview_rowsnumber

Rows shown inline (default 50 for metrics, 30 for records, at most 500). The full result is saved either way.

analyze_flow

metrics:read

Analyze funnels, journeys and stage conversion

How people move, saved as a result: - kind="funnel": a funnel the customer saved in the app (page views, events, milestones) step by step over tracked visitors. Without funnel_id it lists the saved funnels. Needs funnels:read. - kind="paths": which SEQUENCES of touches (channel, source, medium or campaign) end in an outcome, with converters and value per path. population="visitors" for every visitor's journey and the share that converted. - kind="stages": CRM stage to stage conversion (lead, mql, sql, deal_won... in order) over the whole population, including people the tracker never saw.

Ask How is our signup funnel doing, and which step lost the most people?

Arguments 17

For crediting stages to marketing use query_metrics with outcome and attribution instead.

kindstringrequired

Allowed values 3

  • funnel
  • paths
  • stages

websitestring

Website domain, e.g. "acme.com". Omit when the account has only one.

periodstring

Period in plain language: "last 30 days", "yesterday", "July 2026", or "2026-07-01..2026-07-31". Defaults to the last 30 days.

fromstring

Start day (YYYY-MM-DD), instead of period.

tostring

Last day, inclusive.

funnel_idstring

funnel: which saved funnel.

compare_to_previousboolean

funnel: also count the window before.

stagesstring[]

stages: keys in order, e.g. ["lead","mql","customer"].

stepstring

paths: what one step is. Default channel.

Allowed values 4

  • channel
  • source
  • medium
  • campaign

populationstring

paths: journeys behind outcomes (default) or every visitor's journey.

Allowed values 2

  • converted
  • visitors

outcomestring

What counts as an outcome. Defaults to `conversions`. ONE request counts ONE kind: 2 MQLs is not a slice of 39 conversions, it is a different object, so never blend them or present a total across them.

  • conversions (All conversions, from tracking): Every conversion your tracking script recorded: form submissions, meetings booked, chats, payments. One total, no split.
  • conversion_type (Conversions by type, from tracking): The same conversions, split by what kind they were: form fills, signups, meetings, chats, payments and the rest. Which kinds appear depends on what this workspace records. These add up to the conversions total.
  • contact_stage (Contacts by stage, from crm): People who reached each stage of your funnel, from your CRM. Counts PEOPLE, not conversions, so these do not add up to the conversions total. Someone now at Customer still counts once under every stage they passed through.
  • deal_status (Deals by status, from crm): Deals from your CRM, with the money on them. Counts DEALS, not conversions. Pipeline is the amount on deals created; Won revenue is the amount on deals closed won.
  • subscription_status (Subscriptions by status, from payments): Trials, upgrades to paid, and cancellations from your payment provider, with the recurring revenue each one added or removed. Counts subscriptions, not conversions.
  • payment_revenue (Payments, from payments): Individual payments you actually received, through Stripe, Shopify or whichever provider you connected. Subscription renewals are not counted here.
  • revenue (Revenue, from payments): Every money movement we hold for this workspace: payments and refunds from your payment provider, won deals from your CRM, values you typed on a contact, and anything your tracking script reported. Deduplicated so the same money is never counted twice, and credited to channels by the selected attribution model.

Call get_context first to learn which of these this workspace has data for, and the stage keys to pass as outcome_values.

Allowed values 7

  • conversions
  • conversion_type
  • contact_stage
  • deal_status
  • subscription_status
  • payment_revenue
  • revenue

outcome_valuesstring[]

Narrow the outcome to specific stages, e.g. ["mql"] or ["deal_won"]. Keys come from get_context. Omit to count every value of that kind, which is what "show me our funnel" means.

grainstring

WHO the outcome is counted over. Only contact_stage offers companies. The company view is SMALLER than the contact view rather than a slice of it: a company sits at the stage of its most advanced contact, so three people at one account are one company. Use it when the question is about accounts rather than people.

Allowed values 2

  • contacts
  • companies

eventsstring[]

Only count these conversion EVENT names, e.g. ["demo_booked"]. Takes precedence over types.

max_touchesnumber

paths: steps kept before a path is cut with "...". Default 5.

limitnumber

paths: paths to keep. Default 1000.

preview_rowsnumber

Rows shown inline (default 50 for metrics, 30 for records, at most 500). The full result is saved either way.

analyze_cohorts

metrics:read

Analyze customer cohorts, LTV and payback

What customers acquired in a period are worth over time, saved as a result: - kind="ltv": lifetime value, customers, average LTV, repeat rate per acquisition channel/source/medium/campaign (or overall). - kind="curve": the payback curve, one row per cohort per age (period_index 0 = month of acquisition). - kind="revenue_cohorts": customers grouped by the period of their FIRST payment and what they paid at each age, optionally with the cohort's marketing spend (spend=true) for payback, split by first-touch channel or ad platform.

Ask What is the payback period on customers acquired through paid social?

Arguments 9

THIS IS NOT REVENUE IN A PERIOD: the window chooses WHO was acquired, and their later payments count. For revenue in a window use query_metrics. For cost per acquisition by campaign use query_metrics with metrics cac and ltv_cac by ad_campaign.

kindstringrequired

Allowed values 3

  • ltv
  • curve
  • revenue_cohorts

websitestring

Website domain, e.g. "acme.com". Omit when the account has only one.

periodstring

Which customers: those acquired (first paid) in this period. Defaults to the last 30 days; cohorts usually want 6 to 12 months.

dimensionstring

Split by acquisition channel, source, medium, campaign (ltv, curve, revenue_cohorts) or ad_platform (revenue_cohorts).

Allowed values 5

  • channel
  • source
  • medium
  • campaign
  • ad_platform

grainstring

curve and revenue_cohorts: the age unit. Default month.

Allowed values 3

  • day
  • week
  • month

periodsnumber

revenue_cohorts: ages to return, default 12, at most 60.

spendboolean

revenue_cohorts: add cohort_spend (marketing cost of the cohort's period) for payback.

metricsstring[]

ltv and curve: the figures. Defaults to lifetime value, customers and average LTV (curve: revenue per customer).

preview_rowsnumber

Rows shown inline (default 50 for metrics, 30 for records, at most 500). The full result is saved either way.

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_idstring

From get_records kind="contacts" (id) or search.

company_idstring

From get_records kind="companies" or search. Merges everyone at the company.

deal_idstring

From get_records kind="deals" or search. Merges everyone on the deal.

daysnumber

How far back to look. Default 365.

limitnumber

Maximum rows from the event store. Default 2000.

Analyze results

SQL over the results this connection prepared: filter, rank, compare periods, join ad spend to deals. It can read nothing else, and it refuses the classic mistakes, like adding up ROAS across campaigns.

query_result

metrics:read

Analyze results with SQL

Runs ONE read-only SQL SELECT (DuckDB dialect) over results this connection prepared, and saves the output as a NEW result, so steps chain. Tables are result ids: SELECT ... FROM r_abcdefghijklmnop. Join several results (deals from get_records with spend from query_metrics, this quarter with last), filter, rank, pivot, window functions, QUALIFY, date functions on time columns. Returns the new result id, its schema and up to 100 rows inline.

Ask Of those campaigns, which ones saw cost per lead rise 30% quarter over quarter?

Arguments 2

The data never leaves our side and the SQL can reach nothing but your results. Follow the column ROLES in each result: SUM measures; recompute ratios from their formula (SUM(spend) / NULLIF(SUM(conversions), 0)), never SUM or AVG them; never SUM non_additive columns (unique visitors, averages, MRR levels) across rows, prepare them at the grain you need instead. The guard refuses those mistakes with the fix.

Examples: campaigns whose CPA rose 30% quarter over quarter: WITH q AS (SELECT ad_campaign, quarter(date) qt, SUM(spend) / NULLIF(SUM(conversions), 0) cpa FROM r_... GROUP BY ALL) SELECT a.ad_campaign, a.cpa, b.cpa AS next_cpa FROM q a JOIN q b ON a.ad_campaign = b.ad_campaign AND b.qt = a.qt + 1 WHERE b.cpa > a.cpa * 1.3. Wasted search terms: SELECT * FROM r_... WHERE spend > 50 AND platform_conversions = 0 ORDER BY spend DESC.

sqlstringrequired

One SELECT (or WITH ... SELECT). Tables are result ids.

preview_rowsnumber

Rows inline, default 100, at most 500. The whole output is saved as a result.

read_result

metrics:read

Read rows of a result

Reads a page of a result: pick columns, sort, and page with offset. Up to 2000 rows or 40,000 characters a call. Use it to look at rows; use query_result to filter or aggregate, and export_result for the whole file.

Ask Show me the 50 biggest spenders from that table.

Arguments 6
result_idstringrequired

A result id (r_ followed by 16 letters and digits) from a prepare tool, query_result or list_results.

columnsstring[]

Columns to return. Default all.

order_bystring

Sort by this column.

descendingboolean

offsetnumber

Rows to skip. Default 0.

limitnumber

Rows to return. Default 100, max 2000.

export_result

metrics:read

Export a result as a file

Writes a whole result as CSV, JSON Lines or Parquet and returns a download link valid for 6 hours (the link needs no sign-in, so share it only with whoever should see the data). Use it to hand a large table to the user, a spreadsheet, or a code sandbox that can fetch URLs.

Ask Give me that whole table as a CSV.

Arguments 3
result_idstringrequired

A result id (r_ followed by 16 letters and digits) from a prepare tool, query_result or list_results.

formatstring

Default csv.

Allowed values 3

  • csv
  • jsonl
  • parquet

file_namestring

Name of the downloaded file, e.g. "campaigns-2026-q3".

list_results

metrics:read

List live results

The results this connection prepared in the last 24 hours, newest first, with what made each one, its size and its columns. Use it to pick up earlier work instead of preparing the same data again.

Ask What have you already pulled in this conversation?

Arguments 1
limitnumber

Default 20, max 100.

Reports and alerts

Turn an analysis into a doc your team or client can open, share it in your brand, and set alerts that post to Slack or email.

list_docs

docs:read

List docs

List the workspace's docs (reports, dashboards, plans) and the templates a new doc can start from.

Ask Which reports do we already have?

Arguments 1
websitestring

Website domain, e.g. "acme.com". Omit when the account has only one.

get_doc

docs:read

Read a doc

Read a doc as Markdown, with every block's id. Widgets appear as ```sourceloop-widget fences holding their query and visual spec. Use the block ids with edit_doc.

Ask Read me the October client report.

Arguments 1
doc_idstringrequired

The doc id from list_docs.

create_doc

docs:write

Create a doc

Create a doc from a template (list_docs shows them) or from Markdown. Markdown supports headings, lists, tasks, quotes, code, --- page breaks and ```sourceloop-widget fences with JSON { title, query, viz: { type: kpi|line|area|bar|combo|donut|table|pivot|funnel|sankey|heatmap|geo }, span?: 12|8|6|4|3 (consecutive narrow widgets sit side by side), height?: S|M|L|XL or pixels 120-960 }. A widget query may use "$doc.window" to follow the doc's date range. To put a saved result in the doc as a table, write a ```sourceloop-result fence holding JSON { "result_id": "r_...", "columns"?: [...], "order_by"?, "descending"?, "limit"?: up to 500, "title"? }: the rows are filled in on the server from the result, never typed out.

Ask Turn this analysis into a report for the client, with the tables in it.

Arguments 4
websitestring

Website domain, e.g. "acme.com". Omit when the account has only one.

titlestring

templatestring

A template key, e.g. client_report, ad_scaling_plan, traffic_dashboard.

markdownstring

The whole doc as Markdown. Use instead of a template.

edit_doc

docs:write

Edit a doc

Change a doc block by block, merging with anyone editing it at the same time. Ops: { op: "append", markdown } | { op: "insert_after" | "insert_before" | "replace", target: <block id>, markdown | blocks } | { op: "remove", target } | { op: "move_after", block, target }. A restore point is saved first, so a person can undo the whole edit in one step. To put a saved result in the doc as a table, write a ```sourceloop-result fence holding JSON { "result_id": "r_...", "columns"?: [...], "order_by"?, "descending"?, "limit"?: up to 500, "title"? }: the rows are filled in on the server from the result, never typed out.

Ask Add a section on paid social to that report.

Arguments 2
doc_idstringrequired

opsobject[]required

Up to 50 block operations, applied in order.

share_doc

docs:share

Share a doc

Create a share link that opens the doc in the workspace's brand, without signing in. Names and emails are always hidden on shared pages.

Ask Give me a share link to that report that always shows last month.

Arguments 9
doc_idstringrequired

expires_atstring

ISO date after which the link stops working.

fixed_windowstring

The period the link always shows, e.g. "previous month" (moves forward each month) or "2026-10-01..2026-10-31". Omit to follow the doc's own date range.

allow_date_changeboolean

Whether viewers may change the date range. Defaults to false with fixed_window, true without.

titlestring

What the shared page is called for its viewers, e.g. "Acme, October report". Defaults to the doc title.

passwordstring

At least 6 characters. Required for show_contact_details.

show_contact_detailsboolean

Show full emails and phone numbers in lists of people. Needs a password. Off by default.

website_idsstring[]

Websites the link may show (a switcher when several). Defaults to the doc's website.

allow_controlsstring[]

Older form of allow_date_change: every control viewers may change.

list_alerts

automations:read

List the alerts set up on a website

The Slack and email alerts on this website: what triggers each (an event such as a new lead or a won deal, a schedule, or a number crossing a line), where it sends, whether it is on, and when it last sent. Also returns the events and templates available, which create_alert takes.

Ask Which alerts do we have, and when did each last fire?

Arguments 1
websitestring

Website domain. Only needed when the credential covers more than one.

create_alert

automations:write

Create a Slack or email alert

Creates an alert on this website that emails people or posts to a Slack channel. Start from a template (list_alerts returns them with their specs) and pass `spec` with your changes; `deliver` is required, e.g. [{"channel":"email","to":["sam@acme.com"]}]. Slack targets need a channel id from a Slack workspace connected in the app ({"channel":"slack","connectionId":"...","channelId":"C123"}). Thresholds take a `condition` like "spend > 150 and conversions = 0". The alert is on as soon as it is created; call run_alert with test true to send a sample.

Ask Email me whenever a campaign spends 150 in a day with no leads.

Arguments 4
websitestring

Website domain. Only needed when the credential covers more than one.

namestring

What the alert is called, e.g. "New demo booked".

template_idstring

The template it started from, e.g. "new_lead".

specobjectrequired

The alert spec: trigger, filters, gather, condition, compose, deliver, pacing. Take it from a template and edit it.

run_alert

automations:write

Send an alert now

Sends an alert to its destinations now, built from the latest real data. With test true the message is marked as a test. Use to check an alert, or to get a digest on demand.

Ask Send the weekly digest now as a test.

Arguments 2
alert_idstringrequired

From list_alerts.

testboolean

Mark the message as a test. Default true.

Write outcomes back

Close the loop. These change your data, and a contact write carries on into 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 13

Changes propagate to any connected CRM. A write that changes nothing writes nothing. Attribution is computed by Sourceloop and cannot be set.

contact_idstringrequired

The contact id: the id column of get_records kind="contacts", or a contact hit from search.

statusstring

Lead status, the same field as lead_status. Must be a status this workspace defines; an unknown name is refused with the allowed list.

lifecycle_stagestring

Lifecycle stage name from this workspace, e.g. "Customer". Unknown names are refused with the allowed list. Propagates to the connected CRM.

lead_statusstring

Lead status name from this workspace. Unknown names are refused with the allowed list. Propagates to the connected CRM.

qualitystring

Lead quality: good, not_a_fit or spam. Spam leaves every report and is never sent to a CRM or ad platform. "pending" hands the lead back to the workspace's Lead Rules.

Allowed values 4

  • good
  • not_a_fit
  • spam
  • pending

qualifiedboolean

Deprecated: use quality. true means good, false means not_a_fit.

lead_scorenumber

sales_valuenumber

What the deal was actually worth.

quote_valuenumber

currencystring

notesstring

is_spamboolean

Deprecated: use quality "spam".

custom_fieldsobject

Values for the workspace's custom fields (Service, State...), by field key, e.g. {"service": "AC repair"}. null clears one. A value set here is never replaced by a form answer or a lead rule. Unknown keys are refused with the list of fields.

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_idstringrequired

The company id: the id column of get_records kind="companies", or a company hit from search.

company_namestring

industrystring

employee_countnumber

annual_revenuenumber

size_rangestring

countrystring

regionstring

citystring

website_urlstring

linkedin_urlstring

descriptionstring

lifecycle_stagestring

A 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.

Track every conversion to its true source

Capture and send full attribution data from every signup, lead, booking, and sale to your CRM and ad platforms, so you know exactly what's driving revenue.

Without SourceLoop

Untagged

Kayden Floyd

kayden@abc.com

  • SourceUnknown
  • MediumUnknown
  • CampaignUnknown
  • Landing pageUnknown
Journey
No touchpoints captured

With SourceLoop

Auto-tagged

Kayden Floyd

kayden@abc.com · Acme Co.

  • Channel Paid Social
  • CampaignFree_demo
  • Landing page/pricing
Journey
Synced to HubSpot Google Ads Meta