E-commerce
Shopify Flow limits: a field guide for Klaviyo migrations
You moved abandoned cart off Klaviyo. Then the welcome flow needs a 35-day wait, and Shopify Flow caps at 30. Here is the field guide to the rest.

It is 9pm on a Tuesday and your marketing lead is in the Shopify Admin trying to rebuild the welcome series she ran for five years on Klaviyo. The browser has two tabs open. The Flow editor, and the help doc that says "Wait actions support delays from one minute to 30 days." Her old Klaviyo sequence had a 35-day re-engagement step. She closes the laptop and emails you.
This is where most Klaviyo to Shopify Flow migrations turn from a clean swap into a project. The Flow editor looks like Klaviyo at first glance. It is not. The limits are different, they show up in different places, and roughly half of them can be designed around in the Admin while the other half need a Remix app and a developer who knows what a Shopify Function is.
We have rebuilt Klaviyo flow libraries on Shopify Flow plus Functions for merchants across the last eighteen months. The same sixteen limits show up every time. This is the field guide we hand to the merchant on day one.
Where the migration usually breaks
Klaviyo's automation builder is a long, branching graph that runs on Klaviyo's servers and uses Klaviyo's profile store. You drag a Wait. You drag a Conditional Split. You drag a Send Email. The graph reads top to bottom and the building blocks are abundant.
Shopify Flow is also a graph, but it runs on Shopify's event bus, it uses Shopify's customer and order objects, and it has none of Klaviyo's email infrastructure. The Send Email block in Flow only knows how to talk to Shopify Email, the internal customer service team, or an HTTP endpoint you provide. The vocabulary is smaller, the inputs are stricter, and the side effects are typed.
That swap of substrate is where the limits live. Flow trades Klaviyo's profile-centric flexibility for Shopify's object model and event throughput. The sixteen limits below all fall out of that trade.
Eight limits a marketing lead can route around in the Admin
These are the ones you do not need to file a developer ticket for. Open the Flow editor, design around them, and ship.
1. The 30-day Wait cap
Shopify Flow's Wait action accepts a delay from one minute to 30 days. Klaviyo's equivalent goes to one year, which subscription brands rely on for 90-day refill nudges. If your old sequence had a 45-day re-engagement step, chain two Waits. Flow runs are durable, so a 30-day Wait followed by a 15-day Wait works. The cost is that the second Wait reads from the customer state captured at the trigger, not the state at minute 30, so re-check anything that might have changed.
2. Customer Created vs Customer Account Activated
Two triggers, two different audiences. Customer Created fires when any customer record appears, including those auto-created from guest checkout. Customer Account Activated fires when the customer sets a password. If your Klaviyo welcome sequence assumed an account, use Account Activated. Otherwise you will email people who do not know they are customers.
3. No native percentage A/B split
Klaviyo's Conditional Split has a "random percentage" mode. Flow does not. The workaround is the Random Number condition: roll a number between 1 and 100, branch on whether it is less than your split point. Document the seed in a note on the flow so the analytics team can read it later.
4. Latency, not real-time
Flow triggers run on an event queue, not synchronously. Expect one to five minutes between the event and the first action firing, longer during peak hours like Black Friday Monday morning. If your old Klaviyo flow assumed an instant cart-abandon email at the 30 minute mark, just shift the Wait to 25 to compensate.
5. Send Email goes through Shopify Email or HTTP
Flow's Send Internal Email is for staff. Customer email goes through the Shopify Email action, which requires a template configured in the marketing tab. If you kept Klaviyo for sending and only moved automations, you need an HTTP Request action posting to Klaviyo's events API. That sets up the next limit.
6. Metafield comparisons are string-typed
The Condition action sees metafields as strings, even when the underlying type is integer or JSON. Comparing a lifetime-value metafield to 500 with "is greater than" works because Flow coerces. A JSON metafield will fail silently. Expose the value you need into a flat string metafield or a customer tag, then condition on that.
7. Customer Segments evaluate once per run
The Customer Segment condition reads the segment as it was at the moment the trigger fired. If a customer enters segment X mid-flow, the running instance will not notice. Design segments so they are the trigger, not a mid-flow gate.
8. No cross-store flows
If you run multiple Shopify stores under one brand, Flow does not share between them. Each store needs its own copy. Use Flow's import and export to keep them aligned, version the JSON in git, and add a CODEOWNERS file so the brand team has to approve any drift.
Roughly 70 percent of a typical Klaviyo flow library can be rebuilt by a marketing lead inside the Shopify Admin once the eight constraints above are mapped on a whiteboard.
Eight limits that need a Remix developer
These are the ones where the merchant should stop dragging blocks and call the developer. Each is solvable, but the solution lives in a Shopify app, almost always built on Remix, with a Flow Connector extension or a Function extension.
9. The HTTP Request action's 10-second timeout
Flow's HTTP Request action gives up after ten seconds and treats anything beyond about 50KB of response body as a failure. Your enrichment API that takes 12 seconds to score a lead will break the run. Wrap it in a Cloudflare Worker or a Remix route that proxies, caches, and returns within budget. The proxy is also where you put the retry logic Flow does not give you.
10. The Function 11ms CPU budget
Shopify Functions run as WebAssembly and have an 11 millisecond CPU limit per invocation. If you are porting a Klaviyo "calculate dynamic discount" step, you cannot loop over 10,000 line items doing string math. Profile in the Functions log viewer, switch hot paths to Rust when JavaScript runs out of budget, and pre-compute as much as you can in metafields written from a webhook.
11. Functions cannot call out to HTTP
A Function gets its input JSON, runs, returns a result. No fetch, no network, no DNS. If the discount depends on a CRM lookup, the lookup has to happen earlier (in Flow, in an order webhook, anywhere with network) and the result has to ride into the Function as a metafield on the customer or cart. This pattern surprises every JS developer on their first Function.
12. The 256KB input cap on Cart Transform
The Cart Transform function receives the full cart as input. Carts above 256KB get truncated. Most stores never hit it. B2B stores with 500-line POs hit it on day one. The fix is to pre-filter at the merchandising layer, or to split bundle expansion across multiple smaller functions registered against different cart conditions.
13. Discount stacking needs a function
Out of the box, a Shopify cart accepts one automatic discount and one code discount, and they do not stack against each other. Klaviyo flows that fired stacking discounts via the order edit API have no Flow equivalent. A Discount function with the right combinesWith metadata can express almost any stacking rule you need, but you own the math now.
14. No persistent state between function runs
Functions are pure. A function called on the same cart twice in a row knows nothing about the first run. If your discount logic depends on "the customer has used this offer twice already," the count has to live in a metafield that an app updates after each order. Plan the write path before you plan the read path.
15. Custom Flow triggers and actions need an extension
If a marketer wants a trigger like "subscription paused" or an action like "send to our review platform," it lives in a Flow Connector extension inside a Remix app. The extension declares the trigger schema, ships a webhook endpoint that fires it, and registers in the merchant's Flow trigger picker.
16. Function logs cap at 10K events per shop per day
The Partners dashboard shows the last 10,000 Function executions. Hit that ceiling and you are blind for the rest of the day, which on Black Friday is the entire day. Pipe the same payload through the Function's logging API plus a webhook to your own observability stack. We use a small Remix route that buffers and ships to a managed log service.
Limits 11 and 14 compound. If your discount logic needs both a live CRM lookup and a usage counter, you are writing two app surfaces (a webhook receiver and a metafield writer) before the Function does anything.
The day-one triage we run
When a merchant calls and says "we want off Klaviyo automations by end of quarter," the first call is not a kickoff. It is a one-hour audit. We open the Klaviyo flow library, list every active flow, and tag each one against the sixteen limits above. The split usually lands at 70/30: most flows can be rebuilt in the Admin in a single sprint, and a handful need a Remix app.
Here is the rough shape of the spreadsheet we hand back:
flow_name,blocked_by,owner,est_days
Welcome series,#1 wait 35d,marketing,0.5
Browse abandon,#4 latency,marketing,0.5
B2B reorder,#12 cart-transform,dev,4
Loyalty tier discount,#11 no http + #14 state,dev,6
Win-back 60d,#1 wait,marketing,0.5
Post-purchase upsell,#13 stacking,dev,3
The audit kills the surprises that turn a four-week migration into a four-month one. The 35-day Wait is obvious. The Cart Transform that needs to expand a 600-line B2B bundle into per-warehouse line items is not. Find the second class before you scope.
One more thing about Functions
The 11ms budget is the limit that catches the most developers off guard. If you have written serverless functions before, your instinct is "I will cache the heavy work and amortise across runs." Functions do not have a cache. Each invocation starts from zero. The pattern that works is: do as little as possible in the hot path, push everything else to a Remix endpoint that updates metafields on a webhook, and let the Function read those metafields cheaply.
When we rebuilt the post-purchase and re-engagement automations for a Dutch beauty brand off Klaviyo last quarter, the wall we hit was limit eleven. The dynamic-discount function needed live loyalty-tier data, and Functions cannot fetch. We solved it by writing the tier to a customer metafield from a Remix listener on the orders/paid webhook, then the Function read it from the cart's buyerIdentity at checkout. This is the playbook we now use for every ecommerce migration on this stack.
If you are about to start your own migration, the smallest useful thing you can do today is open your Klaviyo flow library, count the flows that use a Wait longer than 30 days, and ask whether each Wait is load-bearing. That one count predicts most of the project's scope.
Key takeaway
Half of every Klaviyo to Shopify Flow migration is design work a marketer can ship in the Admin. The other half needs a Remix app with a Function extension.
FAQ
Can Shopify Flow fully replace Klaviyo for email?
For automation logic, mostly yes. For sending you still need Shopify Email, your own ESP through an HTTP Request action, or to keep Klaviyo as the send layer while Flow runs the triggers.
How long does a Klaviyo to Shopify Flow migration usually take?
A mid-market store with eight to fifteen active flows runs three to six weeks. The split is one sprint of Admin work and one sprint of Remix app work for whatever needs a Function or Flow Connector.
What is the hardest Shopify Function limit to design around?
The combination of no external HTTP plus the 11ms CPU budget. You have to pre-stage every input as a metafield, which means rewriting how the rest of the stack feeds data into checkout.
Do I need Shopify Plus to use Flow and Functions?
Flow is available on most Shopify plans now. Functions ship with Plus by default and on lower tiers through custom apps. Check your plan before scoping work that depends on either.