Back to Inventory Reorder Management

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

Inventory Reorder Management

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

This document gives the FullSpec build team everything needed to construct, connect, and ship the Inventory Reorder Management automation. It covers the current-state process map with bottlenecks identified, full agent specifications including IO contracts, the end-to-end data flow, and the recommended build stack. The FullSpec team owns all build and integration work. The operations team at [YourCompany.com] is responsible for confirming reorder thresholds, supplying clean supplier contact data, and granting API access before build begins.

01Process snapshot

Step
Name
Description
1
Pull Stock Level Report
Operations Coordinator exports current on-hand quantities from Cin7 into a spreadsheet, usually at the start of each day or a few times per week. Time cost: 20 min/cycle.
2
Compare Levels Against Reorder Points
Coordinator manually compares each SKU's current stock against a static reorder threshold maintained in Google Sheets, highlighting rows below the trigger point. BOTTLENECK. Time cost: 35 min/cycle.
3
Identify Supplier for Each SKU
For each flagged SKU, the coordinator looks up the preferred supplier, lead time, and minimum order quantity from a separate reference sheet or contacts list. Time cost: 20 min/cycle.
4
Calculate Reorder Quantity
Coordinator manually calculates order quantity based on average sales velocity, current stock, lead time, and any seasonal adjustment they remember to apply. BOTTLENECK. Time cost: 25 min/cycle.
5
Draft Purchase Order
A purchase order is typed up manually in Cin7 or as a PDF and checked for correct SKU codes, quantities, and pricing. Time cost: 30 min/cycle.
6
Get Manager Approval
For orders above a value threshold, the coordinator forwards the draft PO to the operations manager via Gmail and waits for a reply before sending to the supplier. BOTTLENECK. Time cost: 40 min/cycle.
7
Send Purchase Order to Supplier
The approved PO is emailed to the supplier contact via Gmail as a PDF attachment or directly from Cin7. Time cost: 10 min/cycle.
8
Log PO in Tracking Sheet
Coordinator records the PO number, supplier, expected delivery date, and total value in a shared Google Sheets tracker. Time cost: 10 min/cycle.
9
Chase Supplier Acknowledgement
If no confirmation arrives within a day or two, the coordinator sends a follow-up email to the supplier via Gmail. Time cost: 15 min/cycle.
10
Create Bill in Accounting System
Once the PO is acknowledged, a corresponding bill is created in Xero against the supplier so the accounts team can track the liability. Time cost: 15 min/cycle.
Time cost summary: Total manual time per cycle is 220 minutes (3 hours 40 minutes). At the observed run frequency this maps to approximately 6 hours of manual work per week across the operations team. Steps 1, 2, 3, and 4 are replaced by the Reorder Calculation Agent. Steps 5, 7, 8, 9, and 10 are replaced by the PO Dispatch and Tracking Agent. Step 6 (manager approval) is retained as a human-in-the-loop step but is routed and resolved via Slack rather than untracked email chains.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Reorder Calculation Agent

Monitors Cin7 continuously by polling the inventory API at a configured interval. When a SKU's on-hand quantity meets or falls below its defined reorder point, the agent retrieves the SKU's average daily sales velocity and supplier lead time from Google Sheets, applies a safety stock buffer, and calculates the recommended order quantity. It then assembles a structured draft PO payload containing all fields required for Cin7 PO creation and passes this to the PO Dispatch and Tracking Agent. This agent replaces the four most time-intensive manual steps in the current process: the daily stock export, the spreadsheet threshold comparison, supplier lookup, and manual quantity calculation. Estimated build time: 10 hours. Complexity: Moderate.

Trigger
Cin7 poll detects on-hand quantity for any SKU at or below its configured reorder_point field. Poll interval: every 15 minutes during business hours; every 60 minutes outside business hours.
Tools
Cin7 (inventory read, draft PO create), Google Sheets (sales velocity and lead time reference data)
Replaces steps
Steps 1, 2, 3, 4
Estimated build
10 hours / Moderate
// Input
Cin7 webhook/poll payload: {
  sku_id: string,
  sku_name: string,
  on_hand_qty: number,
  reorder_point: number,
  unit_cost: number,
  default_supplier_id: string
}

