
Your agent needs to read segments, inspect deliveries, and change campaigns in Customer.io. Customer.io ships a hosted MCP server at mcp.customer.io and three distinct REST APIs plus reporting webhooks. They are not two views of the same thing. The MCP server is a per-user OAuth gateway over two of those API surfaces; the APIs are static-credential endpoints across three different hosts. The decision is mostly an auth decision, and for background agents it is a hard one. Here is the framework.
These are two different products with two different intended operators. One is built for a marketer sitting in Claude or Cursor. The other is built for your backend. Read both descriptions before picking, because the naming does not signal the difference.
Customer.io MCP is a hosted, remote MCP server. The endpoints are https://mcp.customer.io/mcp for the US region and https://mcp-eu.customer.io/mcp for the EU region. An account admin enables it once under Settings, then each user connects their own client and authenticates with their own Customer.io login.
The tool surface is deliberately generic. Rather than one tool per marketing object, Customer.io ships three HTTP verbs as tools, a schema introspector, and a skills library that teaches the model how Customer.io works before it calls anything.
Splitting reads from writes and deletes is the notable design choice here. In clients that support tool-level permissions, you can auto-approve reads while gating writes and deletes. Official documentation: Get started with the Customer.io MCP server.
Customer.io does not have one API. It has three, on three hosts, with two different authentication schemes, and the mapping between them is the thing that trips up most first integrations.
The Pipelines API is the recommended ingress path and uses HTTP basic authorization. It is POST-only; deletes and suppressions are expressed as semantic events such as Delete Person rather than as DELETE verbs. The Track API at track.customer.io is the older ingress path, also basic auth, using a Base64-encoded site_id:api_key pair. The App API at api.customer.io/v1 uses bearer authorization with scoped keys, and is the path for triggering broadcasts, sending transactional messages, and fetching data.
Reporting webhooks sit alongside all three and push message activity to your systems. Official documentation: About Customer.io's APIs.
Four dimensions decide this: what the agent can reach, which credential it holds, what breaks in production, and who owns fixing it. Capability coverage is the least interesting of the four for Customer.io, because the MCP gateway design means coverage is unusually broad. Auth is where the real constraint lives.
Because cio_read_api and cio_write_api accept an arbitrary path, the MCP surface is roughly co-extensive with the two API surfaces it fronts. The gaps are the surfaces it does not front, plus the operations that are structurally wrong to route through a tool call.
The ceiling is not breadth. A read-scoped MCP session can already reach most of what a marketer sees, and cio_schema means the agent discovers endpoints instead of you hardcoding them. That is genuinely useful for exploratory and assistive work.
The ceiling is throughput and direction. Every MCP tool call is one HTTP request wrapped in model reasoning and, in permissioned clients, a human approval. That is the wrong shape for ingesting behavioral events at volume, for backfills, or for anything that needs the batch endpoints. It is also the wrong shape for event-driven work: reporting webhooks push to you, and an MCP server has no subscription surface to receive them.
MCP auth on Customer.io is per-user OAuth with no alternative. Each user authenticates with their own Customer.io login, and the connection inherits that person's role and permissions. You cannot grant the MCP server more access than you personally have, though you can grant it less.
Scopes are explicit, and a connection starts with read only. Your agent requests additional scopes at connect time and the user approves or denies them during the authorization flow.
The three write-side scopes are independent. An agent that both edits content and sends messages must request write and write:live separately.
The direct API path has no equivalent constraint. Track and Pipelines take a Base64-encoded site_id:api_key basic auth pair; the App API takes a scoped bearer key. Neither needs a browser, a session, or a human.
Customer.io states the position plainly in the MCP documentation itself: if your AI tool runs in a terminal, the CLI is usually the better fit, because it gives the agent direct command-line access to the full API surface with no MCP setup. That is the vendor telling you which path background agents belong on.
Customer.io ships service accounts for exactly this workload, and they are more granular than a raw API key.
A service account issues tokens prefixed sa_live_, with expiration options of 30, 60, 90 days, one year, or none at all. Tokens can be marked read-only permanently at creation. You can hold several tokens per account so each integration rotates independently, and revoking one invalidates it immediately without touching the others. Details: Customer.io service accounts.
Run a lifecycle-marketing agent for forty marketers across eight customer workspaces and the MCP path gives you forty OAuth connections. Each one was established by a browser flow, each carries that individual's role, and each dies when that individual leaves.
The direct API path gives you a choice that MCP does not: one scoped service account per customer workspace, or per-user delegation where attribution matters. Eight credentials or forty, decided by your threat model rather than by the protocol.
Neither path stores, rotates, or revokes any of them for you. For a deeper look at credential ownership across agent tool-calling patterns, the tradeoffs between shared and per-user credentials are worth reviewing before you commit to an architecture.
On the MCP path, Customer.io owns hosting, the schema introspector, the skills library, and endpoint normalization. You own token storage per user, re-authorization when a session dies, and the fact that scope grants are decided in a consent screen you do not control.
You also own two behaviors that will surprise you. Requests carrying unknown fields in the body return 422 rather than silently dropping them, which is good discipline but will break agents that improvise payloads. Tool errors come back as structured JSON, so anything parsing the older plain-text format needs updating.
On the direct path you own the whole stack, and Customer.io's three-host split makes that heavier than it sounds.
You pick the host, handle the basic-versus-bearer difference, manage pagination and retries, and maintain the mapping between Pipelines semantic events and the operations they stand for. Customer.io currently documents the Track API at 1,000 requests per second and notes that limits are subject to change, so pin your assumptions to the reference rather than to a blog post.
Three account-level behaviors belong in your runbook before you ship.
First, the MCP toggle is checked on every API call. An admin switching it off stops your agent mid-run with no deprecation window. Re-enabling restores the same sessions, since the OAuth tokens are not deleted, but detection is entirely on you.
Second, read:sensitive and write:live each depend on an admin toggle in addition to user consent. An agent that works in your workspace can fail in a customer's purely because their admin left one off.
Third, and worst for compliance: admins cannot view or revoke another user's MCP sessions. Only the session's creator can, from their own personal settings. When an employee leaves, no central action terminates their agent's Customer.io access. This is exactly the problem covered in depth in when an employee leaves, who revokes their AI agent's access.
The split follows the auth model almost exactly.
Use Customer.io MCP when:
Use the Customer.io API directly when:
Every Customer.io agent that serves more than one person ends up holding a pile of credentials. The path you chose determines the token type. It does not determine the infrastructure you have to build around it.
MCP hands you an OAuth session per user. Service accounts hand you a sa_live_ token per workspace, which is long-lived by design and can be created with no expiration at all. In both cases the credential has to live encrypted at rest, isolated per tenant, and resolvable at request time without ever entering agent runtime or LLM context.
Revocation is where both paths leave the sharpest edge. A user can kill their MCP session from personal settings and your agent finds out on the next 401. A sa_live_ token that was created with no expiration and stored in a .env file on a laptop stays valid long after the person holding it has been offboarded. The agent does not decide to keep using it. It just does. Understanding secure token management for AI agents at scale is critical before either path goes to production.
Scalekit's Customer.io MCP connector handles the OAuth flow, vaulted token storage, per-user resolution at request time, and revocation, so the MCP versus API decision does not change your auth infrastructure. Credentials never touch the agent runtime or the LLM context. Connector reference: Customer.io MCP connector and the Customer.io connector overview.
The connector uses Dynamic Client Registration, so there is no Customer.io client ID or secret to register. You create a connection in the dashboard, send the user through one authorization link, and Scalekit injects the right token into every tool call after that.
Install the Python SDK and set your Scalekit credentials. Find these under Developers and API Credentials in the dashboard.
In the Scalekit dashboard, go to AgentKit, then Connections, then Create Connection, and search for Customer.io MCP. Note the connection name you are given.
The connection_name string in your code must match the connection name configured in the Scalekit dashboard exactly. This is the single most common integration error.
Initialize the client and put the user through the Customer.io consent flow once. The scopes the user approves at this screen are the ceiling on everything the agent can do afterward.
Before the agent runs, retrieve the tools this user's connected account is authorized to call. This is not a catalog lookup. list_scoped_tools returns the surface that this specific connected account permits, which is why a marketer with a read-only Customer.io role and an admin get different results from identical code.
Then prime the model. The connector documentation is explicit that customeriomcp_cio_prime should be your first call, because it loads the Customer.io API structure and recommended workflows into context before the agent starts guessing at paths.
Scalekit returns native LangChain StructuredTool objects, so the agent code carries no Scalekit-specific logic past initialization. Narrowing tool_names here is deliberate: this agent reports on campaigns, so it never receives cio_write_api or cio_delete_api, and cannot be talked into a mutation it was not built for. For a full walkthrough of how LangChain tool calling works and where it stops, that context is useful before wiring up the loop.
When the agent does need write access, the MCP tools support a dry_run flag that validates and returns the request without executing it. Treat this as mandatory in any workflow that touches live sends, not as a debugging convenience.
Scalekit's prebuilt Customer.io connector targets the MCP server. If your agent needs the Track, Pipelines, or App API instead, define those as a custom connector and call them through Tool Proxy. The connection, connected account, and authorization model stay identical, so you are not standing up a second auth stack for the second path.
Percent-encoding matters here. Customer.io returns 200 with empty results rather than an error when a query parameter is not encoded, so an agent will happily report "no such customer" for a customer that exists. Setup guide: Add your own connector.
A standard MCP server exposes everything it has. Customer.io MCP is unusually dangerous in this respect, because cio_write_api and cio_delete_api are not narrow tools. They are arbitrary write and delete access to the whole workspace, bounded only by the scopes in the consent screen.
A Virtual MCP server declares exactly which connections and which tools an agent role can see. You create it once per agent role, not once per user.
Add further McpConfigConnectionToolMapping entries for the other connections the agent needs. A churn-response agent that reads Customer.io deliveries and opens tickets elsewhere gets both surfaces from one endpoint, each scoped to the tools you named.
One server definition serves every user. Before each run, confirm the user has authorized the required connections and get their instance URL, which is bound to their connected accounts and no one else's.
The economics are not marginal. A server with 40 tools at roughly 200 tokens each burns about 8,000 tokens before the agent does any work. Scoping to 5 to 10 tools cuts that overhead by around 80 percent, and shrinks the decision space the model is choosing from. Surface reduction is the lever. Model upgrades help. They are not the lever. This is one reason MCP can be up to 32× more expensive than CLI — and why scoping matters so much.
Any MCP-capable framework connects to the instance URL over streamable HTTP. With LangChain, that is langchain-mcp-adapters rather than the native adapter.
Customer.io's own audit trail records what happened inside the workspace. It does not record which agent run triggered it, which prompt led there, or which of your tenants the action belonged to. For a marketing platform where the write path sends real messages to real customers, that gap is not academic.
When a campaign gets edited or a send goes out, three questions arrive at once: who authorized the credential, which agent executed the call, and was that action inside the scope you granted. Scalekit's auth logs tie every Customer.io tool call to the user whose connected account resolved it, with 90 days of history and SIEM-ready export. The broader case for audit trails for agent auth in B2B SaaS explains why per-call attribution matters at enterprise scale.
A shared Customer.io API key looks correct in a demo. In production it collapses attribution: every send, every segment edit, and every suppression appears as one service account, and no log query can reconstruct which person's request produced it. Per-user connected accounts keep the chain intact from prompt to API call, and scope is checked before the request reaches Customer.io rather than being suggested inside a prompt.
If a marketer is in the loop and the agent is helping them explore, draft, and analyze, use Customer.io MCP. The schema introspector and skills library genuinely reduce integration work, role inheritance is enforced by Customer.io, and read-write separation gives you a sane approval model out of the gate.
If the agent runs unattended, ingests events at volume, reacts to webhooks, or acts across multiple customer workspaces, build against the API with service accounts. Customer.io's documentation already points that workload away from MCP, and the absence of any non-interactive credential path into the MCP server is architectural, not a roadmap item.
Most production deployments end up running both, and that is fine. The credential layer is identical either way, and that is the part that needs production-grade infrastructure.
Building lifecycle-marketing agents on Customer.io and want to compare notes on scope design, dry-run policies, or per-tenant isolation? Join the Scalekit Slack community.
If you need help now, talk to an engineer and we will walk through your Customer.io auth model with you.
Browse the Scalekit Customer.io MCP connector.