Back to Fulfilment Performance Reporting

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

Fulfilment Performance Reporting

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

This document is the complete technical reference for the FullSpec team building the Fulfilment Performance Reporting automation. It covers the current-state process map with identified bottlenecks, full agent specifications with IO contracts, the end-to-end data flow trace, and the recommended build stack with total effort estimates. Everything the FullSpec team needs to begin and complete the build is contained here. Nothing in this document requires action from the process owner unless a credential or configuration item is explicitly flagged.

01Process snapshot

Step
Name
Description
1
Identify Reporting Period and Scope
Ops Manager confirms the date range and which warehouses or fulfilment channels are in scope for this cycle. Time cost: 10 min.
2
Export Order Data from Shopify
A CSV of all orders placed in the period is exported from Shopify, including order status, fulfilment date, and SKU breakdown. Time cost: 20 min.
3
Export Shipping Data from ShipStation
A separate shipment report is exported from ShipStation covering dispatch dates, carrier, and delivery confirmations for the same period. Time cost: 20 min.
4
Export Returns and Credits from Xero
Credit notes and returns logged in Xero during the period are exported so returns volume and value can be included in the report. Time cost: 15 min.
5
Paste and Align Data in Master Spreadsheet
BOTTLENECK: All three exports are pasted into Google Sheets, columns are manually aligned, and mismatched or duplicate rows are corrected. Time cost: 45 min.
6
Calculate KPIs
BOTTLENECK: On-time dispatch rate, fill rate, average dispatch lead time, and returns rate are calculated using spreadsheet formulas that must be checked if any source format has changed. Time cost: 30 min.
7
Write Variance Commentary
The ops manager writes a short narrative explaining any KPIs outside target, what caused the variance, and what action is being taken. Time cost: 25 min.
8
Format and Export the Report
The finished spreadsheet is formatted into a readable layout and exported as a PDF or copied into an email template. Time cost: 20 min.
9
Distribute Report via Email and Slack
The report is emailed to the leadership team and a summary message is posted manually in the operations Slack channel. Time cost: 10 min.
10
File Report and Update Historical Log
The PDF and the source spreadsheet are saved to the correct folder and a row is added to the historical performance log manually. Time cost: 10 min.
Time cost summary: Total manual time per cycle is 205 minutes (approximately 3 hours 25 minutes). At a weekly reporting cadence this equals approximately 5 hours/week and 250 hours/year. Steps 1 through 10 are all manual today. The automation replaces steps 1 to 9 with three agents, retaining only a 5-minute human review of the AI-drafted commentary (formerly step 7) before distribution fires. Step 10 (filing and log update) is absorbed into the Report Distribution Agent as an automated append.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Data Collection Agent

Connects to Shopify, ShipStation, and Xero on the configured schedule and retrieves all order, shipment, and returns data for the closed reporting period without any manual exports. The agent fires from a time-based trigger, executes three sequential API calls (one per platform), normalises the field names returned by each source to a canonical schema, deduplicates on order_id and shipment_id, and writes the cleaned result set to the designated Google Sheet tab. If any single API call fails or returns zero records, the agent halts and raises an error alert before writing anything to the sheet, preventing a partial dataset from reaching the KPI calculation step.

Trigger
Scheduled time-based trigger firing at the configured reporting interval (default: Monday 07:00 local time). Cron expression: 0 7 * * 1.
Tools
Shopify (Orders API), ShipStation (Shipments API), Xero (Credit Notes and Invoices API), Google Sheets (write)
Replaces steps
Steps 1, 2, 3, 4, 5
Estimated build
8 hours, Moderate complexity
// Input
reporting_period_start: ISO 8601 date string (e.g. '2024-06-10')
reporting_period_end:   ISO 8601 date string (e.g. '2024-06-16')
warehouse_scope:        string[] | 'all'  // channel filter

