
Your agent needs to read campaign performance and write profiles into Klaviyo. Klaviyo ships a remote MCP server and a full date-versioned REST API, and both reach the same underlying platform. They are not interchangeable. The role requirements differ, the credential types differ, the write surface differs, and one of them assumes a human is watching. Here is how to pick.
Both paths front the same Klaviyo platform, so the difference is not what data exists. The difference is which slice is reachable, under whose credential, and with what runtime assumptions.
Klaviyo went generally available with an enhanced MCP server in August 2025 and now ships two deployments of it. The remote-hosted server is the recommended one: a single endpoint at mcp.klaviyo.com/mcp, authenticated with OAuth using Dynamic Client Registration (DCR), speaking Streamable HTTP. Connecting requires an Owner, Admin, or Manager role on the Klaviyo account.
The local server runs through uvx klaviyo-mcp-server@latest and authenticates with a static private API key supplied as PRIVATE_API_KEY. It is a developer-workstation tool: no OAuth, no per-user identity, no browser-based clients.
The REST API lives at a.klaviyo.com/api/ and is versioned by a date string in a revision header. The current generally available revision is 2026-07-15; each revision is supported for two years, and requests to a retired revision fall forward to the next oldest one, which is a quiet source of behaviour drift if you never re-pin.
Authentication accepts three credential types: a private API key sent as Authorization: Klaviyo-API-Key, an OAuth bearer token from a public app, or a six-character public company ID for the client-side /client endpoints.
Four dimensions decide this for an agent build: what the tool surface actually reaches, what the auth path forces on you, what you end up operating, and which workloads each one suits.
The MCP surface is broad. Scalekit's Klaviyo MCP connector page lists 263 tools spanning campaigns, flows, profiles, segments, catalogs, coupons, templates, reporting, brands, sending domains, and text messaging. Campaign sending is included, so this is not a read-only integration. The gaps are narrow and specific rather than broad.
Webhooks are the gap that matters most for production agents. The published tool table exposes retrieval and deletion, so an agent can audit what exists and tear it down, but it cannot create or update a subscription. Any agent that needs to react to Klaviyo events rather than poll for them has to provision that subscription through the REST API.
Reviews are the second gap: the published table lists exactly one Reviews tool, update_review, so reading review content is API-only. Klaviyo's Conversations API has no corresponding tool category at all, and the public-key /client endpoints used for browser-side event and subscription ingestion are outside the MCP surface by design.
The destructive tools do not simply execute. Call klaviyomcp_bulk_suppress_profiles, klaviyomcp_cancel_campaign_send, klaviyomcp_request_profile_deletion, or klaviyomcp_create_template_preview_send_job and the first attempt fails on purpose, returning instructions plus the exact value to supply in the user_confirmation parameter on the retry.
This is a good control and a real constraint. It assumes a person reviews the failure and approves the retry. A nightly suppression job has no such person. It either stalls waiting for input that never arrives, or it auto-echoes the returned token, which reduces the control to a speed bump.
Every Klaviyo MCP tool also takes a required model parameter plus an optional prompt_intent string. Tool inputs must carry those, which is easy to miss when porting a working REST integration.
Auth is where the two paths diverge hardest, and where the multi-tenant math gets uncomfortable.
The remote server supports one auth method: OAuth with DCR, completed through a browser consent flow. There is no API key option on the remote endpoint and no way to substitute a service credential. Combined with the Owner, Admin, or Manager requirement, this means the MCP path only works when a sufficiently privileged human is present at connection time.
Server behaviour is tuned through query parameters on the URL: read-only=true disables every write tool, core-tools-only=true trims the surface to roughly 40 core tools, and toolsets accepts a comma-separated scope list in resource:access form such as profiles:read,campaigns:read. These are client-configuration knobs, set once per connection, not per agent run.
Private API keys are created per account with per-resource scopes, and Klaviyo does not let you add a scope to an existing key or edit one after creation; you delete and recreate. They never expire on their own, which makes them convenient for background jobs and a liability on a laptop.
OAuth for the REST API is the Authorization Code flow with PKCE, and Klaviyo requires code_challenge_method=S256 for confidential clients as well as public ones. Authorization happens at www.klaviyo.com/oauth/authorize; token exchange and revocation must route through a.klaviyo.com, since Klaviyo stopped serving token traffic on www in March 2025.
This is the detail that reshapes a multi-tenant design. A Klaviyo OAuth grant authorizes your app against a Klaviyo account, not against the individual who clicked Allow. Fifty B2B customers means fifty grants and clean tenant separation. Five marketers inside one of those customers means one grant shared across all five.
The consequence is concrete: Klaviyo cannot tell you which of those five triggered a campaign send, because from its side there was one authorization. Per-user attribution is not something the API withholds; it is something the grant model never captured. If your product needs it, and any audited B2B product does, it has to be recorded at your own tool-execution layer before the call leaves your infrastructure.
Klaviyo rate limits per account, using a one-second burst window and a one-minute steady window, in tiers from XS at 1 per second and 15 per minute up to XL at 350 per second and 3,500 per minute. Requests carrying include or additional-fields parameters draw against a separate, stricter limit on top of the endpoint's own.
The operational difference is worth planning around: OAuth apps receive their own quota per installed instance, meaning per account per app, while private key integrations share a single quota across everything using that account. A fleet of agents on one private key competes with itself.
Recommended reading: OAuth vs API keys for AI agents and Access control for multi-tenant AI agents.
Neither path is managed end to end. The split is in which failures land on you.
Klaviyo maintains the tool definitions, the parameter schemas, and the mapping onto its own endpoints. When Klaviyo ships a capability, the tool appears without a redeploy on your side. That is genuine leverage for a fast-moving product.
What stays yours: token storage, refresh scheduling, revocation handling, and tenant isolation. Access tokens are valid for roughly one hour, with the caveat that you should read expires_in rather than hard-code it. Refresh calls are capped at ten per minute per grant, so a burst of concurrent agent runs hitting expiry together will throttle each other.
You own the whole stack: endpoint selection, JSON:API request shaping, filtering and cursor pagination, 429 handling against the Retry-After header, exponential backoff with jitter, and the full token lifecycle. That is materially more code, and it buys you every endpoint Klaviyo publishes rather than the subset exposed as tools.
Revocation deserves specific attention. Klaviyo access tokens are JWTs and cannot be revoked; only refresh tokens can. Revoking a refresh token stops new access tokens from being issued, but any outstanding access token stays valid until it expires. Your kill switch has up to an hour of lag unless you enforce it at your own execution layer.
The REST API gives you explicit control: pin a revision, upgrade deliberately, and treat a retired revision's fall-forward behaviour as a scheduled risk rather than a surprise. Klaviyo recommends re-pinning every twelve to eighteen months.
MCP tool schemas carry no equivalent version pin. When Klaviyo updates the hosted server, your agent's tool definitions change under it. That is exactly what you want for capability delivery and exactly what you do not want for a deterministic pipeline whose prompts were tuned against a specific schema.
Most Klaviyo agent products end up running both. The split is usually interactive versus unattended rather than one winning outright.
Whichever path you choose, you end up holding Klaviyo credentials for other people's accounts. That is the part neither path solves.
Fifty customers is fifty OAuth grants, fifty refresh tokens, and fifty independent expiry clocks. Each one needs encryption at rest, isolation from every other tenant, proactive refresh well before the hour is up, and a defined behaviour when it dies. Refresh tokens are revoked after ninety days of disuse, so a customer who goes quiet for a quarter comes back disconnected.
An invalid_grant response on refresh means the grant is gone, whether the customer uninstalled your app, the token aged out, or Klaviyo revoked it. Klaviyo's guidance is to treat every invalid_grant as an uninstall regardless of the description. Your agent needs to detect that, stop calling, and re-prompt, rather than retrying into a wall and reporting a silent no-op to the user.
Scalekit's Klaviyo MCP connector handles the OAuth flow, credential vaulting, refresh, and revocation behind a connected account keyed to your own user identifier. The Klaviyo grant is still account-scoped downstream; what you gain is a per-user record on your side of the boundary, so attribution and revocation survive even though Klaviyo's grant model does not carry them.
At the time of writing, Scalekit ships klaviyomcp as a vendor MCP connector, and there is no separate Klaviyo REST connector in the connector catalog. Both paths are reachable, through different mechanisms.
Create the connection once in the dashboard under AgentKit, then authorize each user against it. The connection_name string in code must match the connection name configured in the Scalekit dashboard exactly; this is the single most common integration error.
See Authorize a user for handling the redirect in production rather than printing a link.
Before the agent sees anything, decide what it is allowed to see. actions.langchain.get_tools wraps list_scoped_tools, which returns the tools the current user's connected account is authorized to call, filtered further by the allowlist you pass. A reporting agent has no business holding klaviyomcp_cancel_campaign_send, so it never receives it.
The size of this decision is worth stating plainly. Forty tools at roughly 200 tokens each is 8,000 tokens burned before the agent does any work, and a 263-tool catalog is several times that. Surface reduction is the lever, not better prompting.
Klaviyo's required model parameter is part of each tool's input schema, so the LLM populates it from the schema without extra wiring. The LangChain example covers the same pattern for other connectors.
When the sequence is fixed and there is nothing for a model to decide, skip the reasoning loop. execute_tool runs the call under the user's connected account and returns the result; the credential is resolved server-side and never enters your process or the model's context.
Tool names are case-sensitive and prefixed with klaviyomcp_. Check them against the connector docs rather than guessing from Klaviyo's own naming, which drops the prefix.
For webhook creation, review reads, or anything else outside the tool list, register a second connection against Klaviyo's REST API using Bring your own connector with your own OAuth app, then proxy through it. Scalekit resolves the base URL and injects the credential; you supply the path and Klaviyo's revision header.
Budget for this call carefully: additional-fields draws on its own stricter rate limit in addition to the endpoint's. Custom tools covers shaping the response into a schema the model can use.
Most real Klaviyo agents are not Klaviyo-only. A campaign performance briefing reads Klaviyo, writes a summary to Google Sheets, and posts to Slack. Wiring three MCP servers per user does not scale.
A Virtual MCP server is a single endpoint that declares exactly which connections and which tools an agent role can reach. You create it once, not once per user, and you get a static server URL back.
The briefing agent now cannot suppress a profile or cancel a send, because those tools are not on its endpoint. Klaviyo's read-only=true parameter gets you something similar at the whole-server level; this gets you tool-level least privilege per agent role.
Confirm every connection is still active for the user, then generate the URL for that user and pass it to the agent. Never share one URL across users; each is pre-authenticated for a single identity.
Setup details are in Set up and connect a Virtual MCP server and the Mastra example.
Three things break Klaviyo agents at the second customer, and none of them are about capability.
A single Klaviyo private key with account-wide scopes works perfectly in a demo and centralises every risk in production. One credential, one rate-limit quota, one blast radius, and no way to answer which user triggered a send. Connected accounts invert that: scope is a function of identity, and the credential never enters the agent runtime or the model context.
The honest caveat: Klaviyo's grant is account-scoped, so this does not make Klaviyo enforce per-user permissions it does not model. What it does is keep the identity attached on your side of the call, which is where your audit obligations actually live.
Every tool call is recorded against the identifier that authorized it: who triggered it, which tool ran, what came back. That is the difference between answering a SOC 2 question in an afternoon and reconstructing intent from application logs three weeks later. Audit trails for agent auth covers what a defensible agent trail has to capture.
Klaviyo's own answer to a large tool surface is core-tools-only=true, which is a good default and a blunt instrument: roughly 40 tools for every agent connecting through that configuration. A Virtual MCP server sets the list per agent role, so a briefing agent carries three tools and a campaign-authoring agent carries the eight it needs. Fewer tools in context means fewer wrong selections and fewer tokens burned before any work happens.
If a privileged Klaviyo user is in the loop and the job is reporting, analysis, or assisted authoring, build against MCP. The tool coverage is broad, Klaviyo maintains the schemas, and the confirmation gates on destructive actions work in your favour.
If the agent runs unattended, needs webhook provisioning, ingests at volume, or reaches surfaces the tool list does not expose, build against the REST API and pin a revision. Most products end up running both, split by whether a human is present.
Either way the credential problem is identical, and it is the part that decides whether the thing survives its second customer.
Browse the Scalekit Klaviyo MCP connector, read the connector docs, or check pricing.
If you are building on Klaviyo and want to compare notes on scoping, confirmation gates, or multi-tenant credential design, join the Scalekit Slack community. For a walkthrough against your own architecture, talk to us.
Related agent patterns worth borrowing from: the competitive intelligence briefing agent, the CRM AI agent, and the outbound prospecting agent. Adjacent posts in this series: Mailchimp MCP vs Mailchimp API, Customer.io MCP vs Customer.io API, and HubSpot MCP vs HubSpot API.