---
title: "Order triggers, step by step"
description: "Step-by-step Shopify Flow tutorials for order tags, notes, attributes, line item edits, fulfillment, display status, financial status and shipping address changes."
canonical: "https://docs.workflow-trigger-extensions.app/order-trigger-tutorials"
---

# Order triggers, step by step

Shopify Flow has good triggers for the big moments of an order: created, paid, fulfilled, cancelled. What it lacks is everything in between. These ten triggers fire on the edits that happen to an order after it exists, each with the detail of what changed.

**On this page:** [Order Update](#order-update), [Order Tags Added](#order-tags-added), [Order Tags Removed](#order-tags-removed), [Order Note Changed](#order-note-changed), [Order Attributes Changed](#order-attributes-changed), [Order Line Items Changed](#order-line-items-changed), [Order Fulfillment Changed](#order-fulfillment-changed), [Order Display Status Changed](#order-display-status-changed), [Order Financial Status Changed](#order-financial-status-changed), [Order Shipping Address Changed](#order-shipping-address-changed)

> [!TIP]
> **New to Shopify Flow?**
> Every example follows the same five moves: pick the trigger, add a condition, add an action, turn the workflow on, check Event History. [Your first workflow, step by step](https://docs.workflow-trigger-extensions.app/first-workflow-tutorial.md) shows each of them with screenshots.

![Shopify Flow's trigger picker inside Workflow Trigger Extensions, searched for Order](https://cdn-dev.eu.codecreationlabs.cloud/crm-tool/uploads/SjJ78gKLnLlocopCui3Cs62dJbGZ7F1n/8b1b330fed3ae0d4.png)
*Where you find them: in Shopify Flow choose Select a trigger, open Workflow Trigger Extensions and search for "Order".*

## Order Update

Fires on every update of an order. It carries `changes`: the tracked fields that changed (financial status, fulfillment status, tags, note, line items, custom attributes) with the value before and after.

| | |
| --- | --- |
| **Fires from** | Shopify webhook plus change detection |
| **Permission** | **Order Data Access** |
| **Switched on** | Turning on a Flow workflow that uses it switches it on. Its own switch is on the app's **Triggers** page |

**Use it for**

- One workflow that reacts to several kinds of order edits.
- A change log of orders for customer service.

### What Order Update sends to your workflow

| Variable in Flow | Type | What it holds |
| --- | --- | --- |
| `order` | Order (Shopify) | The order that was updated. A full Shopify order, so all of its fields and Flow's own order actions are available. |
| `payload` | Text | JSON object containing the GID of the order that was updated (e.g. {"id":"gid://shopify/Order/123"}) |
| `changes` | Object | Which tracked fields changed (financial status, fulfillment status, tags, note, line items, custom attributes), with the old and new value of each |
| `changes.fields` | List of text | Names of the tracked fields that changed |
| `changes.items` | List of objects | Old and new value per changed field |
| `changes.items (each).field` | Text | The field that changed: financialStatus, fulfillmentStatus, tags, note, lineItems or customAttributes.<name> |
| `changes.items (each).oldValue` | Text | The value before the update. Long values are shortened |
| `changes.items (each).newValue` | Text | The value after the update. Long values are shortened |
| `eventId` | Text | The id of this event. Search for it in the app's Event History to see exactly what was sent. |

### Example: Write tracked order changes to the order's timeline via your helpdesk

1. Pick **Order Update** as the trigger.
2. Add a **Condition**: `changes.fields` **Not empty and exists**.
3. Add **Send HTTP request** to your helpdesk with `{{order.name}}` in the body. Flow's text fields accept Liquid, so a `for` loop over `changes.items` adds one line per change with its `field`, `oldValue` and `newValue`.
4. Turn the workflow on.

> [!NOTE]
> **Good to know**
> - An order is updated many times in its life (payment, risk, fulfillment, each app that touches it). This is usually the busiest trigger of a store. Narrow it on the Triggers page: the settings icon opens **Which changes fire this**, next to the order history range.
> - A workflow that itself edits the order fires Order Update again. Narrowing to the fields you care about is what breaks such a loop.

## Order Tags Added

Fires when at least one tag is added to an order.

| | |
| --- | --- |
| **Fires from** | Shopify webhook plus change detection |
| **Permission** | **Order Data Access** |
| **Switched on** | Turning on a Flow workflow that uses it switches it on. Its own switch is on the app's **Triggers** page |

**Use it for**

- Let staff start a process by tagging: `gift-wrap`, `hold`, `priority`.
- React to tags from fraud, subscription or fulfillment apps.

### What Order Tags Added sends to your workflow

| Variable in Flow | Type | What it holds |
| --- | --- | --- |
| `order` | Order (Shopify) | The order whose tags changed. A full Shopify order, so all of its fields and Flow's own order actions are available. |
| `addedTags` | Text | Comma-separated list of tags that were added to the order |
| `removedTags` | Text | Comma-separated list of tags that were removed from the order in the same update (may be empty) |
| `currentTags` | Text | Comma-separated list of all current tags on the order |
| `previousTags` | Text | Comma-separated list of all previous tags on the order before this update |
| `eventId` | Text | The id of this event. Search for it in the app's Event History to see exactly what was sent. |

### Example: Hold fulfillment when an order is tagged hold

1. Pick **Order Tags Added** as the trigger.
2. Add a **Condition**: `addedTags` **Includes** `hold`.
3. Add a **For each** over `order.fulfillmentOrders` with the action **Hold fulfillment order**.
4. Turn the workflow on. Build the mirror image with **Order Tags Removed** and **Release fulfillment order hold**.

> [!NOTE]
> **Good to know**
> Tags that Flow adds fire this trigger as well. Make sure a workflow does not add the very tag it listens for.

## Order Tags Removed

Fires when at least one tag is removed from an order.

| | |
| --- | --- |
| **Fires from** | Shopify webhook plus change detection |
| **Permission** | **Order Data Access** |
| **Switched on** | Turning on a Flow workflow that uses it switches it on. Its own switch is on the app's **Triggers** page |

**Use it for**

- Release what a tag was holding back.
- Close a task when the `needs-review` tag is taken off.

### What Order Tags Removed sends to your workflow

| Variable in Flow | Type | What it holds |
| --- | --- | --- |
| `order` | Order (Shopify) | The order whose tags changed. A full Shopify order, so all of its fields and Flow's own order actions are available. |
| `removedTags` | Text | Comma-separated list of tags that were removed from the order |
| `addedTags` | Text | Comma-separated list of tags that were added to the order in the same update (may be empty) |
| `currentTags` | Text | Comma-separated list of all current tags on the order |
| `previousTags` | Text | Comma-separated list of all previous tags on the order before this update |
| `eventId` | Text | The id of this event. Search for it in the app's Event History to see exactly what was sent. |

### Example: Release the hold when the tag is removed

1. Pick **Order Tags Removed** as the trigger.
2. Add a **Condition**: `removedTags` **Includes** `hold`.
3. Add a **For each** over `order.fulfillmentOrders` with **Release fulfillment order hold**.
4. Turn the workflow on.

## Order Note Changed

Fires when the note of an order is added, changed or cleared, by staff or by the customer at checkout edits.

| | |
| --- | --- |
| **Fires from** | Shopify webhook plus change detection |
| **Permission** | **Order Data Access** |
| **Switched on** | Turning on a Flow workflow that uses it switches it on. Its own switch is on the app's **Triggers** page |

**Use it for**

- Alert the warehouse to notes added after the order was placed.
- Parse delivery instructions.

### What Order Note Changed sends to your workflow

| Variable in Flow | Type | What it holds |
| --- | --- | --- |
| `order` | Order (Shopify) | The order whose note changed. A full Shopify order, so all of its fields and Flow's own order actions are available. |
| `oldNote` | Text | The previous note on the order (empty if the note was not set before) |
| `newNote` | Text | The new note on the order (empty if the note was cleared) |
| `eventId` | Text | The id of this event. Search for it in the app's Event History to see exactly what was sent. |

### Example: Alert the warehouse to a late note

1. Pick **Order Note Changed** as the trigger.
2. Add a **Condition**: `order.displayFulfillmentStatus` **Not equal to** `FULFILLED`.
3. Add **Send internal email**: `{{order.name}} note changed from "{{oldNote}}" to "{{newNote}}"`.
4. Turn the workflow on.

## Order Attributes Changed

Fires when the custom attributes of an order (cart attributes, note attributes) are added, removed or changed. Each item in `changes.items` has a `changeType`, the `key` and both values.

| | |
| --- | --- |
| **Fires from** | Shopify webhook plus change detection |
| **Permission** | **Order Data Access** |
| **Switched on** | Turning on a Flow workflow that uses it switches it on. Its own switch is on the app's **Triggers** page |

**Use it for**

- React to attributes written by a delivery date picker, a gift option or a B2B form.
- Detect when staff corrects an attribute after the order.

### What Order Attributes Changed sends to your workflow

| Variable in Flow | Type | What it holds |
| --- | --- | --- |
| `order` | Order (Shopify) | The order whose custom attributes changed. A full Shopify order, so all of its fields and Flow's own order actions are available. |
| `changes` | Object | Detected attribute changes. Each item has a changeType (added, removed, or changed), the key, and the old and new values. |
| `changes.items` | List of objects | List of detected attribute changes |
| `changes.items (each).changeType` | Text | Type of change: added, removed, changed |
| `changes.items (each).key` | Text | The attribute key (a.k.a. note_attribute name) |
| `changes.items (each).oldValue` | Text | The previous value (empty string when changeType is 'added') |
| `changes.items (each).newValue` | Text | The new value (empty string when changeType is 'removed') |
| `eventId` | Text | The id of this event. Search for it in the app's Event History to see exactly what was sent. |

### Example: React to a changed delivery date

1. Pick **Order Attributes Changed** as the trigger.
2. Add a **Condition**: **At least one of** `changes.items` has `key` **Equal to** `delivery_date`.
3. Add **Send internal email**. Flow's text fields accept Liquid, so a `for` loop over `changes.items` prints each entry's `key` with its `oldValue` and `newValue`.
4. Turn the workflow on.

> [!NOTE]
> **Good to know**
> `changeType` is `added`, `removed` or `changed`. For `added` the old value is empty, for `removed` the new one.

## Order Line Items Changed

Fires when an order is **edited**: an item added or removed, a quantity, price or variant changed. Each entry in `changes.items` says which kind (`added`, `removed`, `quantity_changed`, `price_changed`, `variant_changed`) with old and new values.

| | |
| --- | --- |
| **Fires from** | Shopify webhook plus change detection |
| **Permission** | **Order Data Access** |
| **Switched on** | Turning on a Flow workflow that uses it switches it on. Its own switch is on the app's **Triggers** page |

**Use it for**

- Tell the warehouse that a picked order changed.
- Re-run fraud or margin checks after an edit.
- Sync order edits to an ERP, which Shopify's own order webhooks make painful.

### What Order Line Items Changed sends to your workflow

| Variable in Flow | Type | What it holds |
| --- | --- | --- |
| `order` | Order (Shopify) | The order that was updated. A full Shopify order, so all of its fields and Flow's own order actions are available. |
| `changes` | Object | Detected line item changes with old and new values |
| `changes.items` | List of objects | List of detected changes |
| `changes.items (each).changeType` | Text | Type of change: added, removed, quantity_changed, price_changed, variant_changed |
| `changes.items (each).lineItemId` | ID | Line item ID (if available) |
| `changes.items (each).productTitle` | Text | Product title |
| `changes.items (each).variantTitle` | Text | Variant title |
| `changes.items (each).oldVariantId` | ID | Old variant GID (for removed or variant_changed) |
| `changes.items (each).newVariantId` | ID | New variant GID (for added or variant_changed) |
| `changes.items (each).oldProductId` | ID | Old product GID (for removed or variant_changed) |
| `changes.items (each).newProductId` | ID | New product GID (for added or variant_changed) |
| `changes.items (each).oldQuantity` | Number | Old quantity (for quantity_changed) |
| `changes.items (each).newQuantity` | Number | New quantity (for quantity_changed) |
| `changes.items (each).oldPrice` | Text | Old price (for price_changed) |
| `changes.items (each).newPrice` | Text | New price (for price_changed) |
| `changes.items (each).lineItemData` | Text | Full line item data as JSON |
| `eventId` | Text | The id of this event. Search for it in the app's Event History to see exactly what was sent. |

### Example: Stop the warehouse when an item is removed

1. Pick **Order Line Items Changed** as the trigger.
2. Add a **Condition**: **At least one of** `changes.items` has `changeType` **Equal to** `removed`.
3. Add **Add order tags** `edited-recheck` and **Send internal email** that lists the `productTitle` and `variantTitle` of each entry in `changes.items` (a Liquid `for` loop in the email body).
4. Turn the workflow on.

> [!NOTE]
> **Good to know**
> - `oldQuantity` and `newQuantity` are numbers, so conditions like **Greater than** work. `oldPrice` and `newPrice` are text.
> - `lineItemData` holds the full line item as JSON for a **Run code** step.

## Order Fulfillment Changed

Fires when the fulfillment status of an order changes (`null`, `partial`, `fulfilled`, `restocked`) and on fulfillment events in between: a line fulfilled, a fulfillment cancelled, tracking added or changed.

| | |
| --- | --- |
| **Fires from** | Shopify webhook plus change detection |
| **Permission** | **Order Data Access** |
| **Switched on** | Turning on a Flow workflow that uses it switches it on. Its own switch is on the app's **Triggers** page |

**Use it for**

- Send your own shipping notification with tracking.
- Detect cancelled fulfillments.
- Update an ERP per shipment rather than per order.

### What Order Fulfillment Changed sends to your workflow

| Variable in Flow | Type | What it holds |
| --- | --- | --- |
| `order` | Order (Shopify) | The order whose fulfillment status changed. A full Shopify order, so all of its fields and Flow's own order actions are available. |
| `oldFulfillmentStatus` | Text | The previous REST fulfillment status (null, partial, fulfilled, restocked) |
| `newFulfillmentStatus` | Text | The new REST fulfillment status (null, partial, fulfilled, restocked) |
| `changes` | Object | Detected fulfillment changes with details |
| `changes.items` | List of objects | List of detected changes |
| `changes.items (each).changeType` | Text | Type of change: status_changed, line_item_fulfilled, line_item_unfulfilled, tracking_updated |
| `changes.items (each).lineItemId` | ID | Line item ID (for line_item_fulfilled/unfulfilled) |
| `changes.items (each).productTitle` | Text | Product title |
| `changes.items (each).variantTitle` | Text | Variant title |
| `changes.items (each).oldFulfillmentStatus` | Text | Old fulfillment status (e.g. null, partial, fulfilled, restocked) |
| `changes.items (each).newFulfillmentStatus` | Text | New fulfillment status (e.g. null, partial, fulfilled, restocked) |
| `changes.items (each).fulfillmentId` | ID | Fulfillment ID (for tracking_updated) |
| `changes.items (each).trackingNumber` | Text | Tracking number |
| `changes.items (each).trackingUrl` | Text | Tracking URL |
| `changes.items (each).trackingCompany` | Text | Tracking company |
| `eventId` | Text | The id of this event. Search for it in the app's Event History to see exactly what was sent. |

### Example: Forward new tracking numbers

1. Pick **Order Fulfillment Changed** as the trigger.
2. Add a **For each** over `changes.items`.
3. Inside the loop add a **Condition**: the loop item's `trackingNumber` **Not empty and exists**.
4. Add **Send HTTP request** with `{{order.name}}` and the loop item's `trackingNumber` and `trackingUrl`. Pick them with **Add variable**: Flow lists the loop item next to the trigger's data.
5. Turn the workflow on.

> [!NOTE]
> **Good to know**
> - These are the REST status values. An unfulfilled order has the status `null`, which arrives as an empty text.
> - For the states the admin shows (In progress, On hold, Scheduled) use **Order Display Status Changed**.

## Order Display Status Changed

Fires when the fulfillment status **as the admin displays it** changes: `unfulfilled`, `in_progress`, `on_hold`, `scheduled`, `partially_fulfilled`, `fulfilled`. Shopify sends no webhook for these, so the app checks on a schedule.

| | |
| --- | --- |
| **Fires from** | Checked on a schedule, because Shopify sends no webhook for it |
| **Permission** | **Order Data Access** |
| **Switched on** | Turning on a Flow workflow that uses it switches it on. Its own switch is on the app's **Triggers** page |

**Use it for**

- React when a fulfillment service or POS marks an order In progress.
- Escalate orders that are put On hold.

### What Order Display Status Changed sends to your workflow

| Variable in Flow | Type | What it holds |
| --- | --- | --- |
| `order` | Order (Shopify) | The order whose display fulfillment status changed. A full Shopify order, so all of its fields and Flow's own order actions are available. |
| `oldDisplayFulfillmentStatus` | Text | The previous display fulfillment status (e.g. unfulfilled, in_progress, partially_fulfilled, fulfilled, on_hold, scheduled) |
| `newDisplayFulfillmentStatus` | Text | The new display fulfillment status (e.g. unfulfilled, in_progress, partially_fulfilled, fulfilled, on_hold, scheduled) |
| `fulfillmentStatus` | Text | The current fulfillment status (unfulfilled, partial, fulfilled, restocked) |
| `changes` | Object | Detected display status changes with details |
| `changes.items` | List of objects | List of detected changes |
| `changes.items (each).changeType` | Text | Type of change: order_status_changed, fulfillment_display_status_changed |
| `changes.items (each).fulfillmentId` | ID | Fulfillment ID (for per-fulfillment display status changes) |
| `changes.items (each).oldDisplayStatus` | Text | Previous display status |
| `changes.items (each).newDisplayStatus` | Text | New display status |
| `eventId` | Text | The id of this event. Search for it in the app's Event History to see exactly what was sent. |

### Example: Escalate orders that go on hold

1. Pick **Order Display Status Changed** as the trigger.
2. Add a **Condition**: `newDisplayFulfillmentStatus` **Equal to** `on_hold`.
3. Add **Send internal email** to customer service with `{{order.name}}`.
4. Turn the workflow on.

> [!NOTE]
> **Good to know**
> It is a polling trigger: expect a short delay instead of an instant event, and no event for a status that flipped and flipped back between two checks.

## Order Financial Status Changed

Fires when the financial status of an order changes, for example `pending` to `paid`, `paid` to `partially_refunded` or `refunded`, `authorized` to `voided`.

| | |
| --- | --- |
| **Fires from** | Shopify webhook plus change detection |
| **Permission** | **Order Data Access** |
| **Switched on** | Turning on a Flow workflow that uses it switches it on. Its own switch is on the app's **Triggers** page |

**Use it for**

- React to bank transfers and invoices that are marked paid days after the order.
- Tell accounting about refunds.
- Release an order for fulfillment only once it is paid.

### What Order Financial Status Changed sends to your workflow

| Variable in Flow | Type | What it holds |
| --- | --- | --- |
| `order` | Order (Shopify) | The order whose financial status changed. A full Shopify order, so all of its fields and Flow's own order actions are available. |
| `oldFinancialStatus` | Text | The previous financial status (e.g. pending, authorized, paid) |
| `newFinancialStatus` | Text | The new financial status (e.g. paid, partially_refunded, refunded, voided) |
| `eventId` | Text | The id of this event. Search for it in the app's Event History to see exactly what was sent. |

### Example: Release B2B orders when the invoice is paid

1. Pick **Order Financial Status Changed** as the trigger.
2. Add a **Condition**: `oldFinancialStatus` **Equal to** `pending` and `newFinancialStatus` **Equal to** `paid`.
3. Add **Remove order tags** `awaiting-payment` and a **For each** over `order.fulfillmentOrders` with **Release fulfillment order hold**.
4. Turn the workflow on.

> [!NOTE]
> **Good to know**
> Shopify's own **Order paid** trigger covers the plain case. This one earns its place when you need the status you came **from**, or any transition other than to paid.

## Order Shipping Address Changed

Fires when the shipping address of an order changes after it was placed. `shippingAddress` is one line, `shippingAddressDetails` the separate fields.

| | |
| --- | --- |
| **Fires from** | Shopify webhook plus change detection |
| **Permission** | **Order Data Access** + **Customer Data Access** |
| **Switched on** | Turning on a Flow workflow that uses it switches it on. Its own switch is on the app's **Triggers** page |

**Use it for**

- Stop or re-route a shipment when the address changes after picking started.
- Re-run address validation or a fraud check.

### What Order Shipping Address Changed sends to your workflow

| Variable in Flow | Type | What it holds |
| --- | --- | --- |
| `order` | Order (Shopify) | The order whose shipping address changed. A full Shopify order, so all of its fields and Flow's own order actions are available. |
| `shippingAddress` | Text | The order's current shipping (delivery) address as a single formatted line (may be empty) |
| `shippingAddressDetails` | Object | The order's current shipping (delivery) address as structured fields (may be empty) |
| `shippingAddressDetails.firstName` | Text | First name on the address |
| `shippingAddressDetails.lastName` | Text | Last name on the address |
| `shippingAddressDetails.name` | Text | Full name, derived from firstName and lastName |
| `shippingAddressDetails.company` | Text | Company or organization name |
| `shippingAddressDetails.address1` | Text | First line - street address or PO box number |
| `shippingAddressDetails.address2` | Text | Second line - apartment, suite, or unit |
| `shippingAddressDetails.city` | Text | City, district, village, or town |
| `shippingAddressDetails.province` | Text | Region - province, state, or district |
| `shippingAddressDetails.provinceCode` | Text | Region code (for example ON) |
| `shippingAddressDetails.zip` | Text | ZIP or postal code |
| `shippingAddressDetails.country` | Text | Country name |
| `shippingAddressDetails.countryCode` | Text | Two-letter country code (for example US) |
| `shippingAddressDetails.phone` | Text | Phone number on the address |
| `eventId` | Text | The id of this event. Search for it in the app's Event History to see exactly what was sent. |

### Example: Hold an order whose address changed

1. Pick **Order Shipping Address Changed** in Flow. It needs **Order** and **Customer Data Access**; when you switch it on from the Triggers page the app asks for both in one dialog.
2. Add a **Condition**: `order.displayFulfillmentStatus` **Not equal to** `FULFILLED`.
3. Add **Add order tags** `address-changed` and **Send internal email** to the warehouse.
4. Turn the workflow on.

> [!NOTE]
> **Good to know**
> - This is an **opt-in, protected data** trigger. The values are encrypted at rest and decrypted only to hand them to your workflow. See [Permissions and data access](https://docs.workflow-trigger-extensions.app/permissions-and-data-access.md).
> - Two permissions are needed because Shopify exposes the address through the order's customer.

## Next steps

By default the app watches orders from the last 60 days, which is what Shopify grants. To react to edits on older orders, extend the order history on the Permissions page. See [Permissions and data access](https://docs.workflow-trigger-extensions.app/permissions-and-data-access.md).

- [Complete trigger reference](https://docs.workflow-trigger-extensions.app/trigger-reference.md) - every trigger on one page.
- [Choosing the right trigger](https://docs.workflow-trigger-extensions.app/choosing-the-right-trigger.md) - which one fits your case.
- [Custom triggers](https://docs.workflow-trigger-extensions.app/custom-triggers.md) - when no built-in trigger fits, write your own.
- [Event history and troubleshooting](https://docs.workflow-trigger-extensions.app/event-history-and-troubleshooting.md) - see what fired and what it carried.
- [Trigger events on a record's admin page](https://docs.workflow-trigger-extensions.app/admin-blocks.md) - the latest events of one record, on its own page in the Shopify admin.