// Shopify Orders API call
GET /admin/api/2024-04/orders.json
  ?status=any&created_at_min={start}&created_at_max={end}&limit=250
  -> fields: order_id, created_at, fulfillment_status, fulfilled_at, line_items[sku, quantity]

// ShipStation Shipments API call
GET /shipments?createDateStart={start}&createDateEnd={end}&pageSize=500
  -> fields: shipmentId, orderId, shipDate, carrierCode, trackingNumber, shipmentStatusCode

// Xero Credit Notes API call
GET /api.xro/2.0/CreditNotes?where=Date>={start}&&Date<={end}&Type=ACCREC
  -> fields: creditNoteId, date, reference, total, currencyCode, lineItems[itemCode, quantity, unitAmount]

// Output
google_sheet_tab:       'raw_data_{YYYY-WW}'  // new tab per reporting period
columns_written:        [order_id, order_created_at, fulfillment_status, fulfilled_at,
                         sku, order_quantity, shipment_id, ship_date, carrier_code,
                         tracking_number, shipment_status, credit_note_id,
                         return_date, return_reference, return_total, return_qty]
row_count:              integer  // passed to next agent as confirmation
status:                 'success' | 'partial_failure' | 'error'
error_detail:           string | null
  • Shopify requires a private app or custom app with the read_orders OAuth scope. The store must be on the Basic plan or above for API access. Confirm scope grant before build starts.
  • ShipStation requires an API key and API secret generated from the account settings panel. Rate limit is 40 requests/minute; the agent must respect this with a 1.5-second inter-request delay when paginating large result sets (800+ orders/month at peak).
  • Xero requires an OAuth 2.0 connected app with the accounting.transactions.read scope. Tokens expire after 30 minutes; the agent must implement the refresh_token flow using the offline_access scope to avoid session expiry mid-run.
  • Google Sheets write uses a service account with the sheets.googleapis.com API enabled. The service account email must be granted Editor access to the master spreadsheet before the first run.
  • Dedupe logic: order_id is the primary deduplication key for Shopify rows. shipmentId is the key for ShipStation rows. creditNoteId is the key for Xero rows. On each run, the agent checks the existing raw_data tab for the period and skips rows whose key already exists.
  • If row_count returns zero for any source, the agent must not proceed and must fire an error to the logging table and a Slack alert to the configured ops channel before halting.
  • The Google Sheet tab naming convention must be raw_data_{YYYY-WW} for weekly cycles and raw_data_{YYYY-MM} for monthly cycles. The reporting cadence is set at configuration time via an environment variable REPORTING_CADENCE ('weekly' or 'monthly').
  • Confirm with the process owner before build: does the Xero account use multi-currency, and if so should return_total always be normalised to USD?
KPI and Commentary Agent

Fires immediately after the Data Collection Agent confirms the Google Sheet tab is populated with a non-zero row count and a status of 'success'. The agent reads the raw_data tab for the current period, calculates the four core KPIs (on-time dispatch rate, fill rate, average dispatch lead time, and returns rate) using pre-defined calculation logic written into the sheet's kpi_calc tab, then compares each result against the configured target thresholds. Any KPI outside threshold is flagged. An AI language model step then receives the KPI results and threshold breach flags and produces a plain-English variance commentary paragraph. The completed KPI table and commentary are written to the kpi_output tab. The agent then pauses at the human review gate: it sends the draft commentary to the ops manager via a review notification (Slack DM or email) and waits for an approval signal before passing control to the Report Distribution Agent. If no approval is received within 2 hours, the agent re-notifies once, then escalates to the configured fallback reviewer.

Trigger
Webhook or internal event from Data Collection Agent: payload contains sheet_tab_name and row_count > 0 and status = 'success'.
Tools
Google Sheets (read and write), AI language model (commentary drafting via API call)
Replaces steps
Steps 6, 7, 8
Estimated build
7 hours, Moderate complexity
// Input
sheet_tab_name:         string  // e.g. 'raw_data_2024-24'
row_count:              integer // from Data Collection Agent
reporting_period_start: ISO 8601 date string
reporting_period_end:   ISO 8601 date string

