Architecture guide21 min read

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.

Shopify Flow vs Functions vs Webhooks vs Apps for Fulfillment Automation cover image
By Jeroen BoersPublished August 2, 2026Updated August 2, 202621 min read

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.

LayerQuestion it answersBest fitDo not make it own
Shopify FlowWhat should happen after this trigger?Merchant-editable conditions and actions in Shopify adminSynchronous enforcement or a hidden long-running state machine
Shopify FunctionWhat result should Shopify apply at this supported decision point?Target-specific logic evaluated by ShopifyGeneral background work, arbitrary API orchestration, or operator recovery
WebhookWhich event should wake the app?Near-real-time notification after an eventFinal state, ordering guarantees, or the entire business process
App backendWho owns the process across time and systems?Durable policy state, queues, integrations, reconciliation, and recoveryA 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.

Decision matrix: timing, authority, recovery, and merchant control

Use this matrix before discussing implementation. The primary layer is the owner; the supporting layer can help without becoming a second owner for the same state transition.

RequirementPrimary layerCommon supporting layerReason
A merchant edits what happens after an order or fulfillment triggerFlowApp action or HTTP endpointThe workflow stays visible and configurable in Shopify admin
Shopify must apply a supported rule while evaluating checkout or routingApplicable Function APIApp-owned configurationThe rule executes at Shopify's defined decision target
An app reacts after an order or fulfillment eventWebhook plus backendQueue and reconciliation jobThe event wakes processing; the backend owns correctness
A process spans Shopify, a WMS, an ERP, a 3PL, or a customer actionApp or operational backendWebhooks for ingress, Flow for controlsThe process needs durable state and explicit recovery
Custom logic must alter Shopify's default routing or enforce a supported constraintOrder-routing FunctionApp configuration and observabilityThe decision belongs in the documented routing target
A simple merchant rule holds or releases eligible fulfillment workFlow when its action semantics fitApp when ownership or recovery is complexThe simplest adequate owner is usually the most operable one

“Can this tool do it?” is only the first filter. A better design review also asks whether the tool exposes the right state, supports the exact transition, leaves an audit trail, and gives the correct operator a recovery path.

Shopify documents that a Flow order trigger can apply a hold to each fulfillment order, while a fulfillment-order trigger targets one. That resource shape matters. An order can produce multiple fulfillment orders, so a design that stores one undifferentiated held flag per order has already lost important state.

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 layerResponsibilityEvidence to keep
ReceiptVerify, persist, deduplicate, and acknowledgeDelivery ID, topic, shop, API version, timestamps, and processing status
Business workerFetch current state, apply one idempotent transition, record side effectsBusiness identifiers, transition result, attempt, and external request key
ReconciliationFind missed or divergent state and feed it through the same transitionCursor, 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.

ArchitectureOwnerSupporting layersImportant boundary
Merchant-managed exception workflowFlow workflowShopify triggers and supported actionsNo hidden app state is required for correctness
Shopify decision with merchant configurationFunction for the decisionApp for settings, validation, and deploymentThe app prepares policy; the Function applies the supported result
Reliable post-event processingApp business commandWebhook, queue, reconciliationThe event starts work but never becomes final truth
Merchant-controlled app workflowApp state machineFlow app action or triggerFlow 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

Shopify preorders guide

owns the merchant policy and promise, while the

Buy Now, Ship Later guide

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 questionFlowFunctionWebhook plus app
Where is the diagnostic evidence?Workflow run and action detailsApp and Function logs or platform diagnosticsReceipt, job, transition, and reconciliation records
Who can retry?Merchant for eligible failed runsUsually the next Shopify evaluation or an app/configuration changeOperator through an app command or automatic retry policy
What prevents duplicate effects?Action-specific behavior plus downstream safeguardsDeterministic target result and stable configurationDelivery dedupe, idempotent commands, and side-effect keys
How is missed work found?Run monitoring and business reviewValidation of resulting Shopify behaviorScheduled 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:

  1. Store plan: Flow, individual Flow actions, Function APIs, and checkout or routing surfaces can have different plan requirements.

  2. App distribution: public App Store, custom, and other distribution models do not necessarily receive the same Function availability.

  3. API or target: “Shopify Functions” is a family. Confirm the exact API, supported operations, input fields, and current limitations.

  4. 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.

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

Need a durable fulfillment workflow?

We build preorder, hold, release, routing, and operations automation around the order states you actually run.

Discuss fulfillment automation