Back to Order Processing & Fulfilment

Developer Handover Pack

Everything needed to build the automation from scratch: current state, full agent specs, data flow, and the build stack.

4 pagesPDF · Technical
FS-DOC-04Technical

Developer Handover Pack

Order Processing & Fulfilment

[YourCompany.com] · Fulfilment Department · Prepared by FullSpec · [Today's Date]

This document gives the FullSpec build team everything needed to construct, configure, and hand over the Order Processing and Fulfilment automation. It covers the current-state step map with bottleneck flags, full agent specifications with IO contracts, the end-to-end data flow trace, and the recommended build stack. The owner side is responsible for confirming API access, courier rules, and Cin7 data accuracy before build begins. FullSpec handles all construction, integration wiring, testing, and deployment.

01Process snapshot

Step
Name
Description
1
Check New Orders in Sales Platform
Fulfilment Coordinator logs into Shopify to review new paid orders for completeness and payment status. Time cost: 10 min/order
2 BOTTLENECK
Verify Stock Availability in Inventory System
Each order line is cross-referenced against Cin7 to confirm on-hand stock. If stock is short, the coordinator flags the order manually. Stale inventory records cause frequent false blocks. Time cost: 12 min/order
3
Create Sales Invoice in Accounting Platform
A matching sales invoice is manually created in Xero. Line items, customer details, and amounts are re-entered from Shopify. Time cost: 8 min/order
4
Generate and Print Packing Slip
A packing slip is generated from Shopify or a Word template and printed for the warehouse picker. Time cost: 5 min/order
5
Pick and Pack Order in Warehouse
Warehouse staff pick items per the packing slip, pack the goods, and place the slip inside the parcel. Remains manual. Time cost: 8 min/order
6 BOTTLENECK
Book Courier and Print Shipping Label
Staff log into StarShipIt, manually enter or import the order, select a courier service, and print the shipping label. Single largest per-order time sink. Time cost: 7 min/order
7
Apply Label and Dispatch Parcel
Printed label is applied to the parcel and handed to the courier or dropped at a depot. Remains manual. Time cost: 4 min/order
8
Update Order Status in Shopify
Staff return to Shopify and manually mark the order as fulfilled, entering the tracking number from StarShipIt. Time cost: 5 min/order
9
Send Shipping Confirmation to Customer
A shipping confirmation email with tracking number is manually composed and sent via Gmail. Time cost: 4 min/order
10
Deduct Stock in Inventory System
Cin7 stock levels are manually adjusted or confirmed after dispatch to keep inventory counts accurate. Time cost: 5 min/order
11
Notify Team of Dispatch in Slack
A message is posted to the fulfilment Slack channel confirming shipment so customer support staff are aware. Time cost: 2 min/order
Time cost summary: Total manual time per cycle is 70 minutes per order across all 11 steps. At approximately 280 orders per month (~70 orders per week), this equates to 9 hours of manual admin per week and roughly 39 hours per 30-day period. The automation replaces steps 1, 2, 3, 4, 6, 8, 9, 10, and 11. Steps 5 (pick and pack) and 7 (apply label and dispatch) remain with the warehouse team because they require physical handling.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Order Intake Agent

Monitors Shopify for new paid orders via webhook, extracts all line items and their SKUs, and queries Cin7 for current on-hand stock against each SKU. If all SKUs have sufficient stock, the validated order record is passed to the Fulfilment Dispatch Agent. If any SKU is short, the agent sends a Slack alert to the fulfilment channel with the order number, the affected SKU, and the stock shortfall quantity, then halts the order in a held state pending manual review. This agent must handle multi-line orders atomically: a single short SKU holds the entire order, not just that line. Estimated build time: 14 hours. Complexity: Moderate.

Trigger
Shopify order.paid webhook fires on new paid order creation
Tools
Shopify (webhook inbound), Cin7 (stock query), Slack (exception alert)
Replaces steps
Steps 1, 2, and 11 (partial: the Slack dispatch note at step 11 is shared with the Fulfilment Dispatch Agent)
Estimated build
14 hours / Moderate
// Input
shopify.order {
  order_id: string,
  order_number: string,
  customer.email: string,
  customer.first_name: string,
  customer.last_name: string,
  shipping_address: { address1, address2, city, province, zip, country },
  line_items: [{ sku: string, quantity: integer, price: decimal, title: string }],
  total_price: decimal,
  currency: string
}

// Stock check (per SKU)
cin7.stock_query -> GET /api/v2/inventory?sku={sku}
cin7.stock_response {
  sku: string,
  on_hand_quantity: integer,
  location_id: string
}

// Output (stock confirmed)
validated_order {
  order_id: string,
  order_number: string,
  customer: { email, first_name, last_name },
  shipping_address: { address1, address2, city, province, zip, country },
  line_items: [{ sku, quantity, price, title }],
  total_price: decimal,
  currency: string,
  stock_status: 'confirmed'
}

// Output (stock short - Slack alert payload)
slack.message {
  channel: '#fulfilment-alerts',
  text: 'STOCK HOLD: Order {order_number} held. SKU {sku} has {on_hand_quantity} on hand, {quantity} required.',
  order_id: string,
  held_at: ISO8601 timestamp
}
  • Shopify plan must be on Basic or above to expose the orders/paid webhook endpoint. Confirm plan tier before build.
  • Cin7 API access requires a Cin7 Core or Cin7 Omni subscription with the API module enabled. The operations manager must provision an API key scoped to inventory read and sales order write before this agent can be tested.
  • Stock-check logic must iterate over all line_items in a single pass and collect all short SKUs before sending a Slack alert. Do not send one alert per short SKU.
  • The agent must write the order_id and held_at timestamp to a dedicated error log table (Supabase: table 'held_orders') when a stock hold is triggered, so the fulfilment team can track and manually release held orders.
  • Dedupe: Shopify may occasionally fire duplicate webhooks. The agent must check the error log and active pipeline for the order_id before processing. If the order_id already exists in either location, skip silently.
  • The Slack alert channel name ('#fulfilment-alerts') must be confirmed with the operations manager before go-live. Slack workspace must have the automation platform's bot app installed and invited to that channel.
  • Stock threshold logic: if on_hand_quantity >= quantity, treat as confirmed. Do not apply a buffer unless the client specifies one during discovery.
Fulfilment Dispatch Agent

Receives the validated order record from the Order Intake Agent and executes the full dispatch chain without human input. It creates a draft sales invoice in Xero mapped from Shopify line items, pushes the order to StarShipIt to book the appropriate courier and retrieve the shipping label and tracking number, updates Shopify with the fulfilment status and tracking number, sends a personalised shipping confirmation email to the customer via Gmail using a pre-approved template, and posts a dispatch summary to the Slack fulfilment channel. Each step must complete successfully before the next begins. Any step failure triggers an error log entry and a Slack alert to the fulfilment channel with the step name and error message. Estimated build time: 22 hours. Complexity: Moderate.

Trigger
Validated order record passed by Order Intake Agent (stock_status = 'confirmed')
Tools
Xero (invoice creation), StarShipIt (courier booking and label), Shopify (fulfilment update), Gmail (customer email), Slack (dispatch notification)
Replaces steps
Steps 3, 4, 6, 8, 9, and 10
Estimated build
22 hours / Moderate
// Input
validated_order {
  order_id: string,
  order_number: string,
  customer: { email, first_name, last_name },
  shipping_address: { address1, address2, city, province, zip, country },
  line_items: [{ sku, quantity, price, title }],
  total_price: decimal,
  currency: string,
  stock_status: 'confirmed'
}

// Step 1: Xero invoice creation
xero.invoice_payload {
  Type: 'ACCREC',
  Contact: { EmailAddress: customer.email, Name: '{first_name} {last_name}' },
  LineItems: [{ Description: title, Quantity: quantity, UnitAmount: price, AccountCode: '200' }],
  CurrencyCode: currency,
  Reference: order_number
}
xero.invoice_response -> invoice_id: string

// Step 2: StarShipIt courier booking
starshipit.order_payload {
  reference: order_number,
  destination: { address: address1, suburb: city, state: province, post_code: zip, country: country },
  packages: [{ weight_kg: derived_from_sku_lookup }]
}
starshipit.booking_response {
  tracking_number: string,
  carrier_name: string,
  label_url: string
}

// Step 3: Shopify fulfilment update
shopify.fulfilment_payload {
  order_id: order_id,
  tracking_number: tracking_number,
  tracking_company: carrier_name,
  notify_customer: false
}

// Step 4: Gmail confirmation email
gmail.send_payload {
  to: customer.email,
  subject: 'Your order {order_number} has shipped!',
  body_template: 'shipping_confirmation_v1',
  template_vars: { first_name, order_number, tracking_number, carrier_name }
}

// Output: Slack dispatch notification
slack.message {
  channel: '#fulfilment-dispatched',
  text: 'DISPATCHED: Order {order_number} | {first_name} {last_name} | Courier: {carrier_name} | Tracking: {tracking_number}',
  xero_invoice_id: invoice_id
}
  • Xero connection requires OAuth 2.0 with scopes: accounting.transactions, accounting.contacts. The tenant_id must be stored in the credential store and refreshed automatically. Confirm the Xero plan supports API access (Starter plan has invoice limits; Growing or above recommended).
  • StarShipIt API key must be provisioned from the StarShipIt account settings. Confirm that courier service rules, rate cards, and package weight defaults are configured in StarShipIt before the dispatch agent is built. The agent relies on StarShipIt's internal rules to select the correct service; it does not implement its own routing logic.
  • Shopify fulfilment: set notify_customer to false on the fulfilment API call to prevent Shopify's native shipping notification email firing alongside the Gmail confirmation. Review this with the client during discovery as Shopify may have this enabled by default.
  • Gmail sending requires a Google Workspace account with Gmail API enabled. OAuth 2.0 scopes required: gmail.send. The sending address must be a verified Google account, not a generic alias, to avoid deliverability issues.
  • The Gmail email template ('shipping_confirmation_v1') must be built and approved by the client before end-to-end testing begins. Store the template in the automation platform's template store, not hardcoded in the workflow.
  • Xero AccountCode '200' is the default sales revenue account. Confirm this with the client's accountant before go-live. Different product categories may require different account codes.
  • If any step in the dispatch chain fails (Xero, StarShipIt, Shopify, Gmail), log the failure to Supabase table 'dispatch_errors' with order_id, failed_step, error_message, and timestamp. Then send a Slack alert to '#fulfilment-alerts' immediately. Do not silently retry without logging.
  • StarShipIt weight data: if the Cin7 product records include weight_kg per SKU, pull this during the Order Intake Agent stock check and pass it through the validated_order payload. If weight data is absent from Cin7, a default package weight must be agreed with the client and hardcoded as a fallback.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical

03End-to-end data flow

Full trigger-to-output data flow trace including agent handoffs, field names, and error path
// ============================================================
// ORDER PROCESSING & FULFILMENT - END-TO-END DATA FLOW
// ============================================================

// TRIGGER
// Shopify fires order.paid webhook on new paid order
SHOPIFY.WEBHOOK -> order.paid {
  order_id,
  order_number,
  customer.email,
  customer.first_name,
  customer.last_name,
  shipping_address.address1,
  shipping_address.address2,
  shipping_address.city,
  shipping_address.province,
  shipping_address.zip,
  shipping_address.country,
  line_items[].sku,
  line_items[].quantity,
  line_items[].price,
  line_items[].title,
  total_price,
  currency
}

// ============================================================
// AGENT HANDOFF 1: Shopify -> Order Intake Agent
// ============================================================

// ORDER INTAKE AGENT: Dedupe check
CHECK supabase.held_orders WHERE order_id = order_id
CHECK active_pipeline WHERE order_id = order_id
  -> IF FOUND: skip (duplicate webhook)
  -> IF NOT FOUND: proceed

// ORDER INTAKE AGENT: Stock check loop (per SKU)
FOR EACH line_item IN order.line_items:
  CIN7.GET /api/v2/inventory?sku={line_item.sku} -> {
    sku,
    on_hand_quantity,
    location_id
  }
  IF on_hand_quantity < line_item.quantity:
    short_skus.APPEND { sku, on_hand_quantity, required: line_item.quantity }

// ORDER INTAKE AGENT: Route decision
IF short_skus.length > 0:
  // Stock short path
  SLACK.POST #fulfilment-alerts {
    text: 'STOCK HOLD: Order {order_number} | Short SKUs: {short_skus}',
    held_at: ISO8601_timestamp
  }
  SUPABASE.INSERT held_orders { order_id, order_number, short_skus, held_at }
  STOP // human review required