// KPI calculation inputs (read from raw_data tab)
total_orders:           integer
fulfilled_on_time:      integer  // fulfilled_at <= promised_dispatch_date
total_units_ordered:    integer
total_units_shipped:    integer
dispatch_lead_times:    integer[]  // days from order_created_at to ship_date
total_return_value:     float
total_order_value:      float

// KPI targets (from environment config)
target_on_time_dispatch_pct:  float  // e.g. 0.95
target_fill_rate_pct:         float  // e.g. 0.98
target_avg_lead_time_days:    float  // e.g. 2.0
target_returns_rate_pct:      float  // e.g. 0.03

// Output (written to kpi_output tab and passed to next agent)
on_time_dispatch_rate:  float  // e.g. 0.921
fill_rate:              float  // e.g. 0.984
avg_dispatch_lead_time: float  // days
returns_rate:           float  // e.g. 0.027
breached_kpis:          string[]  // KPI names outside threshold
commentary_draft:       string  // plain-English paragraph from AI step
review_status:          'pending' | 'approved' | 'edited_and_approved'

// On approval
approved_commentary:    string  // final text after ops manager review
approved_by:            string  // reviewer name or email
approved_at:            ISO 8601 datetime
  • The KPI calculation logic must be defined as named ranges in a kpi_calc tab in the master spreadsheet. The automation platform reads cell values; it does not write formulas. This prevents formula breakage when source column layouts change.
  • The AI commentary step must receive KPI values, targets, breach flags, and the previous period's KPI values for comparison. The prior-period row must be read from the historical_log tab. If no prior row exists (first run), the comparison clause in the prompt is omitted.
  • The language model call must use a structured system prompt stored as an environment variable (COMMENTARY_SYSTEM_PROMPT). The prompt must instruct the model to: (a) state which KPIs are inside target, (b) explain each breach concisely, (c) avoid speculation about causes not present in the data, and (d) keep the output under 150 words.
  • The human review gate must send a Slack DM to the configured REVIEWER_SLACK_USER_ID containing the KPI table and draft commentary, with Approve and Edit buttons implemented as interactive Slack message actions. The approval webhook callback URL must be registered in the Slack app manifest.
  • If the reviewer clicks Edit, the agent must accept the edited text from the Slack modal submission payload and store it as edited_commentary before passing approved_commentary to the next agent.
  • Review timeout: 2 hours after initial notification. One re-notification at the 2-hour mark. After 4 hours total with no response, escalate to FALLBACK_REVIEWER_EMAIL via Gmail with the draft attached as plain text.
  • Confirm KPI targets and thresholds with the ops manager before build. These must be stored in a named config tab in the spreadsheet (config.kpi_targets) and also mirrored as environment variables so they can be updated without a code deploy.
  • The kpi_output tab must be appended, not overwritten, on each run so that all historical KPI rows remain accessible.
Report Distribution Agent

Fires after the KPI and Commentary Agent emits an approved_commentary value and review_status of 'approved' or 'edited_and_approved'. The agent formats the KPI table and commentary into a structured Slack message and posts it to the configured operations channel. It then triggers a PDF export of the kpi_output tab via the Google Sheets API, attaches the PDF to a Gmail message, and sends it to all addresses in the REPORT_RECIPIENTS list. Finally, it appends a summary row to the historical_log tab, recording the period dates, all four KPI values, breach flags, the approved commentary, and a timestamp. No human action is required at this stage.

