Order events and automation
As a sales order moves through its life, SKU.io quietly emits an event at each milestone — the order is created, a shipment goes out, the order is cancelled, someone edits the ship-to address. You never fire these by hand; they're the internal signals SKU.io uses to react automatically. This page explains which events an order emits and the three kinds of automatic reaction they set off: automation workflows, outbound webhooks, and background sync to your shipping providers.
Words like fulfillment order, shipping provider, and webhook are defined the first time they appear below.
The events an order emits
Each event marks one thing that happened to the order and carries the order (and, for a shipment, the specific fulfillment) to whatever is listening. You don't see the events themselves — you see their effects.
| Event | Fires when | What it sets off |
|---|---|---|
| Sales order created | A new order is saved, from any channel or a manual entry | Automation workflows, the sales_order.created webhook, the Address Warning tag check |
| Sales order failed | An order that was being created fails validation | The order is kept as a draft with a warning, so nothing downstream treats a half-formed order as real |
| Sales order fulfilled | Each shipment is recorded against the order (full or partial) | Automation workflows (the event carries a "partial?" flag) |
| Sales order shipped | The order crosses into fully fulfilled (or over-fulfilled) | The sales_order.shipped webhook |
| Sales order cancelled | The order is cancelled by any path | The sales_order.cancelled webhook, and a background recall of any live fulfillment orders at their provider |
| Shipping address changed | A saved edit changes the customer's ship-to address | A background push of the new address to the order's live fulfillment orders' providers |
| Shipping method changed | The order's shipping method is edited or re-synced from a channel | Editable fulfillment orders inherit the new method |
The first five are true lifecycle milestones. The last two are change signals — they exist to keep already-dispatched work in step with an order you edit after the fact.
Everything happens after the change is saved
Two rules hold for almost all of these reactions, and they explain the behavior you'll observe:
- They run only after the change commits. SKU.io waits until the order is safely saved before reacting, so a workflow or webhook always sees the final, committed order — never a half-written one. If the save is rolled back (a rare database conflict during a bulk import, say), the reaction never happens.
- They run in the background. Automation, webhooks, and provider sync are queued and processed by a worker, not in the moment you click. That's deliberate: a slow third-party provider or a busy workflow can't hold up your order from saving.
Because provider sync runs in the background and off your critical path, a failure there never blocks the order. Instead, SKU.io leaves the live provider order alone and writes a note on the sales order so a person can reconcile it. You'll find those notes on the order — see Add notes, tags, and custom fields.
Automation workflows react to created and fulfilled
Two of the events are wired to the Automation builder as triggers you can start a workflow from:
| Trigger | Starts a workflow when | Useful for |
|---|---|---|
| Sales Order Created | Any new order lands | Tagging, notifying a channel, routing high-value orders for review |
| Sales Order Fulfilled | A shipment is recorded | Post-ship follow-ups, review requests, syncing an external system |
The Sales Order Fulfilled trigger fires on every shipment, including a partial one, and the event tells your workflow whether this was a partial fulfillment — so you can, for example, hold a "thanks for your order" message until the order ships in full.
When automation is turned off for your account, these triggers are skipped entirely — the order still saves and ships normally; no workflow runs.
Webhooks notify outside systems
If you (or a connected app) subscribe to SKU.io's outbound webhooks, three sales-order events are published on the wire:
| Webhook event | Published when |
|---|---|
sales_order.created | A new order is created (any channel or manual) |
sales_order.shipped | An order is fully fulfilled / shipped |
sales_order.cancelled | An order is cancelled |
Each delivery carries a flat snapshot of the order (its lines, customer, and store) so the receiving system doesn't have to call back for details. Webhooks are a developer feature configured in your API settings; the catalog of available events and sample payloads lives there.
SKU.io recalculates an order's fulfillment status constantly as lines change. The sales_order.shipped webhook fires only on the transition into fully fulfilled (or over-fulfilled), not on every recalculation while the order is already fulfilled. So a subscriber receives exactly one "shipped" event per order, at the moment it becomes complete — even if later edits recompute the status again.
Note the split between the two "ship" reactions: the Sales Order Fulfilled automation trigger fires on each individual shipment (partial or full), while the sales_order.shipped webhook fires once, only when the whole order is complete. There is no webhook for a partial shipment.
Background sync keeps shipping providers in step
The last group of reactions exists to keep a 3PL or carrier that already has your order from drifting out of sync when the order changes underneath it. Each targets the order's live fulfillment orders — the ones that are still active and have been registered with a provider — and leaves finished ones alone.
Cancelling recalls live fulfillment orders
When you cancel an order, SKU.io tries to recall (cancel) each of its live fulfillment orders at the shipping provider, so a 3PL doesn't ship an order you just cancelled. This runs in the background after the cancellation is saved.
If the provider can't cancel the order — it has already shipped, is past the cancellation window, or the provider is unreachable — SKU.io leaves the live provider order intact and writes a note on the sales order asking a person to reconcile it (issue a return or refund, or void the shipment from the Fulfillment tab). It deliberately doesn't mark the shipment "voided" inside SKU.io, because a system-initiated cancel has no operator present to confirm that a still-live carrier order should be abandoned. Only a person voiding a shipment by hand takes that step.
Editing the ship-to address re-sends it to the provider
Change a customer's shipping address on an order that has already been handed to a provider, and SKU.io pushes the new address to that provider in the background so the parcel goes to the right place. This fires only on an edit — a brand-new order's address rides along with its first dispatch, so there's nothing to re-send.
If the provider can't accept an address update before a shipment exists, that's a harmless no-op: the new address simply travels with the first shipment. But if a genuine push fails — the provider has already started picking, or is unreachable — SKU.io notes the order so the stale address is visible for manual fixing.
Editing the shipping method updates editable fulfillment orders
Change an order's shipping method — by hand or through a channel re-sync — and its still-editable fulfillment orders (those not yet submitted to a provider) inherit the new method right away. Submitted or closed fulfillment orders are past the point of change and are left untouched, and a method you set by hand on a specific fulfillment order is preserved rather than overwritten.
Across all three of these sync behaviors, SKU.io only touches fulfillment orders that are still open and changeable. Once a fulfillment order has been submitted to a provider or closed, it's considered locked in — SKU.io won't silently rewrite it, and any correction there is a deliberate action you take on the Fulfillment tab. See Split, move, and merge fulfillments.
Worked example: One order, several reactions
Follow SO-DOCS-0001 for customer Blue Bottle Retail and watch the events fire:
| Moment | Event emitted | Automatic reactions |
|---|---|---|
| You create the order for 3 × Stainless Water Bottle 750ml | Sales order created | Any "new order" workflow runs; a sales_order.created webhook is sent; the address is checked for warnings |
| A 3PL picks up 2 of the 3 units | Sales order fulfilled (partial) | A "fulfilled" workflow runs, told this was a partial shipment; no webhook yet |
| The final unit ships and the order completes | Sales order fulfilled (full) → Sales order shipped | The "fulfilled" workflow runs again; now the single sales_order.shipped webhook is sent |
| Later, you cancel a duplicate copy of the order before it ships | Sales order cancelled | A sales_order.cancelled webhook is sent; the copy's live fulfillment order is recalled at the provider |
Each row is one milestone producing its own, independent set of reactions — and the sales_order.shipped webhook lands exactly once, on the row where the order becomes complete.