Shopify Flow vs Functions vs Webhooks vs Apps for Fulfillment Automation
Choose between Shopify Flow, Functions, webhooks, and app backends for fulfillment automation using timing, ownership, recovery, and plan tradeoffs.

Editorial note
Shopify platform contracts and plan notes were checked on August 2, 2026. The examples are source-backed architectural scenarios, not results from a live merchant store, and this guide makes no measured latency, plan-parity, reliability, or merchant-outcome claims. Recheck eligibility and API status before implementation.
Choose the execution point before choosing the tool
The shortest useful answer is this: choose Shopify Flow when a merchant should control a workflow after a trigger, choose a Shopify Function when Shopify must evaluate a supported rule inside its own execution path, use a webhook to tell a backend that something happened, and use an app when the process needs durable state, external integrations, or recovery.
That is not a four-way product comparison. It is an ownership model. One fulfillment system can use all four layers without duplication: a Function makes a routing decision, Shopify emits an event, a webhook wakes an app, and Flow gives an operations team an editable notification or review step.
Architecture goes wrong when teams start with the tool they know. A merchant builds an increasingly stateful Flow because it was quick to install. A developer puts post-purchase orchestration into a Function because the word “fulfillment” appears in the API name. An app treats webhook delivery as final truth. Each approach can work in a demo while leaving no clear owner for retries, stale state, or manual intervention.
The decision rule
First identify when the decision must happen, which system is authoritative, who may change the rule, and who recovers it after failure. Then choose the Shopify surface that matches those constraints.
The distinctions in this guide come from Shopify's published execution contracts. The worked examples are synthetic architecture scenarios. They are useful for design review, but they are not claims about measured latency or a production merchant result.
The short answer: Flow vs Functions vs webhooks vs app backends
Shopify Flow, Functions, webhooks, and apps answer four different questions.
| Layer | Question it answers | Best fit | Do not make it own |
|---|---|---|---|
| Shopify Flow | What should happen after this trigger? | Merchant-editable conditions and actions in Shopify admin | Synchronous enforcement or a hidden long-running state machine |
| Shopify Function | What result should Shopify apply at this supported decision point? | Target-specific logic evaluated by Shopify | General background work, arbitrary API orchestration, or operator recovery |
| Webhook | Which event should wake the app? | Near-real-time notification after an event | Final state, ordering guarantees, or the entire business process |
| App backend | Who owns the process across time and systems? | Durable policy state, queues, integrations, reconciliation, and recovery | A supported native decision that a Function can make more directly |
There is overlap in who configures each layer, but not in the execution contract. An app can deploy a Function, subscribe to webhooks, and expose actions or triggers to Flow. Calling all of that “an app” is technically true and architecturally unhelpful. The useful question is which layer owns each transition.
A practical way to draw the boundary is by time:
While Shopify is evaluating a supported decision: use the applicable Function API.
After Shopify or an app emits a trigger: use Flow for merchant-owned automation or a webhook for app-owned processing.
Across minutes, days, retries, or external systems: keep durable workflow state in the app or operational backend that can reconcile and recover it.
This timing test prevents a webhook from being mistaken for validation and prevents a Function from being mistaken for a general serverless worker.
Use Shopify Flow for merchant-owned post-event automation
Shopify Flow is strongest when an operations team should be able to inspect and change a trigger-condition-action workflow without a code deployment. Good examples include tagging a risky order, notifying a team, adding a review step, or invoking a supported fulfillment action when a visible rule matches.
That merchant ownership is the feature, not a concession. If a warehouse lead should be able to change a threshold or disable an escalation during an incident, Flow can keep the decision close to the operator. The run history also gives the team a place to inspect what happened.
Flow is not a generic synchronous policy engine. A workflow starts from a trigger, evaluates conditions, and runs actions. That makes it a poor owner for a decision that must alter the checkout or routing result currently being evaluated. Use the Function API designed for that decision instead.
Flow also needs an explicit state boundary. Tags and metafields can carry useful markers, but a growing collection of markers is not automatically a reliable state machine. If the process has compensating actions, external identifiers, partial completion, or replay, move the durable process into an app and let Flow remain the merchant-facing control layer.
Hold ownership needs special care
Shopify's Flow release action releases all holds on the selected fulfillment order. If an app owns a separate hold for a different reason, do not let a generic Flow release silently compete with that app-owned state. Assign one release owner or build an explicit review boundary.
Shopify's Admin GraphQL release mutation can instead receive specific hold IDs, and its documentation warns that omitting them can release work prematurely. Exact multi-owner release therefore belongs behind an app-controlled command, not the built-in broad Flow action.
Current plan details also matter. Shopify lists Flow itself for Basic, Grow, Advanced, and Plus, while particular capabilities have narrower availability. The Send HTTP Request action, for example, is currently limited to Grow, Advanced, and Plus. As checked on August 2, 2026, custom partner-app Flow tasks require Plus. Recheck the exact action and store before promising a workflow.
When a run fails, distinguish platform retry behavior from business recovery. Shopify documents transient and permanent Flow errors and exposes failed-run details; some runs can be retried manually after correction. That does not tell the business whether repeating a downstream action is safe. The action's owner still needs an idempotency and recovery rule.
Check Shopify Flow's current workflow and plan requirements.
Use Shopify Functions for Shopify-hosted decisions
Choose a Shopify Function when Shopify exposes a Function target for the decision and must apply the result inside its own backend execution. Shopify invokes the Function, provides the selected GraphQL input, runs the compiled logic, and applies the target's supported output. The Function is not a URL that your app calls whenever it wants background work.
The target is the contract. Delivery Customization can hide, rename, or move delivery options. Cart and Checkout Validation can reject invalid checkout states. Order Routing Functions cover specific location rules, fulfillment constraints, and pickup-option generation. Those APIs are related to fulfillment, but they are not interchangeable.
Configuration should normally be prepared before execution. A merchant-facing app can store policy in supported metafields, validate it, and provide previews. The Function then reads the smallest required input and returns a deterministic result. This keeps the execution path narrow and makes configuration changes auditable.
Do not assume a Function can call any external API. Shopify's network access is limited to particular Function APIs and eligible plans, must be enabled by Shopify, and is not a sensible default for general integration work. Shopify also says it is not generally available in development stores, so confirm an eligible test path before committing to it. If a decision depends on a WMS, ERP, or risk service, ask whether the data can be synchronized ahead of time. If not, an app-owned workflow after the event may be the correct boundary.
A Function is not a miniature backend
Keep external orchestration, long-running state, queues, dashboards, and manual recovery in the app. Use the Function only for the supported decision Shopify asks it to make.
Availability is not one yes-or-no flag. Shopify currently distinguishes Functions delivered through public App Store apps from Functions in custom apps, and individual Function APIs can add narrower plan or access requirements. Verify the distribution model, target API, plan, and current version together.
For a detailed example of the target boundary, read the
Delivery Customization Function guide
. It deliberately stays focused on the delivery options Shopify already produced; routing, holds, and post-checkout fulfillment belong elsewhere.
Use webhooks to notify a backend, not to own final state
A webhook is event delivery. Shopify sends a near-real-time notification after an event has occurred so an app can react without polling continuously. That is an efficient ingress mechanism, but it is not the final state of a fulfillment process.
Webhook ordering is not guaranteed, and a delivery can be duplicated, delayed, or missed. A reliable handler verifies the request, persists enough information to replay it, acknowledges quickly, and queues business processing. Critical workflows also reconcile against current Shopify state so a missed notification does not become permanent drift.
This distinction matters whenever a team says “the webhook will release the order.” The webhook should trigger an idempotent command that checks current policy and fulfillment state. The command—not the HTTP delivery—owns whether the release is allowed, whether it already happened, and how to recover after a partial failure.
| Webhook layer | Responsibility | Evidence to keep |
|---|---|---|
| Receipt | Verify, persist, deduplicate, and acknowledge | Delivery ID, topic, shop, API version, timestamps, and processing status |
| Business worker | Fetch current state, apply one idempotent transition, record side effects | Business identifiers, transition result, attempt, and external request key |
| Reconciliation | Find missed or divergent state and feed it through the same transition | Cursor, lookback window, drift reason, and repair result |
Shopify's Next Generation Events is currently a developer preview for a subset of topics and can run alongside webhooks. Treat that as a changing delivery option, not as permission to remove the correctness model. Event transport may evolve; durable ownership and reconciliation remain app responsibilities.
Once a webhook-backed pipeline is the right choice, continue with the
retry-safe Shopify event pipeline guide
and the
Rails webhook idempotency guide
. Those pages cover correctness after ingress; this guide decides whether webhook ingress is the right layer in the first place.
Use an app backend for durable state, orchestration, and recovery
An app backend should own the process when fulfillment state must survive across multiple events, systems, and human decisions. Examples include a paid order held until a customer releases it, a preorder split across availability dates, a 3PL handoff with acceptance and rejection, or a warehouse review queue with an escalation path.
Durable ownership means more than storing an order ID. The app needs explicit state transitions, Shopify and external identifiers, idempotency keys, attempt history, versioned policy, reconciliation, and a manual escape hatch. Operators should be able to answer what the system believes, what Shopify currently says, which action is pending, and what a safe retry will do.
Shopify remains authoritative for Shopify objects. The app should not copy an order or fulfillment order once and treat that snapshot as eternal truth. Instead, store the app-owned policy and workflow state, reference the Shopify resources it applies to, and reconcile before critical transitions.
The data model must also respect fulfillment orders. A Shopify order can be represented by
multiple fulfillment orders because of location, inventory, routing, or delivery method. A
single app-level boolean such as released cannot describe partial release safely.
Store the relevant fulfillment-order identifiers and the ownership reason for each transition.
Use one command path
Webhook processing, scheduled reconciliation, an operator retry, and a Flow-triggered app action should converge on the same idempotent business command. Four entry points do not justify four implementations of release or routing policy.
The cost is product and operational responsibility. An app backend needs authentication, versioned API calls, queues, observability, data retention, support tooling, and incident recovery. Do not build that system for a two-condition notification Flow already handles. Build it when the business process genuinely requires an owner.
Combine tools without creating competing owners
A composed architecture is usually safer than forcing one layer to do everything. The rule is that composition must preserve one owner per decision.
| Architecture | Owner | Supporting layers | Important boundary |
|---|---|---|---|
| Merchant-managed exception workflow | Flow workflow | Shopify triggers and supported actions | No hidden app state is required for correctness |
| Shopify decision with merchant configuration | Function for the decision | App for settings, validation, and deployment | The app prepares policy; the Function applies the supported result |
| Reliable post-event processing | App business command | Webhook, queue, reconciliation | The event starts work but never becomes final truth |
| Merchant-controlled app workflow | App state machine | Flow app action or trigger | Flow requests a transition; the app validates and records it |
Competing ownership is easy to spot once you name the transition. If Flow and an app can both release a hold without consulting the same policy, there are two owners. If a Function and a backend each decide which location is valid from different configuration snapshots, there are two owners. If an operator can fix Shopify manually but the app never reconciles, there are two realities.
Make supporting layers explicit. A Flow action can call an app command, but the app decides whether that command is currently valid. An app can publish configuration for a Function, but it should not try to reverse the Function result later through unrelated order mutations. A webhook can wake a worker, but the worker must read the state that owns the decision.
This design also improves support. An incident runbook can name the owner and its recovery interface instead of asking staff to inspect four dashboards and guess which one won.
Four worked fulfillment decisions
These are source-backed architecture examples, not test-store results. Their purpose is to show how the ownership model changes with the requirement.
1. Review and hold a high-risk fulfillment
If the requirement is a visible merchant rule that holds eligible fulfillment work and alerts an operations team, Flow can be a good primary owner when its trigger and action semantics fit. Document whether the workflow starts from an order or a fulfillment order, because that changes the resource scope.
If another app already places its own holds, or the review needs multiple reasons, partial release, external risk state, and auditable recovery, an app should own the state machine. Flow can still request review or notify an operator. It should not run a broad release action that bypasses app-owned hold reasons.
2. Release a preorder or paid ship-later order
The app owns the promise, eligibility, customer release action, and durable workflow state. Webhooks notify it about order and fulfillment changes; reconciliation repairs missed events. Flow can add a merchant-editable escalation or tag, but it should not independently decide the final release.
A Function belongs only where a specific supported Shopify decision needs synchronous policy, such as a checkout or routing target. It does not replace the post-purchase release state machine. The
owns the merchant policy and promise, while the
covers the specific customer-controlled hold-and-release model.
3. Choose a fulfillment location
When Shopify must include, exclude, rank, or constrain locations during order routing, start with the applicable Order Routing Function API. An app can own merchant configuration, validation, and observability around the Function. It should not wait for an order webhook and pretend to retroactively make the same routing decision.
Eligibility is part of the architecture. Shopify's order-routing documentation lists distinct APIs for location rules, fulfillment constraints, and pickup-option generators, with plan or access conditions that can differ. As checked on August 2, 2026, custom Order Routing Location Rule Functions are a request-access beta for Plus stores; other routing Function APIs have separate eligibility. Confirm the target store and exact API before choosing this path.
4. Hand fulfillment to a WMS or 3PL
A webhook can notify the app that relevant Shopify state changed. The app persists the event, checks current fulfillment state, sends an idempotent downstream request, and records the WMS identifier. A reconciliation job compares both systems and repairs drift. Flow can alert an operator after a terminal failure or expose a controlled retry action.
The backend owns the handoff because it spans systems and time. A Flow HTTP request alone is rarely enough when the downstream contract needs deduplication, polling, acceptance or rejection, compensating actions, and a support interface.
Design failure ownership and manual escape hatches
The best tool is the one whose failure can be understood and recovered by the right person. Write the recovery contract before implementation, while switching architectures is still cheap.
| Failure question | Flow | Function | Webhook plus app |
|---|---|---|---|
| Where is the diagnostic evidence? | Workflow run and action details | App and Function logs or platform diagnostics | Receipt, job, transition, and reconciliation records |
| Who can retry? | Merchant for eligible failed runs | Usually the next Shopify evaluation or an app/configuration change | Operator through an app command or automatic retry policy |
| What prevents duplicate effects? | Action-specific behavior plus downstream safeguards | Deterministic target result and stable configuration | Delivery dedupe, idempotent commands, and side-effect keys |
| How is missed work found? | Run monitoring and business review | Validation of resulting Shopify behavior | Scheduled reconciliation against authoritative state |
Do not use “retry” as a complete recovery plan. A retry must say which operation repeats, which state is checked first, which idempotency key is reused, what happens after partial success, and when a human should stop automation.
A manual escape hatch should also preserve evidence. “Edit the database” is not an operator workflow. Prefer a named command that shows current Shopify and app state, explains the proposed transition, requires the appropriate permission, records the actor and reason, and schedules reconciliation afterwards.
Finally, decide what happens when Shopify and the app disagree. Usually the repair process reads current Shopify objects, compares them with app-owned policy, and either converges automatically or opens a review case. Silently overwriting one system from an old event is not reconciliation.
Check plan, distribution, and API eligibility
A sound architecture on the wrong eligibility assumptions is still the wrong architecture. Check these four dimensions for the exact store and deployment:
Store plan: Flow, individual Flow actions, Function APIs, and checkout or routing surfaces can have different plan requirements.
App distribution: public App Store, custom, and other distribution models do not necessarily receive the same Function availability.
API or target: “Shopify Functions” is a family. Confirm the exact API, supported operations, input fields, and current limitations.
Access status: developer preview, request-only, and generally available capabilities need different launch and fallback plans.
Put the result in the implementation proposal, not in a developer's memory. Record the source URL, date checked, eligible plans, distribution model, API version, preview or approval status, and fallback if access is absent.
Avoid broad promises such as “Functions work on every plan” or “Flow requires Plus.” Both lose the distinctions that matter. Shopify currently makes Flow broadly available while narrowing particular actions, and makes many Functions available through public apps while applying different rules to custom apps and individual APIs.
Time-sensitive eligibility should become an update trigger for this architecture. Recheck it before build, before merchant acceptance testing, and before production enablement—not only when an article or proposal was first written.
Use this selection and validation checklist
Before implementation, the team should be able to answer every item below in one architecture note.
Execution and ownership
- Does the decision happen during a supported Shopify evaluation or after an event?
- Which layer owns the transition, and which layers only trigger or support it?
- Which system is authoritative for each Shopify object and each app-owned policy?
- Can an order create multiple fulfillment orders that need separate state?
- Should a merchant change the rule in Shopify admin without a deployment?
Failure and recovery
- What happens after duplicate, delayed, out-of-order, or missed event delivery?
- Which command is idempotent, and which key identifies a repeated side effect?
- How does reconciliation find drift against current Shopify state?
- Who can retry, override, or stop the workflow, and where is that action audited?
- Can two systems independently release, reroute, cancel, or fulfill the same work?
Eligibility and proof
- Which store plan, app distribution model, API version, and access status apply?
- Are the trigger resource and action scope correct for each fulfillment order?
- Which examples are documented platform behavior, synthetic fixtures, or live-store evidence?
- What test proves the result and the safe failure path without using customer data?
- Which Shopify change should trigger a new architecture review?
A strong proposal ends with an owner map and a failure matrix, not only a diagram of happy-path arrows. If the team cannot name who repairs a partial release at 08:00 on Monday, the workflow is not ready for fulfillment operations.
Best internal links
Continue with the guide that owns the next implementation decision:
- Shopify Delivery Customization Function deep dive
for hiding, renaming, and moving checkout delivery options.
- How to build a retry-safe Shopify event pipeline with Rails
after choosing webhooks and an app backend.
- Shopify webhook idempotency in Rails
for duplicate-delivery and side-effect controls.
- The Shopify preorders guide
for merchant policy, payment, availability, mixed-cart, and customer-promise decisions.
- Buy Now, Ship Later for Shopify
for a paid hold-and-release workflow and its Addora boundary.
- Shopify Admin GraphQL patterns in Rails
for versioned backend API calls, error handling, and operational context.
- Shopify fulfillment automation services
when the owner map points to a custom build or architecture workshop.
Sources and update triggers
This guide uses Shopify's official documentation as the authority for platform behavior. The architecture recommendations—such as keeping durable multi-system state in one backend and converging ingress paths on one idempotent command—are clearly editorial inferences from those contracts, not quoted Shopify requirements.
Shopify Flow overview and plan requirements, Shopify Functions overview, Shopify webhooks overview, and Shopify's order-routing app documentation were checked on August 2, 2026.
Review this article when Shopify changes Flow plan access or fulfillment actions, Function distribution or network-access rules, an Order Routing Function's eligibility, webhook delivery guidance, the status of Next Generation Events, the fulfillment-order model, or a quarterly stable Admin API version used by the examples.
The launch validation should also recheck that every official URL still supports the nearby claim. Platform eligibility belongs in the release gate even when the architecture itself has not changed.
Frequently asked questions
Is Shopify Flow real-time?
Treat Flow as event-driven automation, not as a guaranteed real-time enforcement layer. A trigger starts a workflow after the relevant event, and actions can fail or retry. If Shopify must apply a rule while evaluating a supported checkout or routing decision, use the applicable Function API instead.
Can a Shopify webhook replace a Function?
No. A webhook tells an app that an event has occurred; it cannot retroactively change the decision that produced the event. A Function runs at a defined Shopify execution target and returns one of that target's supported results.
Are Shopify Functions only available on Shopify Plus?
Not as a blanket rule. Shopify currently says Functions delivered through public App Store apps can work across plans, while custom apps containing Functions require Plus. Individual Function APIs can have narrower plan, access, or distribution requirements, so verify the exact API and installation model.
When does a Shopify Flow workflow need an app backend?
Add an app when the workflow needs durable multi-step state, an external system, app-owned configuration, reconciliation, exact transition ownership, a customer-facing workflow, or recovery controls beyond the available Flow actions.
Can a Shopify Function call an external API?
Do not design around unrestricted network access. Shopify limits network access to particular Function APIs and eligible stores, and it must be enabled. The safer default is to prepare configuration before execution and keep external calls in an app backend.
Where should fulfillment workflow state live?
Shopify should remain authoritative for Shopify fulfillment objects. Long-running policy and integration state should live in the app or operational system that owns the workflow, with explicit identifiers, transitions, reconciliation, and operator recovery. Flow can expose merchant controls without becoming a second state machine.
Recommended reading
Keep exploring the playbook

Shopify Delivery Customization Function deep dive: hide, rename, sort, and safely control shipping options at checkout
A technical Shopify developer guide to Delivery Customization Functions covering hide, rename, and move operations, delivery groups, subscription edge cases, merchant configuration, and the checkout pitfalls that appear in production.

How to build a retry-safe Shopify event pipeline with Rails
A practical architecture guide for Rails teams building retry-safe Shopify event pipelines with webhooks, queues, reconciliation jobs, and durable side-effect tracking.

How to run preorders on Shopify without creating support chaos
A merchant guide to preorder messaging, promise-setting, app selection, order handling, and customer communication for stores selling future inventory on Shopify.
Need a durable fulfillment workflow?
We build preorder, hold, release, routing, and operations automation around the order states you actually run.
Discuss fulfillment automation