Trigger
Internal event from KPI and Commentary Agent: payload contains approved_commentary and review_status in ('approved', 'edited_and_approved').
Tools
Slack (post message to channel), Gmail (send with attachment), Google Sheets (PDF export and historical log append)
Replaces steps
Steps 9, 10
Estimated build
5 hours, Simple complexity
// Input
on_time_dispatch_rate:  float
fill_rate:              float
avg_dispatch_lead_time: float
returns_rate:           float
breached_kpis:          string[]
approved_commentary:    string
approved_by:            string
approved_at:            ISO 8601 datetime
reporting_period_start: ISO 8601 date string
reporting_period_end:   ISO 8601 date string
sheet_tab_name:         string

// Slack output
slack_channel:          string  // from env SLACK_OPS_CHANNEL_ID
slack_message_format:   'blocks'  // Slack Block Kit JSON
slack_post_ts:          string  // returned timestamp from Slack API

// Gmail output
pdf_export_url:         string  // Google Sheets export URL with format=pdf
email_recipients:       string[]  // from env REPORT_RECIPIENTS
email_subject:          'Fulfilment KPI Report: {period_start} to {period_end}'
email_body:             string  // includes KPI table as plain text + commentary
gmail_message_id:       string  // returned by Gmail API

// Historical log append (historical_log tab)
log_columns_written:    [log_date, period_start, period_end, on_time_dispatch_rate,
                         fill_rate, avg_dispatch_lead_time, returns_rate,
                         breached_kpis, approved_commentary, approved_by,
                         slack_post_ts, gmail_message_id]

// Output
distribution_status:    'success' | 'partial_failure' | 'error'
error_detail:           string | null
  • Slack requires a Bot Token (xoxb-) with the chat:write and chat:write.public OAuth scopes. The bot must be invited to the target channel before the first run. Store the token as SLACK_BOT_TOKEN environment variable.
  • The Slack message must use Block Kit with a header block (period label), a section block per KPI (value vs target, with a red circle emoji for breached KPIs and a green circle for KPIs inside target), and a context block containing the approved commentary.
  • Gmail send uses a service account with domain-wide delegation, or alternatively an OAuth 2.0 credential for the sending Gmail address. The gmail.send scope is required. The PDF is attached using a base64-encoded MIME multipart message.
  • PDF export is triggered via the Google Sheets export URL pattern: https://docs.google.com/spreadsheets/d/{SHEET_ID}/export?format=pdf&gid={SHEET_GID}&portrait=true. The GID of the kpi_output tab must be stored as an environment variable PDF_EXPORT_GID.
  • The REPORT_RECIPIENTS list is stored as a comma-separated environment variable. It must be confirmed with the process owner before go-live as it may differ between weekly and monthly cycles. Consider using separate env vars REPORT_RECIPIENTS_WEEKLY and REPORT_RECIPIENTS_MONTHLY.
  • If Slack post fails, the agent must continue to Gmail send and log the Slack failure in the error log. Distribution must not be aborted because of a single channel failure.
  • The historical_log tab must never be cleared between runs. The append must use the Sheets API values.append method with insertDataOption=INSERT_ROWS to avoid overwriting existing rows.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical

03End-to-end data flow

Full trigger-to-output data flow for Fulfilment Performance Reporting automation
// ============================================================
// TRIGGER
// ============================================================
SCHEDULE_TRIGGER fires at cron '0 7 * * 1' (Monday 07:00)
  -> emits: { reporting_period_start, reporting_period_end, warehouse_scope }
  -> derived from: REPORTING_CADENCE env var + current date at trigger time

// ============================================================
// AGENT 1: Data Collection Agent
// ============================================================

// Step 1: Shopify Orders API
GET /admin/api/2024-04/orders.json
  params: { status: 'any', created_at_min: reporting_period_start,
            created_at_max: reporting_period_end, limit: 250 }
  headers: { X-Shopify-Access-Token: SHOPIFY_ACCESS_TOKEN }
  -> raw fields: id (->order_id), created_at (->order_created_at),
                 fulfillment_status, fulfilled_at,
                 line_items[sku, quantity (->order_quantity)]
  -> paginate using Link header until no 'next' rel present