ELSE:
  // Stock confirmed path - build validated_order payload
  validated_order = {
    order_id, order_number,
    customer: { email, first_name, last_name },
    shipping_address: { address1, address2, city, province, zip, country },
    line_items: [{ sku, quantity, price, title, weight_kg }],
    total_price, currency,
    stock_status: 'confirmed'
  }

// ============================================================
// AGENT HANDOFF 2: Order Intake Agent -> Fulfilment Dispatch Agent
// ============================================================

// FULFILMENT DISPATCH AGENT: Step 1 - Xero invoice
XERO.POST /api.xro/2.0/Invoices {
  Type: 'ACCREC',
  Contact.EmailAddress: customer.email,
  Contact.Name: '{first_name} {last_name}',
  LineItems[].Description: line_item.title,
  LineItems[].Quantity: line_item.quantity,
  LineItems[].UnitAmount: line_item.price,
  LineItems[].AccountCode: '200',
  CurrencyCode: currency,
  Reference: order_number
} -> { invoice_id }

// FULFILMENT DISPATCH AGENT: Step 2 - StarShipIt booking
STARSHIPIT.POST /orders {
  reference: order_number,
  destination.address: shipping_address.address1,
  destination.suburb: shipping_address.city,
  destination.state: shipping_address.province,
  destination.post_code: shipping_address.zip,
  destination.country: shipping_address.country,
  packages[].weight_kg: line_item.weight_kg (or default fallback)
} -> {
  tracking_number,
  carrier_name,
  label_url
}

