MCP v2.5.0
October 21, 2026
inspect_site sees pixels a tag manager injects — when a browser renderer is configured, inspect_site now also loads the final page once in a real headless browser and merges the ad pixels that actually fired into the report.
detection.scan_methodis always present:browserwhen the browser pass ran,staticotherwise.- Every
detection.ad_pixels[]entry gainsplatform(facebook,google,tiktok,linkedin,bing,x,reddit,pinterest,snapchat,quora, ornull). - Pixels the browser found carry
via: "browser"andmatched_byvalues prefixedbrowser:. A vendor seen in both the page source and the browser hasvia: "both"and lists both reasons. detection.tag_managersgains aGoogle Tag Managerentry when the page source showed none but the browser initialised one.- A new top-level
browser_scanblock isnullwhen no browser pass ran, otherwise{ final_url, requests, elapsed_ms, error }. - Tags that wait for consent and never fire on load are still not visible. Without a renderer configured, only
scan_method: "static"andplatformare new.
get_workspace_context now returns a business object describing the company.
businessisnulluntil setup step 1 (“About your business”) has been saved. After that it containssummary,type(ecommerce,saas,lead_gen,services, orother),website,platform,lead_destination,uses_native_lead_forms, andad_platforms.summaryis up to three sentences on what the company sells, to whom, and how a customer converts. It is drafted from the website and editable in Setup → step 1.- Existing fields are unchanged.
MCP v2.4.0
October 15, 2026
MCP read-only tokens — when you generate an MCP access token under Settings → Integrations → MCP, you can now tick Read-only.- A read-only token carries the ability
mcp_space:{id}:readinstead ofmcp_space:{id}. The write toolsupdate_contact,update_company, andupdate_eventare hidden fromtools/list, and calling one returns a “Tool not found” error. Every other tool, resource, and prompt works the same. - Read-only tokens show a Read-only badge in the token list, count toward the same 25-token limit, and are revoked the same way.
- Existing tokens keep full access, and a token can’t be converted — generate a new read-only one. OAuth-connected clients are unchanged and always get full access.
v1.5.0
October 16, 2026
All ids are returned as strings — everyid, *_id, and *_ids value in every API response and export file is now a JSON string.
- Ids can exceed 2^53, and JavaScript clients rounded them when they were returned as numbers.
- Events already documented string ids and now return them. Contacts and Companies ids were documented as integers and are now strings, including
export_id,company_id,contact_id,contact_ids, andconflicting_company_id. - Path and body parameters accept ids as before.
- MCP tool outputs are unchanged.
v1.4.1
September 29, 2026
Contacts dataset:tracking_ids column — the Contacts dataset has a new optional column, tracking_ids.
tracking_idsis an uncapped JSON array of strings: every distincttracking_idsent on the contact’s events, including values inherited from contacts merged into it. It’s[]when none, and off by default.- It sits in the Identity graph group, next to
emails,phones, andcomet_tokens. - Existing exports are unaffected. Create a new Contacts export with the column enabled to get it.
v1.4.0
September 23, 2026
New endpoint: Update Event —PUT /events/{id} updates fields on an already-ingested event using sparse PUT semantics.
amountis the updatable field today:number | null,>= 0; numeric strings like"120.25"are accepted, andnullclears the amount. At least one updatable field must be provided.- Returns
200with{ "message": "Event updated successfully" }, and404when the id does not exist in the caller’s Space. - Rate limited at 60 requests per minute per Space, the same bucket as Update Contact and Update Company.
- New MCP tool
update_eventmirrors the endpoint. It requiresamountand cannot clear it tonull.
v1.3.0
September 9, 2026
Company external ID on Create Event —POST /events/track now accepts two new optional fields: company_external_id and company_name.
company_external_idgroups this contact into a company using your own account or company identifier. It’s trimmed, case-preserved (never lowercased), and stored byte-exact — 1 to 190 characters after trimming. Floats, arrays, and booleans are rejected with a422.company_nameis a display name for the company identified bycompany_external_id— 1 to 255 characters after trimming, same type and validation rules. Last write wins, and it’s never used for matching: two companies with the same name stay separate. It’s ignored whencompany_external_idisn’t also sent.- Blank strings for either field are treated as absent (
null). - This is unrelated to
tracking_id:tracking_idmerges events into a single contact and never groups companies, whilecompany_external_idgroups contacts into a company and never merges contacts or takes part in contact matching. Never send a shared account ID astracking_id. company_external_idis only honored when the Space’s company identity mode is set to External ID in Space Settings. In the default Auto mode (companies grouped by business-email domain), the value is accepted and stored but ignored for grouping.
external_id on the Companies API — Get Company, List Companies, and Update Company now expose external_id, the customer-supplied external ID counterpart to domain.
external_idis an identifier, not just a label: it decides which company future events carrying thatcompany_external_idare matched to, the same waydomaindecides matching by email domain.- Update Company now accepts
external_id(string | integer | null) with the same semantics asdomain: trimmed, but case is preserved (byte-exact —ACCT_1andacct_1are different ids), 1 to 190 characters, integers stored as their decimal string, and a blank string treated asnull. - Setting an
external_idthat already belongs to another company in the Space is rejected with409—{ message, field, conflicting_company_id }— and nothing is written.fieldcan now bedomainorexternal_id. - Sending
external_id: nullreleases the company’s claim on the identifier; the company keeps itsid, history, and contacts. - The “at least one field” validation message on Update Company is now “At least one of domain, external_id or name must be provided.”
external_idis only honored by event ingestion when the Space’s company identity mode is External ID. In the default Auto mode, the value is stored and returned through this API but ignored for grouping.
PUT /contacts/{id}/company and DELETE /contacts/{id}/company let you move a contact onto a different company, or off its company entirely, without waiting for another event.
- A contact belongs to at most one company per Space — attaching moves it off any company it currently belongs to.
- The contact’s conversions, touchpoints, and visit metadata are re-stamped with the new (or
null) company id in the same transaction, so company-level reports and the Companies dataset in the data warehouse follow immediately. - Both calls are idempotent: repeating an attach with the contact’s current company id, or detaching an already-company-less contact, re-stamps rows (or no-ops) with a
200rather than an error. - Rate limited at 60 requests per minute per Space, the same bucket as Update Contact.
- What happens next depends on the Space’s company identity mode: in Auto mode, the tracker only assigns a company to a contact that currently has none, so a manual attach sticks and a manual detach is undone by the contact’s next business-email event; in External ID mode, the next event carrying a different
company_external_idre-points the contact again either way. - There is no MCP tool for these endpoints yet.
Company External ID and Company Name are now available as mapping destination fields on every webhook integration, alongside your existing field mappings. They map to the same company_external_id and company_name keys as Create Event.
Company Tracking settings — a new Space Settings → Additional Setup → Company Tracking page holds the company identity mode (Auto, the default, groups companies by business-email domain; External ID groups them by company_external_id alone) alongside the ad-platform conversion identity mode. company_external_id sent on events is only honored in External ID mode.
New guide: Account-level attribution for B2B — Account-level attribution for B2B (company external ID) walks through using company_external_id to attribute a conversion by one teammate to an ad another teammate clicked, including webhook/Stripe metadata mapping and the ad-platform conversion identity setting.
v1.2.0
September 3, 2026
Email lookup for List Contacts —GET /contacts now accepts an email query parameter to look contacts up by exact address instead of, or in addition to, a creation-date range. Accepts a single address, a comma-separated list, or a repeated email[] parameter — up to 100 addresses per request.
- Matching is exact (not partial or fuzzy) and case-insensitive, with surrounding whitespace trimmed. It resolves against the contact’s primary email, all secondary emails, and emails captured on profiles that were later merged into the contact, so an address that only ever appeared on a merged-away profile still finds the surviving contact.
start_date/end_dateare now optional whenemailis supplied, but remain required as a pair otherwise — sending only one is a422regardless ofemail.emailand a date range can be combined; both conditions AND together.- No match returns a normal
200with an emptydataarray, never a404, and several distinct contacts can legitimately share an address — all of them are returned. - The rate limit is unchanged at 15 requests per minute per Space, which is why the batch form exists: batch up to 100 addresses into a single call rather than one call per address for bulk reconciliation.
- The
list_contactsMCP tool supports the sameemailparameter (as an array only — no comma-separated string form).
GET /contacts/by-email/{email} and GET /contacts/by-email?email= look up a single contact by address, for when you expect exactly one match. The response is identical to Get Contact, including all of its optional query parameters (include_comet_tokens, include_events, hide_direct_touchpoints, use_custom_field_labels, include_browsing_session_data).
- Resolution uses the same rules as the
emailfilter above: exact, case-insensitive, whitespace-trimmed, spanning the contact’s primary email, secondary emails, and emails from merged-away profiles. - Returns
404when no contact matches, and409withcontact_idswhen more than one contact matches — mirroring the409on Delete Contact PII. Use the List Contactsemailfilter to see all matches, or request one of the returnedcontact_idsviaGET /contacts/{id}. - Rate limited at 30 requests per minute per Space — the same bucket as Get Contact, not the 15/minute limit on List Contacts.
MCP v2.3.0
September 2, 2026
Personal access tokens (PATs) are now available for server-side and headless MCP agents. If you’re running a CI job, backend service, or any script without a browser, you no longer need to work around the OAuth flow. Generate a token directly from Settings → Integrations → MCP under MCP Access Tokens, name it, and send it asAuthorization: Bearer like any other MCP token.
Key details:
- A personal access token behaves exactly like an OAuth-issued token: tied to your user account, working for a single Space, with your Space access re-checked on every request.
- Tokens don’t expire. Revoke them when the agent is decommissioned.
- You can generate tokens until you hold up to 25 MCP tokens for a Space; tokens issued to OAuth clients count toward that number.
- All MCP tokens, OAuth- and PAT-issued alike, are listed and revocable from the same Settings → Integrations → MCP page.
.mcp.json config example.
v1.1.1
September 2, 2026
Company identity rebuild — a company’s stable identity is now its numericid, matched via lookup tables keyed on an email domain and/or a customer-supplied external ID (each unique per Space). This changes domain semantics across the Companies endpoints:
domainis now nullable on Get Company and List Companies. It’snullfor a company identified only by an external ID, with no associated email domain.- Update Company:
domainis an identifier, not just a label — changing it moves the company’s claim on that domain. Values are trimmed and lowercased before storing (a blank string is treated asnull), matching ingestion. Sendingdomain: nullreleases the company’s claim on its domain: the company keeps itsid, history, and contacts, but future events from that domain will create or match a different company. If the new domain already belongs to another company in the Space, the request is now rejected with409 Conflict—{ message, field: "domain", conflicting_company_id }— and nothing is changed. name: nullis now rejected. The tracker’snamecolumn is required, so sendingname: nullon Update Company now returns422(“The name must be a string and cannot be null.”) instead of clearing the value. Omit the key to leavenameuntouched.- Companies dataset (Data Warehouse):
domainis no longer a locked column — it’s optional and nullable, matching the API. A new optionalexternal_idcolumn is available (the customer’s own company identifier).idandcreated_atremain locked.
external_id is not yet exposed on the Companies API responses — that’s tracked separately.
v1.1.0
August 7, 2026
Delete Contact PII endpoint — AddedDELETE /contacts/{id}/pii and DELETE /contacts/pii?email={email} for permanently scrubbing a contact’s personally identifiable information. Exactly one of id or email must be provided. The request validates and resolves the target contact synchronously, then queues the scrub and returns 202 Accepted with { contact_id, status: "queued", message } — call Get Contact afterward to confirm the fields have cleared.
- Deleted: all emails, phones, names, locations, devices, IPs, tracking IDs, and comet tokens for the contact (including data inherited from merged profiles), all raw browsing-session hits, and the contact’s own
name,email,phone,location,city,state,country,device_type,os,browser,language, and all 30 custom fields. - Kept: the contact record (
id,created_at), its events and journey, and its merge history. Event metadata (webhook payloads, URL query strings) can still contain PII — delete individual events via Delete Event if needed. - Returns
404if theidoremaildoesn’t resolve to a contact in your space, and409withcontact_idsifemailmatches more than one distinct contact — nothing is deleted in that case. Unlike Delete Event, this endpoint is not idempotent on missing targets.
MCP v2.2.0
June 18, 2026
OAuth Client ID Metadata Documents (CIMD) are now supported and are the preferred registration path for new custom MCP clients. Instead of calling the registration endpoint, you host a JSON metadata document at a stablehttps:// URL and use that URL as your client_id — no Dynamic Client Registration call required. Cometly fetches, validates, and caches the document on the first /authorize request.
Key details:
- The document must include
client_id(equal to its own URL),client_name,redirect_uris, andtoken_endpoint_auth_method: "none". - Redirect URIs must be loopback or same-origin https (same scheme, host, and port as the
client_idURL). Cross-origin https and custom URI schemes are not accepted for CIMD clients — use DCR for custom schemes. - The document is cached up to 24 hours (respecting
Cache-Control: max-age; default window 15 minutes). - Support is advertised as
client_id_metadata_document_supported: truein the authorization server metadata. DCR remains available as a fallback.
MCP v2.1.2
June 10, 2026
Major hosted MCP clients now complete OAuth Dynamic Client Registration out of the box, with no operator configuration required. Claude (claude.ai), ChatGPT, Cursor, VS Code for the Web, Grok, Gemini, and Perplexity are all accepted by default. See Authentication for the full host list.MCP v2.1.1
June 3, 2026
Dynamic Client Registration now accepts a partialredirect_uris array. Previously, if any single URI in the redirect_uris array failed the redirect URI policy, the entire registration was rejected with 400 invalid_redirect_uri. Now the endpoint registers only the valid subset and drops the rest — a 201 response’s redirect_uris field reflects the accepted URIs, which may be fewer than you submitted. The registration is only rejected outright when none of the supplied URIs are valid. The per-URI validation rules (loopback, allowlisted https host, approved custom scheme, reverse-DNS scheme) are unchanged. See Authentication for the full policy table.
v1.0.8
May 26, 2026
Bug fixes and behavioral improvements forcustom_field_filters on List Contacts and List Events:
- Age operators on date fields now work.
age_greater_than,age_equal_to, andage_less_thanwere documented but incorrectly rejected with a 422 when passed a whole-number day count. The validator now accepts numeric values for these operators on date fields (profile_field_26–profile_field_30). - Date filter comparisons are timezone-aware.
equal_to,greater_than,less_than, andbetweenon date fields now evaluate against your space’s configured timezone.equal_toexpands to a full midnight-to-midnight range for the given day;greater_thancompares against end-of-day. Previously these operators compared raw strings, which would miss column values with a stored time component. - Numeric
any/unknownsemantics. For numeric fields,anymatches contacts where the value is greater than0;unknownmatches contacts where the value is0or unset. not_equal_toandnot_containsno longer include unset values. Previously these operators could return contacts where the field was NULL or empty. If you need those contacts, add a separateunknowncondition ANDed alongside.- Text filter literals. The
_and\characters incontains,starts_with,ends_with, andcontains_wordfilter values are now treated as literal characters instead of LIKE wildcards.
MCP v2.1.0
May 21, 2026
Removed the four export tools —request_event_export, request_contact_export, request_company_export, and get_export_status. Bulk extraction is better handled by the Public API V1 export endpoints (programmatic) or the in-app CSV export buttons (interactive). Routing the rows through an MCP turn just consumed tool slots and added a polling round-trip without giving the agent any analytical leverage. Use query_events_aggregated, query_contacts_aggregated, or the various query_ad_metrics_* tools when the agent needs to reason about large datasets — not export them.
The MCP catalog is now 50 external tools across 14 toolsets (down from 54 / 15).
MCP v2.0.0
May 15, 2026
Major expansion of the Cometly MCP server — from 7 read-only tools to 54 external tools across 15 toolsets, plus the first MCP Resources and Prompts. Backwards compatible: every v1 tool kept its original input/output shape, withformat: "csv" | "json" | "yaml" added as an opt-in.
New toolsets (49 new tools):
- Workspace orientation —
get_workspace_context,list_custom_events,list_my_spaces,list_metric_catalog,list_filterable_fields,list_user_defined_metrics,resolve_date_range. - Search —
search_workspace. - Analytics depth —
query_ad_metrics_compare,query_attribution_models,query_metric_breakdown. - Event aggregations —
query_events_aggregated,list_event_types. - Journeys —
get_contact_journey,query_contacts_aggregated,get_company_journey. - Ads Manager hierarchy —
list_ad_accounts,list_campaigns,list_adsets,list_ads,get_ad_entity. - Reports & dashboards —
list_dashboards,get_dashboard,list_reports,get_report_definition,run_saved_report,analyze_report,list_subscriptions,get_subscription_last_run. - Segments —
list_segments,get_segment,query_segment_membership. - Integrations —
list_integrations,get_pixel_status,list_event_mappings. - Health & diagnostics —
get_space_health,debug_data_flow, plus the existinginspect_site/verify_pixel_events. - Webhook debugging —
list_recent_webhooks,get_webhook_event(payloads PII-redacted; prompt-injection patterns flagged in a warning footer). - Exports —
request_event_export,request_contact_export,request_company_export,get_export_status. (Removed in v2.1.0.) - AI helpers —
search_cometly_docs,get_chart_spec_template.
cometly://workspace/contextcometly://catalog/metricscometly://catalog/attribution-modelscometly://catalog/dimensionscometly://catalog/custom-events
analyze_performancecompare_attribution_modelsweekly_reviewfind_underperformers
- Custom event label resolution — analytics tools accept either
custom_event_2or the configured label (“Deal Created”) in their inputs. Output rewrites slot ids to labels with a# label_map:footer for round-tripping. - Empty-state hints — analytics tools annotate suspicious all-zero results with hints like “No facebook_ads account is connected to this space” or “Last google_ads sync was 3 days ago”.
- Token-budget pagination — responses cap around ~6,000 tokens with a
truncated_at_token_budget: truefooter + opaquenext_cursor. - CSV default for new tools — ~50% fewer tokens than JSON for tabular results. Legacy v1 tools keep JSON as default.
- DST caveat —
query_ad_metricsandresolve_date_rangesurface a DST advisory when the requested window crosses a transition. - Honest forecasting language — softened “anomaly detection” / “forecast” claims in tool descriptions to “directional reads” — we don’t run statistical models, the agent does threshold math.
external: falsefiltering — tools that only make sense in-app (chat history, file attachments) are hidden from external MCP clients via a server-levelshouldRegisterfilter.
v1.0.7
May 14, 2026
- Update Contact endpoint — Added PUT /contacts/ for updating an existing contact’s
emails,phones,names,locations, andcustom_fields. Uses sparse PUT semantics: any field you include replaces the existing collection in full; any field you omit is left untouched. Empty arrays ([]) wipe a collection; providingcustom_fieldsclears every key not in the object. - Update Company endpoint — Added PUT /companies/ for updating a company’s
domainandname. Uses sparse PUT semantics — only the fields you include are modified. namefield on Get Company — The Get Company response now includesnamealongsideidanddomain. This is an additive change; existing fields are unchanged.custom_field_filtersparameter on List Contacts — Addedcustom_field_filtersto the List Contacts endpoint. Accepts an array of AND-joined conditions (max 10) for filtering contacts by their custom profile field values. Each condition takes{ field, operator, value }(or{ field, operator, start, end }for thebetweenoperator). Thefieldmust reference aprofile_field_Nslot configured with a label in your space; operator vocabulary depends on the field’s type (text / number / date).custom_field_filtersparameter on List Events — Same parameter added to the List Events endpoint, narrowing results to events whose owning contact matches the conditions. Same shape and operator vocabulary as List Contacts.
v1.0.6
April 20, 2026
include_browsing_session_dataparameter on Get Contact — Addedinclude_browsing_session_datato the Get Contact endpoint. When set to1, the response includes abrowsing_session_hitsarray with up to the last 1000 raw browsing hits (page views) for the contact, ordered by most recent first. Each hit includesevent_url(full URL),referrer_host,source,country,device_type,browser,os,event_name, andevent_time_utc.
v1.0.5
April 7, 2026
include_eventsparameter on Get Contact — Addedinclude_eventsto the Get Contact endpoint. When set to1, the response includes aneventsarray containing the contact’s full journey — all conversions and touchpoints sorted by most recent first. Conversion events includeevent_name,amount,event_url,order_id,order_name,channel(browser/server), andintegration. Touchpoint events includesource,touchpoint_url,referrer_url, and the full ad hierarchy (ad_name,adset_name,campaign_name,account_name).hide_direct_touchpointsparameter on Get Contact — Addedhide_direct_touchpoints(default1) to the Get Contact endpoint. Wheninclude_events=1, direct touchpoints are excluded by default. Set to0to include them.
v1.0.4
March 19, 2026
use_custom_field_labelsparameter — Addeduse_custom_field_labelsto the List Contacts and Get Contact endpoints. When set to1, custom field keys use user-defined labels (e.g."Customer Age") instead of raw column names (e.g."profile_field_1"). Fields without a configured label retain their raw column name.
v1.0.3
February 27, 2026
- Ad hierarchy fields on List Events — Added
ad_name,adset_id,adset_name,campaign_id,campaign_name,account_id, andaccount_nameas requestable fields on the List Events endpoint. These fields requiresourcesto be provided and return the ad, ad set, campaign, and account data from the attributed touchpoint.
v1.0.2
February 19, 2026
include_all_emailsparameter — Addedinclude_all_emailsto the List Contacts and Create Contact Export endpoints. When set to1, returns all email addresses associated with a contact, including emails from merged profiles.
v1.0.1
February 17, 2026
- Custom fields on Create Event — The Create Event endpoint now accepts custom profile fields (
profile_field_1throughprofile_field_30) as root-level properties. Fields 1-15 accept text, 16-25 accept numbers (string or number), 26-30 accept dates. event_urlfield — Addedevent_urlas a requestable field on the List Events and Create Event Export endpoints. Returns the full URL where the event occurred.