// Step 2: ShipStation Shipments API
GET /shipments
  params: { createDateStart: reporting_period_start,
            createDateEnd: reporting_period_end, pageSize: 500, page: 1 }
  headers: { Authorization: Basic base64(SS_API_KEY:SS_API_SECRET) }
  -> raw fields: shipmentId, orderId, shipDate (->ship_date),
                 carrierCode (->carrier_code), trackingNumber (->tracking_number),
                 shipmentStatus (->shipment_status)
  -> paginate using pages field in response until page == pages

// Step 3: Xero Credit Notes API
GET /api.xro/2.0/CreditNotes
  params: { where: 'Date>=DateTime(Y,M,D)&&Date<=DateTime(Y,M,D)&&Type="ACCREC"' }
  headers: { Authorization: Bearer XERO_ACCESS_TOKEN }
  -> on 401: refresh token via POST /identity/connect/token
  -> raw fields: CreditNoteID (->credit_note_id), Date (->return_date),
                 Reference (->return_reference), Total (->return_total),
                 CurrencyCode, LineItems[ItemCode (->item_code),
                 Quantity (->return_qty), UnitAmount]

// Step 4: Deduplicate and normalise
  merge_key_shopify:    order_id
  merge_key_shipstation: shipmentId
  merge_key_xero:        credit_note_id
  join: Shopify.order_id LEFT JOIN ShipStation.orderId
        -> matched rows carry both order and shipment fields
  append: Xero credit note rows independently (keyed by return_date and credit_note_id)

// Step 5: Write to Google Sheets
Sheets API: spreadsheets.values.update
  range: '{raw_data_YYYY-WW}!A1'
  valueInputOption: 'RAW'
  body: { values: [[order_id, order_created_at, fulfillment_status, fulfilled_at,
                    sku, order_quantity, shipment_id, ship_date, carrier_code,
                    tracking_number, shipment_status, credit_note_id,
                    return_date, return_reference, return_total, return_qty]] }
  -> emits to Agent 2: { sheet_tab_name, row_count, status: 'success' }

// -- AGENT 1 -> AGENT 2 HANDOFF --
// Payload: { sheet_tab_name, row_count, reporting_period_start,
//            reporting_period_end, status }

// ============================================================
// AGENT 2: KPI and Commentary Agent
// ============================================================

// Step 6: Read raw_data tab and kpi_calc tab
Sheets API: spreadsheets.values.get
  ranges: ['{raw_data_YYYY-WW}!A:P', 'kpi_calc!A:Z', 'config.kpi_targets!A:B',
           'historical_log!A:N']  // last row for prior-period comparison
  -> derive:
       total_orders          = COUNT(DISTINCT order_id)
       fulfilled_on_time     = COUNT WHERE fulfilled_at <= promised_dispatch_date
       on_time_dispatch_rate = fulfilled_on_time / total_orders
       total_units_ordered   = SUM(order_quantity)
       total_units_shipped   = SUM(order_quantity WHERE shipment_status='shipped')
       fill_rate             = total_units_shipped / total_units_ordered
       dispatch_lead_times   = [ship_date - order_created_at] per order (days)
       avg_dispatch_lead_time= MEAN(dispatch_lead_times)
       total_return_value    = SUM(return_total)
       total_order_value     = SUM(order line item values from Shopify)
       returns_rate          = total_return_value / total_order_value

// Step 7: Compare KPIs against targets
  breach check:
    on_time_dispatch_rate < target_on_time_dispatch_pct -> flag
    fill_rate             < target_fill_rate_pct        -> flag
    avg_dispatch_lead_time > target_avg_lead_time_days  -> flag
    returns_rate           > target_returns_rate_pct    -> flag
  -> breached_kpis: string[]