Google Sheets lookup row: {
  sku_id: string,
  avg_daily_sales: number,
  supplier_lead_time_days: number,
  safety_stock_days: number,
  min_order_qty: number,
  supplier_email: string,
  supplier_name: string
}

// Output
Draft PO payload: {
  sku_id: string,
  sku_name: string,
  supplier_id: string,
  supplier_name: string,
  supplier_email: string,
  reorder_qty: number,
  unit_cost: number,
  total_value: number,
  expected_delivery_date: ISO8601 date,
  calculation_basis: {
    avg_daily_sales: number,
    lead_time_days: number,
    safety_stock_days: number,
    on_hand_at_trigger: number
  },
  cin7_draft_po_id: string
}
  • Cin7 API tier requirement: the Cin7 Core (formerly DEAR) plan must include API access. Confirm the active subscription tier before build begins. The polling endpoint is GET /v1/ref/product with quantity fields; PO creation uses POST /v1/purchase.
  • Google Sheets reference tab must be named 'SKU_Reference' with columns in the exact order: sku_id, avg_daily_sales, supplier_lead_time_days, safety_stock_days, min_order_qty, supplier_email, supplier_name. Column headers are case-sensitive for the lookup mapping.
  • Reorder quantity formula: reorder_qty = MAX(min_order_qty, CEIL((avg_daily_sales * (lead_time_days + safety_stock_days)) - on_hand_qty)). If the result is zero or negative after recalculation, the agent must skip the SKU and log a 'no_action' record rather than raising a zero-quantity PO.
  • Dedupe behaviour: if a draft PO for the same sku_id already exists in Cin7 with status 'Draft' or 'Backordered', the agent must skip creation and log a 'duplicate_skipped' event. Check via GET /v1/purchase?productID={sku_id}&status=Draft before posting.
  • Fallback: if the Google Sheets lookup returns no row for a sku_id, the agent must route to an error branch, post a Slack alert to the operations channel with the missing sku_id, and halt further processing for that SKU only.
  • The Cin7 API key must be stored in the shared credential store and never hardcoded in workflow nodes. Credential name: 'Cin7_API_Prod'.
  • Confirm with the operations manager that all SKUs in scope have the reorder_point field populated in Cin7 before go-live. Null or zero values will suppress triggers and cause missed reorders.
PO Dispatch and Tracking Agent

Receives the completed draft PO payload from the Reorder Calculation Agent and orchestrates the full dispatch, approval, notification, and tracking sequence. It evaluates the total PO value against a configurable approval threshold. Orders below the threshold proceed directly to supplier dispatch. Orders at or above the threshold trigger a Slack approval message to the operations manager with inline approve and reject actions. Once approved (or auto-approved), the agent sends the PO to the supplier via Gmail, creates a draft bill in Xero, appends a row to the Google Sheets PO tracker, and schedules a 24-hour chaser email if no supplier acknowledgement is detected. This agent replaces five manual steps and converts the manager approval from an untracked email chain into a structured, auditable Slack workflow. Estimated build time: 18 hours. Complexity: Complex.

Trigger
Fires when the Reorder Calculation Agent outputs a completed draft PO payload containing a valid cin7_draft_po_id.
Tools
Cin7 (PO status update), Slack (approval request and operations alerts), Gmail (PO email to supplier, chaser email), Xero (draft bill creation), Google Sheets (PO tracker append)
Replaces steps
Steps 5, 7, 8, 9, 10
Estimated build
18 hours / Complex
// Input
Draft PO payload from Reorder Calculation Agent: {
  sku_id, sku_name, supplier_id, supplier_name,
  supplier_email, reorder_qty, unit_cost,
  total_value, expected_delivery_date,
  cin7_draft_po_id
}

// Decision branch
IF total_value >= approval_threshold:
  -> POST Slack message to #ops-approvals channel
     Slack payload: {
       blocks: [section, actions],
       actions: [{ action_id: 'approve_po', text: 'Approve' },
                 { action_id: 'reject_po',  text: 'Reject'  }],
       metadata: { cin7_draft_po_id, total_value, sku_name }
     }
  -> Wait for Slack interactive callback (timeout: 48 hrs)
  -> On timeout: escalate to ops manager via direct Slack message

