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
websitestringWebsite 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
topicstringOne section only.
Allowed values 6
metricsdimensionsrecordssqlplaybooksfilters
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
websitestringWebsite 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.
websitestringWebsite 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"].
granularitystringAdd a time column: one row per bucket (per dimension value), in the workspace time zone.
Allowed values 5
hourdayweekmonthquarter
periodstringPeriod in plain language: "last 30 days", "yesterday", "July 2026", or "2026-07-01..2026-07-31". Defaults to the last 30 days.
fromstringStart day (YYYY-MM-DD), instead of period.
tostringLast 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".
matchstringCombine filters with AND (all, default) or OR (any).
Allowed values 2
allany
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.
outcomestringWhat 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
conversionsconversion_typecontact_stagedeal_statussubscription_statuspayment_revenuerevenue
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.
grainstringWHO 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
contactscompanies
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.
journeystringWhose 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
personcompany
compare_to_previousbooleanAdd each metric's previous-period value and change, and the previous totals.
sort_bystringRank rows by this metric, largest first. Matters only when more rows exist than `limit`.
limitnumberRows to keep (series, with granularity). Default and max: as many as fit 200,000 rows.
include_creativebooleanWith by=["ad"], also keep each ad's thumbnail URL, headline, landing page and format.
queryobjectInstead 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_rowsnumberRows 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).
kindstringrequiredAllowed values 11
ad_entitiesad_changesad_search_termsad_pacingcontactscompaniesdealssubscriptionspaymentssessionsevents
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. For ad_changes, ad_search_terms, ad_pacing (default last 7 days), contacts, deals.
entity_typestring[]ad_entities, ad_changes: which kinds of entity.
statusarraystringad_entities: platform statuses, e.g. ["ENABLED"]. deals: open, won or lost.
actor_typestring[]ad_changes: who made the change.
campaign_idstringAd kinds: one campaign (platform id) and what sits under it.
platformstringAd kinds: e.g. google_ads, meta_ads, microsoft_ads.
as_ofstringad_entities: settings as they stood at the end of this day (YYYY-MM-DD).
textstringad_entities: name or keyword contains; companies: name or domain contains; deals: name contains.
min_spendnumberad_search_terms: spent at least this much (workspace currency).
zero_conversionsbooleanad_search_terms: only terms with no platform conversion.
channelstringcontacts, companies, deals: first-touch channel.
sourcestringmediumstringcampaignstringcontacts: first-touch campaign.
latest_channelstringlatest_sourcestringlatest_campaignstringtypestringcontacts: conversion category.
event_namestringcontacts: exact conversion event.
lifecycle_stagestringowner_emailstringcompany_domainstringemailstringcontacts: needs pii:read.
phonestringcontacts: needs pii:read.
include_spambooleanmin_valuenumbercontacts: sales value at least; deals: amount at least.
pipeline_idstringaccount_idstringdeals: one company's deals.
industrystringcountrystringhas_dealsbooleaninclude_personalbooleanevent_typestring[]payments: provider event types to keep.
event_type_filterstringevents: pageview, click, form_submit, custom...
device_typestringsessions: desktop, mobile, tablet.
anonymous_idstringsessions, events: one visitor (device).
conversions_onlybooleanevents: only events that are conversions.
limitnumberRows at most. Default and max 200,000.
preview_rowsnumberRows shown inline (default 50 for metrics, 30 for records, at most 500). The full result is saved either way.
search
metrics:read Search by name
Finds things by name across every ad account and the CRM: campaigns, ad groups, ads, keywords and negative keywords (name or keyword text contains), contacts (name or company; email needs pii:read), companies (name or domain) and deals (name). Use it to resolve "the Brand US campaign" to its id, or to answer "is 'emergency plumber' targeted anywhere". Each hit carries its kind, id, parent ids and status, and the full list is saved as a result.
Ask Is 'emergency plumber' targeted in any of our campaigns?
Arguments 5
For performance of what you found, pass its id to query_metrics as a filter (e.g. filters=["ad_campaign:eq:<campaign_id>"]).
textstringrequiredWhat to look for; case-insensitive, matched anywhere in the name.
kindsstring[]Where to look. Default: every ad entity kind, plus companies and deals the connection may read.
statusstring[]Ad entities only, e.g. ["ENABLED"].
websitestringWebsite domain, e.g. "acme.com". Omit when the account has only one.
preview_rowsnumberRows 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.
kindstringrequiredAllowed values 3
funnelpathsstages
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.
fromstringStart day (YYYY-MM-DD), instead of period.
tostringLast day, inclusive.
funnel_idstringfunnel: which saved funnel.
compare_to_previousbooleanfunnel: also count the window before.
stagesstring[]stages: keys in order, e.g. ["lead","mql","customer"].
stepstringpaths: what one step is. Default channel.
Allowed values 4
channelsourcemediumcampaign
populationstringpaths: journeys behind outcomes (default) or every visitor's journey.
Allowed values 2
convertedvisitors
outcomestringWhat 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
conversionsconversion_typecontact_stagedeal_statussubscription_statuspayment_revenuerevenue
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.
grainstringWHO 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
contactscompanies
eventsstring[]Only count these conversion EVENT names, e.g. ["demo_booked"]. Takes precedence over types.
max_touchesnumberpaths: steps kept before a path is cut with "...". Default 5.
limitnumberpaths: paths to keep. Default 1000.
preview_rowsnumberRows 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.
kindstringrequiredAllowed values 3
ltvcurverevenue_cohorts
websitestringWebsite domain, e.g. "acme.com". Omit when the account has only one.
periodstringWhich customers: those acquired (first paid) in this period. Defaults to the last 30 days; cohorts usually want 6 to 12 months.
dimensionstringSplit by acquisition channel, source, medium, campaign (ltv, curve, revenue_cohorts) or ad_platform (revenue_cohorts).
Allowed values 5
channelsourcemediumcampaignad_platform
grainstringcurve and revenue_cohorts: the age unit. Default month.
Allowed values 3
dayweekmonth
periodsnumberrevenue_cohorts: ages to return, default 12, at most 60.
spendbooleanrevenue_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_rowsnumberRows 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_idstringFrom get_records kind="contacts" (id) or search.
company_idstringFrom get_records kind="companies" or search. Merges everyone at the company.
deal_idstringFrom get_records kind="deals" or search. Merges everyone on the deal.
daysnumberHow far back to look. Default 365.
limitnumberMaximum 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.
sqlstringrequiredOne SELECT (or WITH ... SELECT). Tables are result ids.
preview_rowsnumberRows 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_idstringrequiredA 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_bystringSort by this column.
descendingbooleanoffsetnumberRows to skip. Default 0.
limitnumberRows 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_idstringrequiredA result id (r_ followed by 16 letters and digits) from a prepare tool, query_result or list_results.
formatstringDefault csv.
Allowed values 3
csvjsonlparquet
file_namestringName 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
limitnumberDefault 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
websitestringWebsite 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_idstringrequiredThe 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
websitestringWebsite domain, e.g. "acme.com". Omit when the account has only one.
titlestringtemplatestringA template key, e.g. client_report, ad_scaling_plan, traffic_dashboard.
markdownstringThe 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_idstringrequiredopsobject[]requiredUp 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_idstringrequiredexpires_atstringISO date after which the link stops working.
fixed_windowstringThe 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_changebooleanWhether viewers may change the date range. Defaults to false with fixed_window, true without.
titlestringWhat the shared page is called for its viewers, e.g. "Acme, October report". Defaults to the doc title.
passwordstringAt least 6 characters. Required for show_contact_details.
show_contact_detailsbooleanShow 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
websitestringWebsite 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
websitestringWebsite domain. Only needed when the credential covers more than one.
namestringWhat the alert is called, e.g. "New demo booked".
template_idstringThe template it started from, e.g. "new_lead".
specobjectrequiredThe 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_idstringrequiredFrom list_alerts.
testbooleanMark 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_idstringrequiredThe contact id: the id column of get_records kind="contacts", or a contact hit from search.
statusstringLead status, the same field as lead_status. Must be a status this workspace defines; an unknown name is refused with the allowed list.
lifecycle_stagestringLifecycle stage name from this workspace, e.g. "Customer". Unknown names are refused with the allowed list. Propagates to the connected CRM.
lead_statusstringLead status name from this workspace. Unknown names are refused with the allowed list. Propagates to the connected CRM.
qualitystringLead 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
goodnot_a_fitspampending
qualifiedbooleanDeprecated: use quality. true means good, false means not_a_fit.
lead_scorenumbersales_valuenumberWhat the deal was actually worth.
quote_valuenumbercurrencystringnotesstringis_spambooleanDeprecated: use quality "spam".
custom_fieldsobjectValues 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_idstringrequiredThe company id: the id column of get_records kind="companies", or a company hit from search.
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.