// Step 8: AI commentary call
POST /v1/chat/completions  // language model endpoint
  body: {
    model: LLM_MODEL_ENV_VAR,
    messages: [
      { role: 'system', content: COMMENTARY_SYSTEM_PROMPT },
      { role: 'user',   content: JSON.stringify({
          period: { start, end },
          kpis: { on_time_dispatch_rate, fill_rate,
                  avg_dispatch_lead_time, returns_rate },
          targets: { target_on_time_dispatch_pct, target_fill_rate_pct,
                     target_avg_lead_time_days, target_returns_rate_pct },
          breached_kpis,
          prior_period_kpis: { ... }  // from historical_log last row
        })
      }
    ],
    max_tokens: 250
  }
  -> commentary_draft: string

// Step 9: Human review gate
Slack API: chat.postMessage
  channel: REVIEWER_SLACK_USER_ID  // DM
  blocks: [KPI table, commentary_draft, [Approve] [Edit] buttons]
  -> wait for interactive callback on /slack/actions webhook
  -> on 'approve':      review_status = 'approved'
                        approved_commentary = commentary_draft
  -> on 'edit':         open modal, accept edited_text
                        review_status = 'edited_and_approved'
                        approved_commentary = edited_text
  -> on timeout 2h:     re-notify REVIEWER_SLACK_USER_ID
  -> on timeout 4h:     Gmail escalation to FALLBACK_REVIEWER_EMAIL

// Write KPI results to kpi_output tab
Sheets API: spreadsheets.values.append
  range: 'kpi_output!A:N'
  -> row: [period_start, period_end, on_time_dispatch_rate, fill_rate,
           avg_dispatch_lead_time, returns_rate, breached_kpis (comma-joined),
           commentary_draft, review_status, approved_commentary,
           approved_by, approved_at]

// -- AGENT 2 -> AGENT 3 HANDOFF --
// Payload: { on_time_dispatch_rate, fill_rate, avg_dispatch_lead_time,
//            returns_rate, breached_kpis, approved_commentary,
//            approved_by, approved_at, reporting_period_start,
//            reporting_period_end, sheet_tab_name }

// ============================================================
// AGENT 3: Report Distribution Agent
// ============================================================

// Step 10: Slack channel post
Slack API: chat.postMessage
  channel: SLACK_OPS_CHANNEL_ID
  blocks: Block Kit JSON {
    header:  'Fulfilment KPI Report: {period_start} to {period_end}',
    section: on_time_dispatch_rate vs target (green/red circle),
    section: fill_rate vs target,
    section: avg_dispatch_lead_time vs target,
    section: returns_rate vs target,
    context: approved_commentary
  }
  -> slack_post_ts: string (returned by API)

// Step 11: PDF export
GET https://docs.google.com/spreadsheets/d/{SHEET_ID}/export
  params: { format: 'pdf', gid: PDF_EXPORT_GID, portrait: true,
            fitw: true, top_margin: 0.5, bottom_margin: 0.5 }
  -> pdf_bytes: binary

// Step 12: Gmail send
Gmail API: users.messages.send
  to:      REPORT_RECIPIENTS (comma-separated env var)
  subject: 'Fulfilment KPI Report: {period_start} to {period_end}'
  body:    plain-text KPI table + approved_commentary
  attach:  pdf_bytes as 'fulfilment_report_{period_start}_{period_end}.pdf'
  -> gmail_message_id: string

// Step 13: Historical log append
Sheets API: spreadsheets.values.append
  range: 'historical_log!A:N'
  insertDataOption: 'INSERT_ROWS'
  -> row: [log_date (now()), period_start, period_end, on_time_dispatch_rate,
           fill_rate, avg_dispatch_lead_time, returns_rate,
           breached_kpis (comma-joined), approved_commentary,
           approved_by, approved_at, slack_post_ts, gmail_message_id,
           distribution_status]

// ============================================================
// END OF RUN
// ============================================================
distribution_status: 'success'
-> error log row written to Supabase errors table only if any step
   returned a non-success status
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

04Recommended build stack

