Customer.io has no SMTP relay and no single API key — it exposes three separate REST APIs on three hosts with three credential types, and transactional email leaves through the App API at POST /v1/send/email rather than any mail server. That split is the biggest source of integration bugs here. It is also why MCP matters more on this platform than most: Customer.io ships a first-party remote MCP server with OAuth, separate read and write scopes, and 7 tools covering the whole App API rather than a curated handful of actions.
Model Context Protocol is how an assistant such as Claude, ChatGPT or Cursor gets authenticated, structured access to a vendor API instead of guessing at endpoints. Customer.io shipped a first-party server early, and its design — generic API-shaped tools rather than a fixed catalogue of feature verbs — is what gives it full coverage.
The first-party remote server lives at https://mcp.customer.io/mcp (EU: https://mcp-eu.customer.io/mcp), authenticated by OAuth against your normal login. Transport type is http — sse is explicitly unsupported. A connection defaults to the read scope and never exceeds the permissions of the user who authorised it.
Two endpoints are worth wiring up, for different risk profiles: the official server for analysis and workspace management, the Zapier endpoint as a narrow write-only ingestion path.
First-party remote server, OAuth, transport http. Seven tools: cio_prime loads an AI-ready API reference, cio_schema discovers endpoints and parameters, then cio_read_api, cio_write_api and cio_delete_api execute authenticated calls. cio_skills_* expose custom agent skills.
Zapier-hosted endpoint exposing 3 actions: Create or Update a Person, Create Event, Create Anonymous Event. Useful when an agent should only push data in, with no read access to profiles or campaigns.
Most ESPs in 2026 have no MCP server, or expose a thin curated action set that breaks the moment you need something outside it. The cio_prime plus cio_schema pattern means the agent learns the API surface then calls it, so coverage tracks the App API as endpoints ship. Check the scope model in procurement: reads, writes and deletes are separate tools, connections default to read-only, and May 26, 2026 added toggles for live-data editing and sensitive-attribute access. Setup guides exist for Claude, ChatGPT and Cursor. Comparing shortlists? See the same section on Klaviyo, ActiveCampaign and Brevo.
There is no such thing as “the Customer.io API”. There are three, they do different jobs, and confusing their hosts or credentials is the most common failure mode here. Track ingests people, events and devices. The App API sends transactional messages, triggers broadcasts and manages campaigns, segments and templates. Pipelines (CDP) is a Segment-spec ingestion endpoint. Region is fixed at account creation for every workspace in the account; the dashboard is fly.customer.io in both regions and the EU data centre is in Belgium.
| Track API base URL | https://track.customer.io · EU https://track-eu.customer.io |
| App API base URL | https://api.customer.io · EU https://api-eu.customer.io |
| Pipelines (CDP) base URL | https://cdp.customer.io · EU https://cdp-eu.customer.io |
| MCP endpoint | https://mcp.customer.io/mcp · EU https://mcp-eu.customer.io/mcp |
| Versions | Track v1 and v2, App v1, Pipelines unversioned (Segment spec). No dated version header |
| Response format | JSON on every surface |
| Recipients per transactional request | 15 total across To, CC and BCC |
| Region redirects | Only track and transactional calls are redirected between regions — App management and Pipelines calls are not |
Track v1 is what every official library and third-party CDP connector calls; Customer.io states v2 is not used in any of its own libraries, so /api/v2/entity and /api/v2/batch are a hand-rolled path you opt into for batching. The July 15, 2026 UI rename left endpoint names alone, so /v1/campaigns and /v1/newsletters still work.
Four distinct credential types are in play. There is no unified key, and a key from one surface returns a bare 401 on another.
Username is the Site ID, password the Track API key, both workspace-scoped. It writes people, events, devices and objects and can delete profiles — treat it as a write credential.
Created in Account Settings under API Credentials, sent as Authorization: Bearer <App API key>. This key sends transactional email, triggers broadcasts, and reads or writes campaigns, segments, collections, exports, snippets and deliveries. Keys are scoped — mint one per integration.
Username is the source write key, password blank. HTTP clients that drop an empty password field break here. On an unexplained 401, check the raw Authorization header: it must be base64 of writekey: including the trailing colon.
No API key. OAuth against your Customer.io login, inheriting your role permissions, which is why read, write and delete are separate tools. Connections start read-only; live-data editing and sensitive-attribute reads are explicit toggles added May 26, 2026.
Every mismatch — App API key on a Track call, Track key on a send, write key with a non-blank password — returns 401 with no indication of which credential or which host was expected. Log the target host alongside the credential name on every outbound call, and audit each host string for its -eu twin before you start swapping keys.
Limits are per API surface, not per account. Customer.io calls them soft — not strictly enforced, but consistently exceeding them may cause throttling or dropped data — and subject to change.
| Surface | Limit | Notes |
|---|---|---|
| App API — standard endpoints | 10 requests/second | Campaigns, segments, exports, collections, deliveries |
| App API — transactional send | 3000 requests / 3 seconds | Soft limit, shared with the rest of the ingress |
| App API — triggered broadcasts | 1 request / 10 seconds | Tightest limit on the platform — batch recipients into one trigger |
| Track API | 1000 requests/second | Live integrations and historical backfill alike |
| Pipelines (CDP) API | 3000 requests / 3 seconds | POST-only Segment-spec surface |
| Payload caps | 32 KB per entity, 500 KB per batch | Track v2 and Pipelines batch; 32 KB per contained item |
There is no published per-hour email throttle: Customer.io meters monthly email allotment per plan instead, with Essentials including 1 million emails and 5k profiles. The fix for the two limits that actually bite is architectural. For API-triggered broadcasts at 1 per 10 seconds, stop calling per recipient — one trigger carries the whole audience, and per-person timing belongs in an event-triggered automation. For bulk ingestion, move off per-person Track calls onto /api/v2/batch or the Pipelines batch endpoint sized just under 500 KB. Track payload caps are enforced separately, and the 300 unique attributes per person ceiling is the one that surprises B2B teams.
Customer.io maintains 12 first-party clients in three families. The four server-side clients (Python, Node, Go, Ruby) wrap both the Track API and the App API, transactional send included. The Data Pipelines clients are forks of the Segment libraries and speak only to cdp.customer.io. The five mobile SDKs cover identify and track plus push and in-app.
| Language | Package | Install | Repo |
|---|---|---|---|
| Python | customerio | pip install customerio | customerio-python |
| Node.js / TypeScript | customerio-node | npm i --save customerio-node | customerio-node |
| Go | go-customerio | go get github.com/customerio/go-customerio/v3 | go-customerio |
| Ruby | customerio | gem install customerio | customerio-ruby |
| JS, Python, Go (Data Pipelines) | cdp-analytics-* | npm i @customerio/cdp-analytics-node, pip install customerio-cdp-analytics, go get github.com/customerio/cdp-analytics-go | cdp-analytics-js |
| Swift / iOS | customerio-ios | Swift Package Manager, or CocoaPods pod 'CustomerIO' | customerio-ios |
| Kotlin / Android | io.customer.android | Gradle, coordinate and version from the README | customerio-android |
| React Native, Flutter, Expo | customerio-reactnative, customer_io, customerio-expo-plugin | npm i customerio-reactnative, flutter pub add customer_io, npx expo install customerio-expo-plugin | customerio org |
Staleness is not the issue: the mobile repos were pushed on August 19, 2026, the server-side clients between July 24 and July 29, 2026. Coordinates are. The Go module major version, the Android Gradle coordinate and the registry names for the Flutter and Data Pipelines packages are version-dependent — copy them from each repo README. Every server-side client takes a region argument and defaults to US.
Community coverage is thin by design. Track and Pipelines are simple enough (Basic auth, JSON POST) that most teams call them with the HTTP client they already have, and because Pipelines follows the Segment spec, any Segment-compatible library works after a host and write-key swap — that is the de facto community SDK layer. For PHP, Java, .NET and Elixir, check last commit date and EU-host support first.
The working set. Paths are relative to their own host. Full reference and OpenAPI spec: Customer.io API documentation.
| Resource | Methods | Description |
|---|---|---|
Transactional emailapi › /v1/send/email | POST | Send with transactional_message_id (or inline body), to, identifiers and message_data. Returns delivery_id and queued_at. |
Transactional pushapi › /v1/send/push | POST | Push to a device registered against an identified profile. |
Transactional SMSapi › /v1/send/sms | POST | SMS via the configured provider — Twilio, plus Sinch, Infobip and Vonage since July 30, 2026. |
Transactional in-appapi › /v1/send/in_app | POST | Trigger a transactional in-app message for a profile. |
Transactional inboxapi › /v1/send/inbox_message | POST | Deliver to the app notification inbox channel. |
Transactional contentapi › /v1/transactional/{id}/content/{content_id} | PUT | Update the stored body of a transactional template programmatically. |
Identify person, v1track › /api/v1/customers/{identifier} | PUT, DELETE | Create or update attributes, or delete the profile. |
Eventtrack › /api/v1/customers/{identifier}/events | POST | Behavioural event on an identified person. Name max 100 bytes, data max 100,000 bytes. |
Anonymous eventtrack › /api/v1/events | POST | Event for an unidentified visitor, optionally with an anonymous_id for later merge. |
Devicetrack › /api/v1/customers/{identifier}/devices | PUT, DELETE | Register or remove a push token for a person. |
Entity, v2track › /api/v2/entity | POST | Unified person, object and event write endpoint. 32 KB max per request. |
Batch, v2track › /api/v2/batch | POST | Batched entity operations. 500 KB total, 32 KB per item. |
Pipelines ingestioncdp › /v1/{identify|track|page|screen|group|alias} | POST | Segment-spec ingestion. Basic auth, write key as username, blank password. POST-only. |
Pipelines batchcdp › /v1/batch | POST | Batched Segment-spec calls. 500 KB total, 32 KB per call. |
Workspace managementapi › /v1/{broadcasts|campaigns|segments|customers|collections|exports|sender_identities|snippets|activities|messages|newsletters} | GET, POST, PUT, DELETE | Journeys management surface: API-triggered broadcasts (1 per 10 seconds), newsletter create and send (April 14, 2026), Design Studio endpoints (March 18, 2026). |
The canonical call. The Bearer header carries the App API key, not the Track API key — swapping them returns 401 with no explanation.
curl --request POST https://api.customer.io/v1/send/email
--header 'Authorization: Bearer YOUR_APP_API_KEY'
--header 'Content-Type: application/json'
--data '{
"transactional_message_id": "3",
"to": "person@example.com",
"identifiers": { "email": "person@example.com" },
"message_data": { "name": "Person", "order_total": "59.99" }
}'
# 200 -> { "delivery_id": "...", "queued_at": 1755561600 }
# EU: https://api-eu.customer.io/v1/send/emailOne package, two clients, two credentials: APIClient is the App API, CustomerIO the Track API.
# pip install customerio
from customerio import APIClient, CustomerIO, Regions, SendEmailRequest
client = APIClient("YOUR_APP_API_KEY", region=Regions.US) # Regions.EU -> api-eu.customer.io
request = SendEmailRequest(
to="person@example.com",
transactional_message_id="3",
identifiers={"email": "person@example.com"},
message_data={"name": "person", "order_total": "59.99"},
)
with open("receipt.pdf", "rb") as f:
request.attach("receipt.pdf", f.read()) # attachments under 2 MB combined
print(client.send_email(request)) # -> {'delivery_id': '...', 'queued_at': ...}
# Ingestion: different API, different credentials
cio = CustomerIO("SITE_ID", "TRACK_API_KEY", region=Regions.US) # track.customer.io
cio.identify(id="user-123", email="person@example.com", plan="pro")Transport must be http; sse is unsupported. Auth is OAuth in the browser on first connect, so no key lands in the config file.
{
"mcpServers": {
"customer-io": {
"type": "http",
"url": "https://mcp.customer.io/mcp"
}
}
}
// EU workspaces MUST use https://mcp-eu.customer.io/mcp
// Call cio_prime, then cio_schema, before any read or write.Customer.io exposes no SMTP ingress, so you cannot point WP Mail SMTP, Laravel, Django or an appliance at it. The only SMTP feature is outbound: Customer.io connecting as a client to your server. Port 25 is refused — 465, 587 or 2525 are the alternatives, and they are your provider ports. With custom SMTP on, bounce and delivery data stop reaching Deliveries unless your provider is Mailgun, Mailjet, Mandrill, Postmark, SendGrid or SparkPost with feedback configured, and domain authentication stops being automatic.
US or EU is chosen at account creation for every workspace in the account, and Customer.io does not support data centre migrations except in extenuating circumstances. Because only track and transactional calls redirect, a hardcoded US host in an EU integration fails silently on management and Pipelines calls rather than erroring on day one. Geolocation collection also defaults on in the US and off in the EU.
Essentials starts at $100 per month for 5k profiles — people and objects. Accounts, companies, devices and custom objects consume the same allowance as human contacts, so 5,000 people plus 3,000 accounts is 8,000 profiles. Transactional email is not a separate SKU and eats the same email allowance, with one documented escape hatch: sends through your own custom SMTP server do not count against the plan allotment, at the price of losing delivery reporting.
A single transactional request caps at 15 recipients across To, CC and BCC, with all attachments under 2 MB and executable-adjacent extensions (bat, com, exe, msi, vbs, scr and more) blocked. The July 15, 2026 rename left API paths untouched, so a runbook or LLM prompt written in the new vocabulary will not match /v1/campaigns. And “disable message retention” stops Customer.io storing message bodies — right for password-reset tokens, wrong for anything you need to debug.
No endpoint deprecations landed in this window — the July rename is cosmetic at the API layer. Track the source in the official Customer.io release notes.
Three products, one brand. track.customer.io ingests people, events and devices. api.customer.io is the App API: transactional sends, broadcast triggers, campaign and segment management. cdp.customer.io is the Data Pipelines endpoint. Each has an EU twin, and only track and transactional calls auto-redirect between regions.
POST https://api.customer.io/v1/send/email with Authorization: Bearer <App API key> and a body of transactional_message_id, to, identifiers and message_data. A 200 returns delivery_id and queued_at. Caps: 15 recipients across To, CC and BCC, under 2 MB of attachments. There is no SMTP alternative.
Depends on the verb. Writing people or events: Track API key, HTTP Basic with your Site ID as username. Sending messages or managing campaigns and segments: App API key, a scoped Bearer token. Feeding Data Pipelines: the source write key as Basic username with a blank password. None is interchangeable, and a wrong key returns a bare 401.
Per surface: App API standard endpoints 10 req/s, transactional sends 3000 per 3 seconds, API-triggered broadcasts 1 per 10 seconds, Track API 1000 req/s, Pipelines 3000 per 3 seconds. Batch payloads cap at 500 KB total, 32 KB per item. Customer.io calls these soft limits but warns that consistently exceeding them may cause throttling or dropped data.
Yes — a first-party remote server at https://mcp.customer.io/mcp (EU: https://mcp-eu.customer.io/mcp). Add it as an HTTP MCP server; transport must be http, since sse is unsupported. Auth is OAuth against your Customer.io login, so no API key goes in the config and the connection cannot exceed your role permissions. Seven tools, led by cio_prime and cio_schema, with reads, writes and deletes split apart.
Twelve: Python, Node, Go and Ruby server-side; three Data Pipelines analytics clients (JS, Python, Go); and iOS, Android, React Native, Flutter and Expo on mobile. Region is a constructor argument that defaults to US — region=Regions.EU in Python, { region: RegionEU } in Node. Forgetting it is the most common EU-workspace bug: track and transactional calls still appear to work while management calls fail.
This review follows our email infrastructure testing methodology. We disclose affiliate relationships in our editorial independence policy.