// On approval (or auto-approve for sub-threshold orders)
Cin7: PATCH /v1/purchase/{cin7_draft_po_id} -> status: 'Approved'
Gmail: Send PO email {
  to: supplier_email,
  subject: 'Purchase Order {cin7_draft_po_id} - {sku_name}',
  body: template 'po_dispatch_template',
  attachment: Cin7 PO PDF (GET /v1/purchase/{cin7_draft_po_id}/pdf)
}
Xero: POST /api.xro/2.0/PurchaseOrders -> Draft bill {
  Contact: { supplier_name, supplier_id (Xero ContactID) },
  LineItems: [{ Description: sku_name, Quantity: reorder_qty,
                UnitAmount: unit_cost, AccountCode: '310' }],
  Date: today, DueDate: expected_delivery_date,
  Reference: cin7_draft_po_id
}
Google Sheets: APPEND row to 'PO_Tracker' tab {
  po_id, sku_id, sku_name, supplier_name, reorder_qty,
  total_value, expected_delivery_date, dispatch_timestamp,
  approval_status, xero_bill_id, ack_received: false
}

// Chaser logic (24-hour timer node)
IF no supplier reply detected after 24 hrs:
  Gmail: Send chaser {
    to: supplier_email,
    subject: 'Acknowledgement required: PO {cin7_draft_po_id}',
    body: template 'po_chaser_template'
  }
  Slack: POST alert to #ops-approvals {
    text: 'No acknowledgement received for PO {cin7_draft_po_id}
           ({sku_name}). Chaser sent to {supplier_email}.'
  }
  Google Sheets: UPDATE row -> chaser_sent: true, chaser_timestamp: now

// Output
Final tracker row state: {
  po_id, sku_id, sku_name, supplier_name, reorder_qty,
  total_value, expected_delivery_date, dispatch_timestamp,
  approval_status: 'approved' | 'auto-approved' | 'rejected',
  xero_bill_id: string,
  ack_received: boolean,
  chaser_sent: boolean
}
  • Approval threshold value must be agreed with the operations manager before build and stored as a configurable environment variable (ENV: PO_APPROVAL_THRESHOLD_USD). Do not hardcode this value in workflow nodes.
  • Slack app must be installed to the workspace with scopes: chat:write, chat:write.public, im:write, and incoming-webhook. The interactive components endpoint must be configured in the Slack app manifest to point to the automation platform's webhook receiver URL.
  • Gmail OAuth2 credentials must use the operations coordinator's Google Workspace account (or a dedicated service account with domain-wide delegation). Scopes required: https://www.googleapis.com/auth/gmail.send. Credential name in shared store: 'Gmail_OpsCoordinator_OAuth'.
  • Xero connection requires OAuth2 with scopes: openid, profile, email, accounting.transactions, accounting.contacts.read. The Xero organisation must have the supplier already set up as a Contact before the bill creation step can map the ContactID. Build a lookup step: GET /api.xro/2.0/Contacts?where=Name='{supplier_name}' to resolve ContactID dynamically.
  • Xero account code '310' is the default purchases account. Confirm this code is valid in the connected Xero organisation before go-live. Make it configurable via environment variable (ENV: XERO_PURCHASES_ACCOUNT_CODE).
  • The 24-hour chaser timer must use the automation platform's native delay or schedule node, not a polling loop. If the platform supports it, store the scheduled chaser job ID in the Google Sheets tracker row so it can be cancelled if manual acknowledgement is logged.
  • Rejection handling: if the operations manager rejects a PO in Slack, the workflow must update the Cin7 draft PO status to 'Voided', post a Slack notification to the operations channel with the rejection reason (captured from the Slack modal), and append a 'rejected' status row to the PO tracker. No email is sent to the supplier.
  • Google Sheets tracker tab must be named exactly 'PO_Tracker'. The sheet ID must be stored as an environment variable (ENV: GSHEETS_PO_TRACKER_ID). Column order: po_id, sku_id, sku_name, supplier_name, reorder_qty, total_value, expected_delivery_date, dispatch_timestamp, approval_status, xero_bill_id, ack_received, chaser_sent, chaser_timestamp.
  • Confirm that all active suppliers have a valid email address in the Google Sheets SKU_Reference tab before go-live. A missing supplier_email must trigger the error branch and Slack alert rather than a failed Gmail send.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical

