API reference
The BusinessMCP platform API
Everything is one workspace behind one key. Call your hosted MCP server, read the registry feed, send analytics events (including the tracker’s server-side goal and identify endpoints), and manage tasks — all with real, copyable examples.
Base URL
https://businessmcp.comAuthentication
Authorization: Bearer mcph_...Create a Bearer mcph_* key in your dashboard Settings. The same key authenticates both the MCP endpoint and the REST tasks API. See expose your MCP endpoint for the full walkthrough.
Endpoints
/api/spec/toolsNoneThe full tool catalogue, generated from the running server: every platform AND connector tool (with the provider that must be connected), the data classes each touches, its MCP annotations, cost class and plan gate, plus the ext_* gateway namespace, prompts, resources, the access presets and the response conventions (error envelope, pagination, units). Add ?schemas=1 for input schemas. Public and uncached-by-key, so you can review exactly what a scoped key could reach before you issue one.
Request
curl -s 'https://businessmcp.com/api/spec/tools' | jq '.counts'Response
{
"total": 112,
"platform": 66,
"connector": 46,
"actions": 28,
"unclassified": 0
}/.well-known/oauth-protected-resourceNoneRFC 9728 protected-resource metadata, naming the authorization server. This is what Claude.ai and ChatGPT read to discover how to authorize — their connector UIs accept OAuth only and have no field for a Bearer token, so this is the path those clients take. The authorization server itself is described at /.well-known/oauth-authorization-server.
Request
curl -s 'https://businessmcp.com/.well-known/oauth-protected-resource'Response
{
"resource": "https://businessmcp.com/api/mcp",
"authorization_servers": ["https://businessmcp.com"],
"bearer_methods_supported": ["header"]
}/api/mcpBearer mcph_*Your company’s hosted MCP server. Speaks the Model Context Protocol over JSON-RPC (streamable HTTP), spec revision 2026-07-28. The bearer key resolves your workspace and its access policy. Supports tools/list + tools/call, resources/list + resources/read (businessmcp://snapshot, ://sops, ://memory, ://brain/{query}), and prompts/list + prompts/get (weekly-growth-review, funnel-audit, and more). Output is scrubbed by the key’s policy. An unauthenticated request returns 401 with a WWW-Authenticate challenge pointing at /.well-known/oauth-protected-resource, so a spec-compliant client can discover the OAuth flow on its own.
Request
curl -X POST 'https://businessmcp.com/api/mcp' \
-H 'Authorization: Bearer mcph_your_key_here' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_analytics",
"arguments": { "window_days": 30 }
}
}'Response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{\"visitors\":1284,\"sessions\":1610,\"leads\":41,\"conversions\":18,\"revenue\":2489,\"units\":{\"visitors\":\"distinct people in the window\",\"revenue\":\"USD\"},\"window\":{\"since\":\"2026-08-05\",\"end\":\"2026-09-03\"},\"compare\":{\"change_pct\":{\"visitors\":12}}}"conversion_rate\":0.031,\"revenue_cents\":248900,\"range\":\"30d\"}"
}
]
}
}/api/registry/v0/serversPublicMCP Registry v0-shaped feed of servers in this registry, so the marketplace is machine-consumable by MCP clients and aggregators. Supports search, limit (max 200) and offset. Every entry advertises the streamable-HTTP remote.
Request
curl -X GET 'https://businessmcp.com/api/registry/v0/servers?search=stripe&limit=2&offset=0'Response
{
"servers": [
{
"name": "com.mcphosting/stripe",
"description": "Customers, payments and subscriptions",
"version": "1.0.0",
"status": "active",
"_meta": {
"com.mcphosting": {
"vendor": "Stripe",
"featured": true,
"listing_url": "https://businessmcp.com/mcp-servers/stripe"
}
},
"remotes": [
{ "type": "streamable-http", "url": "https://businessmcp.com/api/mcp" }
]
}
],
"metadata": { "count": 1, "total": 1, "next_offset": null }
}/api/analytics/eventPublic (origin-verified)The analytics beacon that track.js posts to. Cookieless and cross-origin; verifies the origin against your registered domain and responds fast so it never blocks page unload. You rarely call this by hand — the tracking script does — but the shape is public. type is pageview | goal | revenue | identify | engagement | exposure: a pageview carries pg {t, w, h, l} (title, viewport, language); an engagement beacon carries engaged_ms and scroll_pct for the page being LEFT and never counts as a pageview; identify is routed to the CRM, not the firehose. eid is the client event id (also emitted as the mcph:goal DOM event) that dedupes a server-forwarded conversion against your own pixel, v is the tracker build, and att is the first (ft) and last (lt) touch the tracker kept client-side. The body is JSON, but the Content-Type is text/plain: a browser must never send application/json here — a non-safelisted type forces a CORS preflight that a credentialed sendBeacon fails, and the beacon is silently dropped. The server JSON-parses the raw body regardless of Content-Type.
Request
# Content-Type stays text/plain (browsers must not send application/json — CORS preflight)
curl -X POST 'https://businessmcp.com/api/analytics/event' \
-H 'Content-Type: text/plain' \
-d '{
"trackerId": "YOUR_TRACKER_ID",
"type": "revenue",
"name": "purchase",
"eid": "e1m9x2k4abcd",
"v": "2.0.0",
"url": "https://yoursite.com/checkout/success",
"referrer": "https://google.com",
"revenueCents": 4900,
"visitorId": "v8f3a2c1b",
"metadata": { "plan": "pro" },
"att": {
"ft": { "src": "google", "med": "cpc", "cmp": "brand", "ids": { "gclid": "Cj0K…" }, "lp": "/pricing", "ts": 1756400000000 },
"lt": { "ref": "https://news.ycombinator.com/", "lp": "/", "ts": 1756486400000 }
}
}'Response
HTTP/1.1 204 No Content/api/tracker/{trackerId}/goalPublic (tracker id in the path)Trusted server-side goal — record a conversion from your own backend (a payment webhook, a job) that the browser tracker cannot see. Body: name (required), visitorId (the id from mcph.getVisitorId(), to stitch the journey), revenue_cents (number → a revenue event) and metadata (object). No user-agent bot detection runs on this path, so keep it server-to-server. Origin is checked only when a browser sends one; a server call without an Origin passes. Limits: 120/min per IP and 1,200/min per tracker; bodies over 4KB get 413. Errors: 400 missing name or invalid body, 403 foreign browser origin, 404 unknown tracker, 429 over limit. The sibling POST /api/tracker/{trackerId}/bot-hit records JS-less AI crawlers — see /guides/track-ai-crawlers-server-side.
Request
curl -X POST 'https://businessmcp.com/api/tracker/YOUR_TRACKER_ID/goal' \
-H 'Content-Type: application/json' \
-d '{
"name": "purchase",
"visitorId": "v8f3a2c1b",
"revenue_cents": 4900,
"metadata": { "plan": "pro", "order_id": "ord_1192" }
}'Response
{ "ok": true }/api/tracker/{trackerId}/identifyPublic (tracker id in the path)Server-side identify for funnels the client tracker cannot see inside — a cross-origin iframe (Typeform, Heyflow) or an off-domain funnel — pointed at from the provider’s webhook or your backend. Body: email (required, must be a real address — disposable and placeholder domains answer 400), visitorId (optional, for a full journey stitch when the funnel was entered with a decorated id) and traits (object; traits.source defaults to "webhook"). A business email auto-derives the company for enrichment. A workspace’s own teammates are never minted as leads: their address returns { ok: true, skipped: "teammate" }. Same limits and error codes as the goal endpoint.
Request
curl -X POST 'https://businessmcp.com/api/tracker/YOUR_TRACKER_ID/identify' \
-H 'Content-Type: application/json' \
-d '{
"email": "jordan@acme.com",
"visitorId": "v8f3a2c1b",
"traits": { "source": "typeform", "plan": "pro" }
}'Response
{ "ok": true }/api/tracker/{trackerId}/configPublic (tracker id in the path)The public per-site config track.js fetches on load to decide whether to show the support widget and whether to load the in-house heatmap/replay recorder (or a legacy Clarity/Hotjar id). Always answers 200: an unknown or disabled tracker returns { enabled: false } so the loader cleanly no-ops. Pass ?lang= (the browser language) to receive the widget strings translated when auto-translate is on; the IP country is the fallback. Cached 60s. Nothing here is secret — it is what every visitor’s browser already downloads.
Request
curl -X GET 'https://businessmcp.com/api/tracker/YOUR_TRACKER_ID/config?lang=de'Response
{
"enabled": true,
"config": {
"mode": "chat",
"position": "right",
"brandColor": "#2a75ba",
"headerTitle": "Chat with us",
"greeting": "Hi! How can we help?",
"showPhone": false,
"autoTranslate": true
},
"lang": "de",
"strings": { "header_title": "…", "greeting": "…" },
"heatmaps": {
"mode": "inhouse",
"sample_rate": 1,
"replay_enabled": true,
"mask_all_inputs": true,
"clarity_id": null,
"hotjar_id": null,
"exclude_paths": ["/u/*", "/book/*"]
}
}/api/v1/runsBearer mcph_*Drive a full Assistant run from code. POST a prompt (optionally with a thread_id to continue a conversation, and mode "deep" for a bigger, verified analysis pass) to queue a run; it returns the thread_id. This is the same Head-of-BI agent, tools and access policy as the dashboard Assistant.
Request
curl -X POST 'https://businessmcp.com/api/v1/runs' \
-H 'Authorization: Bearer mcph_your_key_here' \
-H 'Content-Type: application/json' \
-d '{
"prompt": "How did conversions trend this month, and which channel drove them?",
"mode": "deep"
}'Response
{ "thread_id": "th_7b21" }/api/v1/runsBearer mcph_*Poll a run by thread_id. Returns the messages and the latest assistant reply once the run finishes. Poll every second or two until the reply is present.
Request
curl -X GET 'https://businessmcp.com/api/v1/runs?thread_id=th_7b21' \
-H 'Authorization: Bearer mcph_your_key_here'Response
{
"thread_id": "th_7b21",
"status": "done",
"reply": "Conversions rose 18% MoM to 312, led by Organic Search (…).",
"messages": [ /* full turn history */ ]
}/api/v1/meBearer mcph_*Return the calling key’s scope: whether it has full access, its label, whether it can take actions, and the data classes it is permitted. Use it to verify a scoped key before relying on it.
Request
curl -X GET 'https://businessmcp.com/api/v1/me' \
-H 'Authorization: Bearer mcph_your_key_here'Response
{
"full_access": false,
"label": "Analytics-only",
"can_act": false,
"data_classes": ["behavior"]
}/api/v1/tasksBearer mcph_*Public REST surface for the CLI and external automations. GET lists tasks (filter by status and dept); POST creates a task. Authenticated with the same mcph_* key as your MCP endpoint.
Request
curl -X GET 'https://businessmcp.com/api/v1/tasks?status=todo' \
-H 'Authorization: Bearer mcph_your_key_here'Response
{
"tasks": [
{
"id": "t_9c21",
"title": "Draft Q3 SEO plan",
"dept": "Marketing",
"status": "todo"
}
]
}/api/v1/tasksBearer mcph_*Create a task and optionally seed it with a first message that kicks off an agent run. Requires a title; dept defaults to Operations.
Request
curl -X POST 'https://businessmcp.com/api/v1/tasks' \
-H 'Authorization: Bearer mcph_your_key_here' \
-H 'Content-Type: application/json' \
-d '{
"title": "Summarize last week\u2019s revenue by channel",
"dept": "Marketing",
"message": "Pull the numbers and post a short summary."
}'Response
{
"id": "t_9c34",
"title": "Summarize last week\u2019s revenue by channel",
"status": "todo"
}/api/v1/exportBearer mcph_*Programmatic CSV export. type=analytics (the multi-section overview report), type=sessions (one row per session) or type=contacts (the CRM). Analytics/sessions require the behavior data class, contacts require contact_pii; revenue columns are blank without the revenue class. Max 30 exports/hour.
Request
curl -X GET 'https://businessmcp.com/api/v1/export?type=sessions&range=30' \
-H 'Authorization: Bearer mcph_your_key_here' -o sessions.csvResponse
day,channel,referrer,country,device,...
"2026-08-27","Organic Search","google.com","US","desktop",.../api/v1/trackBearer mcph_*Record product-usage events from your own backend (Segment-style). Send {events:[…]} (up to 500), a single event object, or application/x-ndjson with one event per line; 1MB max. Each event takes a name (normalized to lowercase [a-z0-9_:-], max 64 — the normalized name is echoed back), an optional user_id (your product user id, resolved to the CRM contact and their web journey), visitor_id (the mcph_vid from the tracker), an ISO 8601 timestamp up to 30 days in the past (defaults to now; more than 5 minutes ahead is rejected), properties, and revenue_cents for purchases. Bad events are reported per index and never fail the batch. Events count toward your monthly events allowance; they are never dropped for quota.
Request
curl -X POST 'https://businessmcp.com/api/v1/track' \
-H 'Authorization: Bearer mcph_your_key_here' \
-H 'Content-Type: application/json' \
-d '{
"events": [
{ "name": "Invite Sent", "user_id": "u_42", "timestamp": "2026-09-05T10:12:00Z",
"properties": { "plan": "pro", "seats": 3 } },
{ "name": "Order Placed", "user_id": "u_42", "revenue_cents": 4900 },
{ "name": "identify" }
]
}'Response
{
"ok": true,
"accepted": 2,
"rejected": [ { "index": 2, "error": "`name` is reserved or normalizes to empty (use [a-z0-9_:-])" } ],
"events": [
{ "index": 0, "name": "invite_sent", "timestamp": "2026-09-05T10:12:00.000Z", "day": "2026-09-05" },
{ "index": 1, "name": "order_placed", "timestamp": "2026-09-06T08:00:01.120Z", "day": "2026-09-06" }
]
}/api/v1/identifyBearer mcph_* (crm_actions)Link your product user id to a CRM contact and merge traits. Pass user_id and/or email (one is required) plus an optional visitor_id to stitch the web journey; traits are merged, never clobbered. Disposable email domains are refused and your own workspace members are skipped. The calling server IP is never treated as the visitor.
Request
curl -X POST 'https://businessmcp.com/api/v1/identify' \
-H 'Authorization: Bearer mcph_your_key_here' \
-H 'Content-Type: application/json' \
-d '{ "user_id": "u_42", "email": "ana@acme.com", "traits": { "name": "Ana Silva", "plan": "pro" } }'Response
{ "ok": true, "contact_id": "c_7f31…", "created": false }/api/v1/groupBearer mcph_* (crm_actions)Upsert an account (your own company/tenant object) keyed by account_id, and attach the contacts behind user_ids to it. Only the fields you send are updated. Accounts are private to your workspace; plan, mrr_cents and seats are yours to define.
Request
curl -X POST 'https://businessmcp.com/api/v1/group' \
-H 'Authorization: Bearer mcph_your_key_here' \
-H 'Content-Type: application/json' \
-d '{ "account_id": "acct_acme", "name": "Acme", "domain": "acme.com",
"plan": "pro", "mrr_cents": 49900, "seats": 12, "user_ids": ["u_42", "u_43"] }'Response
{ "ok": true, "account_id": "acct_acme", "id": "a_10c2…", "created": true, "linked": 2, "unknown_user_ids": [] }/api/v1/contactsBearer mcph_* (contact_pii)The CRM as JSON — one record per contact with scores, subscription facts, account, company profile, identities and traits. Keyset-paginated on (updated_at, id): pass next_cursor back as ?cursor= until has_more is false, and ?updated_since=<iso> to pull only what changed (the incremental-sync loop). Filters: ?stage=<lifecycle|qualified>, ?account_id=<uuid>, ?email=. Visitor ids are counted, not listed, unless ?include=identities. Money fields are null without the revenue class; company_profile is redacted without enrichment. Max 200 per page, 600 requests/min per key. GET /api/v1/contacts/{id} returns one record with every identity listed.
Request
curl -s 'https://businessmcp.com/api/v1/contacts?updated_since=2026-09-01T00:00:00Z&limit=100' \
-H 'Authorization: Bearer mcph_your_key_here'Response
{
"items": [
{ "id": "c_7f31…", "email": "ana@acme.com", "name": "Ana Silva", "lifecycle_stage": "sql",
"first_channel": "Organic Search", "deal_value_usd": 12000, "ltv_usd": 499,
"subscription": { "status": "active", "plan": "pro", "mrr_usd": 499, "seats": 12, "trial_ends_at": null, "churned_at": null },
"scores": { "lead": 82, "band": "hot", "fit": 70, "intent": 90, "churn_risk": 10, "is_pql": true, "ltv_estimate_usd": 2500 },
"account": { "id": "a_10c2…", "external_id": "acct_acme", "name": "Acme" },
"company_profile": { "domain": "acme.com", "name": "Acme Inc", "industry": "computer software", "employee_range": "51-200" },
"identities": [ { "kind": "email", "value": "ana@acme.com" }, { "kind": "user_id", "value": "u_42" } ],
"visitor_id_count": 2, "traits": { "plan": "pro" },
"created_at": "2026-08-30T09:12:44.120331+00:00", "updated_at": "2026-09-05T18:02:10.551902+00:00" }
],
"has_more": true,
"next_cursor": "eyJ1IjoiMjAyNi0wOS0wNVQxODowMjoxMC41NTE5MDIrMDA6MDAiLCJpIjoiYzdmMzEifQ"
}/api/v1/accountsBearer mcph_* (contact_pii)Your accounts (the objects POST /api/v1/group writes) as JSON, keyset-paginated on (updated_at, id) with the same ?updated_since / ?cursor / ?limit contract as contacts. Filters: ?external_id=, ?domain=. mrr_usd is null without the revenue class.
Request
curl -s 'https://businessmcp.com/api/v1/accounts?updated_since=2026-09-01' \
-H 'Authorization: Bearer mcph_your_key_here'Response
{
"items": [
{ "id": "a_10c2…", "external_id": "acct_acme", "name": "Acme", "domain": "acme.com",
"plan": "pro", "mrr_usd": 499, "seats": 12, "traits": {},
"company_profile": { "domain": "acme.com", "name": "Acme Inc", "industry": "computer software", "employee_range": "51-200" },
"created_at": "…", "updated_at": "…" }
],
"has_more": false,
"next_cursor": null
}/api/v1/eventsBearer mcph_* (behavior)Sessions as JSON — one row per visit from the durable session store, the same rows the CSV export serves: day, visitor and contact ids, channel and UTMs, entry/exit pages, pageviews, engaged seconds, goals, revenue, geography, language and device. Window ≤90 days: ?from=YYYY-MM-DD&to=YYYY-MM-DD, or ?updated_since=<day>, default the trailing 30 days; keyset-paginated on (day, id) via ?cursor=; ?visitor_id= narrows to one person. revenue_usd is null without the revenue class; answers 402 while the workspace is traffic-locked.
Request
curl -s 'https://businessmcp.com/api/v1/events?from=2026-09-01&to=2026-09-06&limit=200' \
-H 'Authorization: Bearer mcph_your_key_here'Response
{
"items": [
{ "id": "s_…", "day": "2026-09-05", "visitor_id": "v_…", "contact_id": "c_7f31…",
"channel": "Organic Search", "referrer": "google.com", "entry_path": "/pricing", "exit_path": "/signup",
"pageviews": 4, "duration_seconds": 212, "bounce": false, "converted": true, "goal_names": ["signup"], "revenue_usd": 0,
"utm_source": null, "utm_medium": null, "utm_campaign": null, "utm_content": null, "utm_term": null,
"country_code": "US", "region": "CA", "city": "San Francisco", "language": "en", "device_type": "desktop", "os": "macOS", "browser": "Chrome" }
],
"has_more": false,
"next_cursor": null,
"window": { "from": "2026-09-01", "to": "2026-09-06" }
}Events API
/api/v1/track, /api/v1/identify and /api/v1/group feed product-usage data from your own backend into the same analytics, CRM and identity graph the tracking script writes to. The public tracker webhooks (/api/tracker/<id>/goal|identify) stay the no-auth surface for Zapier-style tools; the Events API needs a Bearer key because a user_id merge is a CRM write.
Identity and sessions
user_idis your product user id. It resolves to the contact (and through it to the visitor the tracker saw), so server events inherit the person’s first-touch channel and UTMs.- Server events form one session per user (or visitor, or anonymous) per UTC day — they never add page views, bounces or unique visitors.
- Event names are normalized exactly like
mcph.track();identify,pageviewandpingare reserved. revenue_centsrecords a purchase (and forwards to your ad platforms when server-side conversions are enabled).
Timestamps, batching and limits
timestampis ISO 8601 (or an epoch number). Up to 30 days in the past is accepted for backfills; a past day is re-processed by the next analytics run (within ~2 hours). More than 5 minutes ahead is rejected.- Up to 500 events per request, 1MB per body; NDJSON is accepted for streaming writers.
- Per-event errors come back as
rejected[{index, error}]; the rest of the batch is stored. A 400 means nothing was stored. - 600 requests/minute per key and 3,000/minute per workspace; a 429 carries Retry-After.
identifyandgrouprequire thecrm_actionsdata class on the key’s access policy.
Read API
GET /api/v1/contacts, /api/v1/contacts/{id}, /api/v1/accounts and /api/v1/events are the pull-sync surface for a warehouse, a reverse-ETL tool or your own backend — the same records the data-change webhooks push, so one parser serves both. Where the CSV export is a snapshot, these are incremental.
Pagination and sync
- Every list answers
{items, has_more, next_cursor}. Passnext_cursorback as?cursor=untilhas_moreis false. - Cursors are keyset (the last row seen), never offsets — stable while rows are being written. Treat them as opaque.
?updated_since=(ISO day or instant) is the incremental loop: store the newestupdated_atyou saw and ask for everything after it.?limit=up to 200 (default 50). Events span at most 90 days per call.- Money is
*_usddecimals;nullmeans unknown, never zero.
Access policy
- Contacts and accounts need the
contact_piiclass; events needbehavior. A key without it answers 403. - Without
revenue: deal value, LTV, MRR, LTV estimate and session revenue come backnull. - Without
enrichment: thecompany_profileblock reads[redacted]. - 600 requests/min per key, 3,000/min per workspace → 429 with
Retry-After. - Mint a scoped key under Settings → API & Vault; the presets there decide which classes it carries.
Outgoing webhooks
Subscribe an HTTPS endpoint in Settings → API & Vault and we POST workspace events to it: lead.created, visitor.hot, outreach.replied, meeting.booked and alert.raised — plus the data-change events contact.created, contact.updated, contact.scored, company.identified, account.updated and revenue.recorded (opt-in per endpoint). Payloads can contain lead names and email addresses — point them only at systems you trust. data.url is always absolute; data.entity is the contact (or a bare {type, id} for a company/visitor) the event is about, with every field present and null when unknown.
Payload
POST <your URL>
X-MCPH-Event: lead.created
X-MCPH-Timestamp: 1756400000
X-MCPH-Signature: sha256=<hex>
{
"id": "…", "event": "lead.created",
"created_at": "2026-08-28T12:00:00.000Z",
"workspace_id": "…",
"data": {
"kind": "lead", "title": "New lead: …", "body": "…",
"url": "https://businessmcp.com/dashboard/crm/contact/…",
"entity": {
"type": "contact", "id": "…",
"name": "…", "email": "…", "company": "…", "domain": "…",
"stage": "lead", "lead_score": 82, "band": "hot",
"lead_quality_band": "ok", "deal_value_cents": null,
"owner_name": null, "first_channel": "Organic Search",
"hdyhau": null, "last_page": "/pricing",
"dashboard_url": "https://businessmcp.com/dashboard/crm/contact/…"
}
}
}Verify & semantics
// Node — reject if it doesn't match
const expected = 'sha256=' + crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(timestamp + '.' + rawBody)
.digest('hex')- 5s timeout, one retry ~2s later.
- Auto-disabled after 20 consecutive failures (re-enable in Settings).
- Up to 5 endpoints per workspace; ~300 deliveries/endpoint/hour.
- Answer 2xx quickly — do the work async on your side.
Data events
The free reverse-ETL path. data is the SAME record GET /api/v1/contacts (or /accounts) returns, so a consumer that already parses the read API parses these for free. Every delivery carries its own id — dedupe on it, and reconcile anything the hourly cap dropped with ?updated_since=.
X-MCPH-Event: contact.scored
{
"id": "…", "event": "contact.scored",
"ts": "2026-09-06T02:14:09.000Z",
"workspace_id": "…",
"data": { …the /api/v1/contacts record… }
}
// company.identified
"data": {
"visitor_id": "v_…", "contact_id": "c_…",
"via": "ip", "sources": ["registry"], "confidence": 0.9,
"company": { "id": "…", "domain": "acme.com", "name": "Acme",
"industry": "computer software", "employee_range": "51-200" },
"contact": { …the contact record… }
}contact.created/contact.updated— every identify, form, signup, import, merge and Stripe stitch; the record after the write.contact.scored— once per contact per nightly run, only when the lead score, band, PQL flag or churn risk actually changed.company.identified— an identified person was linked to a company; anonymous reveals do not fire.account.updated— the account record pluscreated: true|false.revenue.recorded—{amount_usd, currency, source, visitor_id, contact_id, channel, utm_*, contact}for every server-verified purchase.- Pick the events per endpoint in Settings → API & Vault; the notification events stay selected by default, data events are opt-in.
Errors & limits
Status codes
401— missing or invalidmcph_*key400— malformed request (e.g. a task with no title)413— analytics beacon body over the 8KB cap204— analytics event accepted
Quotas
Metered actions are gated by your plan's monthly analytics-event quota, and agent runs are protected by a per-run cost ceiling. When you hit a limit you get a clear message — upgrade your plan or add an event top-up to raise it.
Prefer the terminal?
The mcphosting CLI wraps these APIs for one-command connect, search and list.