Orchestration layer
A workflow automation tool running one discrete workflow per agent (three workflows total: Data Collection, KPI and Commentary, Report Distribution). Each workflow is self-contained with its own trigger, error handling, and exit condition. A shared credential store is used across all three workflows to avoid duplicating API keys. Credentials are stored as named, encrypted variables accessible to all workflows in the project, never hardcoded in workflow nodes.
Webhook configuration
Two internal webhooks are required as agent-to-agent handoff signals: (1) Data Collection Agent fires a webhook to the KPI and Commentary Agent workflow on success; (2) KPI and Commentary Agent fires a webhook to the Report Distribution Agent workflow on review approval. A third inbound webhook is required from Slack to receive interactive component callbacks (Approve and Edit button clicks) for the human review gate. The Slack callback URL must be registered in the Slack app's Interactivity settings before testing. All webhooks must validate a shared secret header (X-FullSpec-Secret) to reject unauthorised calls.
Templating approach
The Slack Block Kit JSON for both the reviewer DM (review gate) and the ops channel post (distribution) is templated as a JSON string with placeholder tokens ({period_start}, {period_end}, {on_time_dispatch_rate}, {kpi_status_emoji}, {approved_commentary}) that are substituted at runtime before the API call is made. The Gmail email body uses a plain-text template with the same substitution pattern. Templates are stored as environment variables (SLACK_REVIEW_TEMPLATE, SLACK_CHANNEL_TEMPLATE, EMAIL_BODY_TEMPLATE) so they can be updated without touching workflow logic. The AI commentary system prompt is similarly stored as COMMENTARY_SYSTEM_PROMPT.
Error logging
All agent failures write a structured row to a Supabase errors table (table name: automation_errors). Columns: id (uuid), workflow_name (string), error_step (string), error_message (string), input_payload (jsonb), created_at (timestamptz), resolved (boolean, default false). On any error row insert, a Slack alert is posted automatically to the SLACK_ERRORS_CHANNEL_ID channel (separate from the ops reporting channel) with the workflow name, failing step, and error message. The Supabase project and table must be provisioned before build begins. Connection credentials are stored in the shared credential store as SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY.
Testing approach
All three agents must be built and tested against sandbox or test credentials first. Shopify provides a development store for API testing. ShipStation has a sandbox environment accessible via separate sandbox API keys. Xero provides a demo company for OAuth testing. Google Sheets testing uses a dedicated test spreadsheet (separate from the production master) with the same tab and column structure. The Slack reviewer DM and channel post are tested against a private test Slack workspace before being pointed at the production workspace. Gmail send is tested with a restricted REPORT_RECIPIENTS list containing only FullSpec team addresses until end-to-end QA is signed off. Each agent is tested in isolation before the full chain is run. Two complete end-to-end runs using real historical data (backfilled period dates) must pass before go-live.
Estimated total build time
Data Collection Agent: 8 hours. KPI and Commentary Agent: 7 hours. Report Distribution Agent: 5 hours. End-to-end integration testing and QA: 2 hours. Total: 22 hours across a 4-week delivery schedule.
Pre-build blockers: The following items must be resolved before any workflow build begins. (1) Shopify read_orders API credentials confirmed and scoped. (2) ShipStation sandbox API key and production API key provided. (3) Xero OAuth 2.0 app created with accounting.transactions.read and offline_access scopes, and the refresh token flow confirmed. (4) Google Sheets service account created and granted Editor access to the master spreadsheet. (5) Slack app created with chat:write, chat:write.public, and im:write scopes, bot added to the ops channel and errors channel. (6) Gmail sending credential (service account with domain-wide delegation or OAuth token) confirmed. (7) KPI targets and thresholds documented and agreed by the ops manager. (8) Supabase project provisioned and errors table schema applied. Contact the FullSpec team at support@gofullspec.com if any of these items need assistance before build start.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Fulfilment Performance Reporting.

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