03End-to-end data flow

Full trigger-to-output data flow: Inventory Reorder Management automation
// ─────────────────────────────────────────────────────────────────
// TRIGGER: Cin7 poll detects SKU at or below reorder_point
// ─────────────────────────────────────────────────────────────────
Cin7.poll() -> GET /v1/ref/product
  Filter: on_hand_qty <= reorder_point
  Emit per matching SKU: {
    sku_id, sku_name, on_hand_qty,
    reorder_point, unit_cost, default_supplier_id
  }

// ─────────────────────────────────────────────────────────────────
// DEDUPE CHECK: skip if active draft PO already exists
// ─────────────────────────────────────────────────────────────────
Cin7.GET /v1/purchase?productID={sku_id}&status=Draft
  IF existing draft found:
    -> log 'duplicate_skipped' to error log table
    -> HALT branch for this sku_id
  ELSE:
    -> continue to AGENT HANDOFF 1

// ═════════════════════════════════════════════════════════════════
// AGENT HANDOFF 1: Trigger -> Reorder Calculation Agent
// Fields passed: sku_id, sku_name, on_hand_qty, unit_cost,
//                default_supplier_id
// ═════════════════════════════════════════════════════════════════

// ─────────────────────────────────────────────────────────────────
// REORDER CALCULATION AGENT: lookup + quantity calculation
// ─────────────────────────────────────────────────────────────────
GoogleSheets.lookup('SKU_Reference', sku_id)
  Returns: {
    avg_daily_sales, supplier_lead_time_days,
    safety_stock_days, min_order_qty,
    supplier_email, supplier_name
  }
  IF no row found for sku_id:
    -> Slack.alert('#ops-approvals', 'Missing SKU reference: {sku_id}')
    -> log error, HALT branch

reorder_qty = MAX(
  min_order_qty,
  CEIL((avg_daily_sales * (supplier_lead_time_days + safety_stock_days))
       - on_hand_qty)
)
  IF reorder_qty <= 0:
    -> log 'no_action', HALT branch

expected_delivery_date = TODAY + supplier_lead_time_days (ISO8601)
total_value = reorder_qty * unit_cost

Cin7.POST /v1/purchase -> create draft PO {
  supplier_id, sku_id, reorder_qty,
  unit_cost, expected_delivery_date
}
  Returns: cin7_draft_po_id

// ═════════════════════════════════════════════════════════════════
// AGENT HANDOFF 2: Reorder Calculation Agent -> PO Dispatch Agent
// Fields passed: sku_id, sku_name, supplier_id, supplier_name,
//   supplier_email, reorder_qty, unit_cost, total_value,
//   expected_delivery_date, cin7_draft_po_id
// ═════════════════════════════════════════════════════════════════

// ─────────────────────────────────────────────────────────────────
// PO DISPATCH AND TRACKING AGENT: approval gate
// ─────────────────────────────────────────────────────────────────
IF total_value >= ENV.PO_APPROVAL_THRESHOLD_USD:
  Slack.POST('#ops-approvals') -> approval message {
    sku_name, supplier_name, reorder_qty,
    total_value, expected_delivery_date,
    actions: [approve_po, reject_po],
    metadata: { cin7_draft_po_id }
  }
  WAIT for Slack interactive callback (timeout: 48 hrs)
    ON reject_po:
      Cin7.PATCH /v1/purchase/{cin7_draft_po_id} -> status: 'Voided'
      GoogleSheets.APPEND 'PO_Tracker' -> approval_status: 'rejected'
      Slack.POST('#ops-approvals') -> rejection notification
      -> HALT
    ON timeout:
      Slack.DM(ops_manager_user_id) -> escalation message
      -> HALT until response received
