How to Auto-Fulfill WooCommerce Orders with ShipStation
Automatically push WooCommerce orders to ShipStation for label generation and fulfillment.
What This Integration Does
ShipStation is great at the actual mechanics of shipping (carrier rates, label production, batching, returns) but its native WooCommerce integration imports orders on a polling interval and forces you into ShipStation's UI for things like tag-based automations. Building the integration as a Spojit workflow gives you near-real-time order push, lets you bake your own business rules into the order (warehouse routing, gift wrap, freight class) before ShipStation sees it, and gets tracking numbers back into WooCommerce on the same run.
The workflow runs on each new WooCommerce order. It fetches the full order, transforms it into ShipStation's order shape, creates the ShipStation order, rates the shipment, generates a label, and writes the tracking number back to WooCommerce so the customer's order page updates and the standard "completed" email fires.
Prerequisites
- A WooCommerce connection (REST API key and secret with read/write).
- A ShipStation connection (API key and secret).
- At least one ShipStation warehouse and carrier (USPS, UPS, FedEx, etc.) connected to the account.
- Product weights on WooCommerce products - rates and packing slips need them.
Step 1: WooCommerce Webhook Trigger
Drop a Trigger node and set type to Webhook. In WooCommerce, create a webhook for the Order created topic (or Order updated if you'd rather wait for the order to reach "processing"). Point it at the trigger URL.
Step 2: Fetch the Full Order
WooCommerce webhook bodies are reasonably complete but it's safer to refetch. Add a Connector node on woocommerce with get-order, passing {{ trigger.body.id }}.
Step 3: Transform - Map to ShipStation
Add a Transform node that reshapes the WooCommerce order to ShipStation's order JSON:
{
"orderNumber": "{{ order.number }}",
"orderDate": "{{ order.date_created }}",
"orderStatus": "awaiting_shipment",
"customerEmail": "{{ order.billing.email }}",
"billTo": {
"name": "{{ order.billing.first_name }} {{ order.billing.last_name }}",
"street1": "{{ order.billing.address_1 }}",
"city": "{{ order.billing.city }}",
"state": "{{ order.billing.state }}",
"postalCode": "{{ order.billing.postcode }}",
"country": "{{ order.billing.country }}"
},
"shipTo": { ... },
"items": [
{ "sku": "{{ item.sku }}", "name": "{{ item.name }}", "quantity": "{{ item.quantity }}", "unitPrice": "{{ item.price }}" }
]
}
Step 4: Create the ShipStation Order and Rate It
Add two Connector nodes on shipstation:
create-order- pushes the transformed order in. ShipStation returns its internalorderId.get-rates- returns cost and ETA across the connected carriers. Pick the cheapest acceptable service in a Transform step (or fall back to a configured default).
Step 5: Generate the Label
Add a Connector node on shipstation with create-shipment-label. Pass the order ID, chosen carrier code, and service code. The response includes a tracking number and a base64 label PDF.
Step 6: Write Back to WooCommerce
Add a Connector node on woocommerce with update-order. Set order status to completed and add a note containing the tracking number and carrier. If you use a "WooCommerce Shipment Tracking" style plugin, push the tracking into its meta field so customers see a clean tracking link on their order page. Optionally fan out to slack send-message into #fulfillment.
Tips
- Use a Condition on order total to route high-value orders through a signature-required service.
- For multi-item orders going to the same destination, ship as a single multi-line shipment in ShipStation rather than splitting per item.
- Schedule a daily companion workflow to void any unused labels with
void-labeland reclaim the postage. - Store the label PDF in your own bucket; ShipStation retains them but has size limits on free plans.
Common Pitfalls
- Weight units: WooCommerce stores weight in the store's configured unit (kg, lb, oz). ShipStation expects ounces by default. Normalize in the Transform step.
- State codes: WooCommerce sends full names for some locales but ShipStation wants ISO codes. Maintain a small mapping table or use a Transform node to normalise.
- Webhook duplicates: WooCommerce can fire both
order.createdandorder.updatedfor the same order. Filter on status to avoid creating duplicate ShipStation orders. - Order status race: setting WooCommerce status to
completedcan trigger the customer email before the tracking number is saved. Save the tracking note first, then transition status.
Testing
Create a test WooCommerce order with a real-looking address and a single low-weight item. Run the workflow once manually and confirm: the order shows up in ShipStation with the right items and address, a label is produced, and the WooCommerce order ends in completed status with a tracking number in its notes. Only after that should you enable the production webhook.