// FULFILMENT DISPATCH AGENT: Step 3 - Shopify fulfilment update
SHOPIFY.POST /orders/{order_id}/fulfillments {
  tracking_number: tracking_number,
  tracking_company: carrier_name,
  notify_customer: false
}

// FULFILMENT DISPATCH AGENT: Step 4 - Gmail customer email
GMAIL.SEND {
  to: customer.email,
  subject: 'Your order {order_number} has shipped!',
  template: 'shipping_confirmation_v1',
  vars: { first_name, order_number, tracking_number, carrier_name }
}

// FULFILMENT DISPATCH AGENT: Step 5 - Slack dispatch notification
SLACK.POST #fulfilment-dispatched {
  text: 'DISPATCHED: Order {order_number} | {first_name} {last_name} | {carrier_name} | {tracking_number}',
  xero_invoice_id: invoice_id
}

// ============================================================
// ERROR PATH (any step in dispatch chain)
// ============================================================
ON FAILURE AT step {step_name}:
  SUPABASE.INSERT dispatch_errors {
    order_id, order_number, failed_step: step_name,
    error_message: error.message,
    timestamp: ISO8601_timestamp
  }
  SLACK.POST #fulfilment-alerts {
    text: 'ERROR: Order {order_number} failed at {step_name}. Details logged.',
    timestamp: ISO8601_timestamp
  }
  STOP // do not proceed to subsequent steps
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation platform with native HTTP/webhook support. One workflow per agent: 'order-intake-agent' and 'fulfilment-dispatch-agent'. Both workflows share a single credential store for all API keys and OAuth tokens. Agent-to-agent handoff is implemented via an internal data pass or a lightweight queue entry rather than a direct chained call, so each agent can be monitored and restarted independently.
Webhook configuration
Shopify: configure the order.paid webhook in Shopify Admin > Settings > Notifications > Webhooks, pointing to the automation platform's inbound webhook URL for the order-intake-agent workflow. Webhook secret must be stored in the credential store and verified on every inbound request using HMAC-SHA256 signature validation against the X-Shopify-Hmac-Sha256 header. Reject any request that fails signature validation with a 401 response.
Templating approach
The Gmail shipping confirmation email uses a named template ('shipping_confirmation_v1') stored in the automation platform's template store. Template variables are: first_name, order_number, tracking_number, carrier_name. The template must be approved by the client in HTML and plain-text formats before end-to-end testing begins. Template updates do not require a workflow rebuild.
Error logging
Two Supabase tables are required before go-live. Table 'held_orders': columns order_id (text, primary key), order_number (text), short_skus (jsonb), held_at (timestamptz). Table 'dispatch_errors': columns id (uuid, auto), order_id (text), order_number (text), failed_step (text), error_message (text), timestamp (timestamptz). Any insert to either table also triggers a Slack POST to #fulfilment-alerts. FullSpec will configure a Supabase alert rule to notify support@gofullspec.com if the dispatch_errors table receives more than 3 inserts within any 60-minute window.
Testing approach
All integration testing runs in sandbox-first mode. Shopify: use a development store or draft orders with test payment gateway. Cin7: use the staging environment if available, otherwise test against live with known SKUs at confirmed stock levels. Xero: use the Xero demo company tenant for invoice creation tests. StarShipIt: use the StarShipIt sandbox environment (api.sandbox.starshipit.com) for courier booking tests. Gmail: send test emails to an internal inbox only during QA. Slack: post to a private '#automation-test' channel during development, switch to production channels only at go-live. All sandbox credentials are stored separately from production credentials in the credential store.
Estimated total build time
Order Intake Agent: 14 hours. Fulfilment Dispatch Agent: 22 hours. End-to-end integration testing and QA (sandbox and parallel run): 20 hours. Total: 56 hours across 4 to 5 delivery weeks.
Three items must be confirmed before the build can begin: (1) Cin7 API key provisioned and inventory records verified as accurate with the operations manager. (2) StarShipIt courier service rules, rate cards, and package weight defaults documented and signed off. (3) Shopify's native fulfilment notification emails reviewed and disabled if the Gmail confirmation step is the intended customer-facing notification. Delays in any of these items will push the build timeline. Raise any blockers to support@gofullspec.com immediately.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Order Processing & Fulfilment.

Launch Plan
Operations · Owner
View
ROI and Business Case
Finance · Owner
View
Process Runbook / SOP
Operations · Owner
View
Integration and API Spec
Technical · Developer
View
Test and QA Plan
Quality · Developer
View