ELSE:
  approval_status = 'auto-approved'
  -> continue

// ─────────────────────────────────────────────────────────────────
// PO DISPATCH: Cin7 approve, Gmail send, Xero bill, Sheets log
// ─────────────────────────────────────────────────────────────────
Cin7.PATCH /v1/purchase/{cin7_draft_po_id} -> status: 'Approved'

Cin7.GET /v1/purchase/{cin7_draft_po_id}/pdf -> po_pdf_bytes

Gmail.send({
  to: supplier_email,
  subject: 'Purchase Order {cin7_draft_po_id} - {sku_name}',
  body: template('po_dispatch_template', { po fields }),
  attachment: { filename: 'PO_{cin7_draft_po_id}.pdf', content: po_pdf_bytes }
})
  Records: dispatch_timestamp = now()

Xero.GET /api.xro/2.0/Contacts?where=Name='{supplier_name}'
  Returns: xero_contact_id
Xero.POST /api.xro/2.0/PurchaseOrders {
  Contact: { ContactID: xero_contact_id },
  LineItems: [{ Description: sku_name, Quantity: reorder_qty,
                UnitAmount: unit_cost,
                AccountCode: ENV.XERO_PURCHASES_ACCOUNT_CODE }],
  Date: today, DueDate: expected_delivery_date,
  Reference: cin7_draft_po_id,
  Status: 'DRAFT'
}
  Returns: xero_bill_id

GoogleSheets.APPEND('PO_Tracker', {
  po_id: cin7_draft_po_id, sku_id, sku_name,
  supplier_name, reorder_qty, total_value,
  expected_delivery_date, dispatch_timestamp,
  approval_status, xero_bill_id,
  ack_received: false, chaser_sent: false
})

// ─────────────────────────────────────────────────────────────────
// CHASER TIMER: 24-hour delayed node
// ─────────────────────────────────────────────────────────────────
Schedule.delay(24hrs) ->
  IF ack_received == false (read from PO_Tracker row):
    Gmail.send({
      to: supplier_email,
      subject: 'Acknowledgement required: PO {cin7_draft_po_id}',
      body: template('po_chaser_template', { po fields })
    })
    Slack.POST('#ops-approvals') {
      text: 'No acknowledgement for PO {cin7_draft_po_id}
             ({sku_name}). Chaser sent to {supplier_email}.'
    }
    GoogleSheets.UPDATE('PO_Tracker', po_id) {
      chaser_sent: true, chaser_timestamp: now()
    }

// ─────────────────────────────────────────────────────────────────
// FINAL STATE: PO_Tracker row (terminal)
// ─────────────────────────────────────────────────────────────────
PO_Tracker row: {
  po_id, sku_id, sku_name, supplier_name, reorder_qty,
  total_value, expected_delivery_date, dispatch_timestamp,
  approval_status: 'approved' | 'auto-approved' | 'rejected',
  xero_bill_id, ack_received, chaser_sent, chaser_timestamp
}
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation tool (platform TBD at build start). Structure as two discrete workflows: one per agent. Both workflows share a single credential store so that API keys and OAuth tokens are managed centrally. No credentials are stored inside individual workflow nodes.
Cin7 integration
REST API v1 over HTTPS. Authentication: API key passed as X-AUTH-APPLICATION header. Polling workflow runs every 15 minutes during business hours (06:00 to 20:00 local) and every 60 minutes outside those hours. Use a schedule-trigger node with two cron expressions, not a continuous loop. PO creation and status updates use POST and PATCH to /v1/purchase respectively. Credential name: Cin7_API_Prod.
Google Sheets integration
Sheets API v4 via OAuth2 service account (domain-wide delegation preferred for [YourCompany.com] Google Workspace). Read operations target the 'SKU_Reference' tab; append and update operations target the 'PO_Tracker' tab. Both tab names and the spreadsheet ID (ENV: GSHEETS_PO_TRACKER_ID) must be stored as environment variables. Credential name: GSheets_ServiceAccount.
Slack integration
Slack app with Bot Token scopes: chat:write, chat:write.public, im:write. Interactive components (approve/reject buttons) require the automation platform's webhook URL to be registered as the Interactivity Request URL in the Slack app manifest. The ops-approvals channel ID and the operations manager's Slack user ID must be stored as environment variables (ENV: SLACK_OPS_CHANNEL_ID, ENV: SLACK_OPS_MANAGER_USER_ID). Credential name: Slack_BotToken.
Gmail integration
Gmail API via OAuth2. Scopes: https://www.googleapis.com/auth/gmail.send. Use the operations coordinator's Google Workspace account or a dedicated shared mailbox. PO PDF attachment is retrieved from the Cin7 API as a binary stream and passed directly to the Gmail send node as base64-encoded content. Credential name: Gmail_OpsCoordinator_OAuth.
Xero integration
Xero Accounting API v2 via OAuth2 (PKCE flow, 30-minute token with automatic refresh). Scopes: openid, profile, email, accounting.transactions, accounting.contacts.read. Rate limit: 60 calls/minute per connection; build retry logic with exponential backoff (3 retries, 5s/15s/45s delays) on 429 responses. Xero purchases account code stored as ENV: XERO_PURCHASES_ACCOUNT_CODE. Credential name: Xero_OAuth2_Prod.
Webhook configuration
The Slack interactive components endpoint must be a stable HTTPS URL exposed by the automation platform. If the platform uses ephemeral webhook URLs, provision a static reverse-proxy endpoint (e.g. a lightweight cloud function) to relay Slack callbacks to the active workflow instance. All inbound webhook URLs must require signature verification using the Slack signing secret (ENV: SLACK_SIGNING_SECRET).
Templating approach
Email bodies for the PO dispatch and chaser emails are stored as named templates within the automation platform's template store (names: po_dispatch_template, po_chaser_template). Templates use variable substitution for: sku_name, cin7_draft_po_id, supplier_name, reorder_qty, total_value, expected_delivery_date. Templates must be reviewed and approved by the operations manager before go-live.
Error logging
All errors, skipped branches (duplicate_skipped, no_action), and fallback events are written to a Supabase table named 'automation_error_log' with columns: id (uuid), workflow_name (text), event_type (text), sku_id (text), po_id (text), error_message (text), created_at (timestamptz). A Supabase row-insert trigger monitors this table and fires a Slack alert to #ops-approvals whenever event_type is not 'no_action' or 'duplicate_skipped'. Connection string stored as ENV: SUPABASE_LOG_URL and ENV: SUPABASE_LOG_KEY.
Testing approach
All integrations are first validated against sandbox/staging credentials: Cin7 demo environment, Xero Demo Company, a dedicated Google Sheet (non-production), and a private Slack test channel (#automation-qa). No live supplier emails are sent during testing. Gmail send node is replaced with a logging-only node in the staging workflow. Full end-to-end staging tests must pass before production credentials are swapped in.
Approval threshold config
PO approval threshold stored as environment variable ENV: PO_APPROVAL_THRESHOLD_USD. Initial value to be confirmed with the operations manager before build. Must be updateable without redeploying the workflow (i.e. read at runtime from the environment variable store, not hardcoded in a node).
Estimated total build time
Reorder Calculation Agent: 10 hours. PO Dispatch and Tracking Agent: 18 hours. End-to-end integration testing and QA: 10 hours. Total: 38 hours, aligning with the confirmed build effort for the Standard build tier.
Pre-build blockers to resolve before a single workflow node is created: (1) Cin7 API key issued and tier confirmed as API-enabled. (2) All SKUs in scope have reorder_point populated in Cin7 (not null, not zero). (3) Google Sheets SKU_Reference tab is complete with valid supplier emails and lead times for all active suppliers. (4) Operations manager has confirmed the PO approval threshold value. (5) Slack app is installed to the workspace and the interactivity endpoint URL is registered. (6) Xero OAuth2 app is created in the Xero developer portal and all active suppliers are set up as Xero Contacts. Contact support@gofullspec.com to confirm these items are in place before the build start date.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Inventory Reorder Management.

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