Back to Vendor Performance Tracking

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

Vendor Performance Tracking

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

This document gives the FullSpec build team everything needed to construct, configure, and hand over the Vendor Performance Tracking automation. It covers the current manual process and its bottlenecks, full specifications for each of the three agents, the end-to-end data flow, and the recommended build stack. The process owner's responsibility is to confirm scoring thresholds, PO numbering conventions, and tool access before build begins. FullSpec handles all build, integration, testing, and monitoring from that point forward.

01Process snapshot

Step
Name
Description
1 BOTTLENECK
Collect Delivery Data from Suppliers
Ops Coordinator manually checks emails, supplier portals, and delivery notes to compile what arrived, when, and whether it matched the purchase order. (40 min)
2
Pull Invoice Data from Accounting System
Coordinator logs into Xero and exports invoice records for the period, noting discrepancies between billed and ordered amounts. (20 min)
3 BOTTLENECK
Update Vendor Scorecard Spreadsheet
Delivery timing, invoice accuracy, and quality issues are manually entered into the master Google Sheets scorecard for each vendor. (45 min)
4
Calculate Performance Scores
Coordinator applies a scoring formula in the spreadsheet, sometimes by hand when cells have formatting issues, to generate an overall rating per vendor. (20 min)
5
Identify Underperforming Vendors
Coordinator visually scans the scorecard to flag any vendor whose score has dropped below the acceptable threshold. (15 min)
6
Notify Ops Manager of Poor Performers
Coordinator writes and sends a manual summary email to the ops manager listing flagged vendors with context pulled from memory or notes. (20 min)
7
Ops Manager Reviews and Decides on Action
Ops Manager reads the email, cross-references the spreadsheet, and decides whether to contact the vendor, escalate, or monitor further. (25 min)
8
Draft Vendor Communication if Required
If action is needed, the ops manager manually drafts an email to the vendor requesting explanation or improvement. (25 min)
9
Update Vendor Record with Outcome
After communication, the coordinator updates the vendor's Airtable record with the action taken and any commitments made. (15 min)
10 BOTTLENECK
Prepare Monthly Vendor Review Report
At month end, the coordinator manually assembles a summary report from the scorecard, pulling trend data and commentary for the review meeting. (50 min)
Time cost summary: Total manual time per cycle is 275 minutes (4 hours 35 min). At approximately 40 vendor events per month with a weekly rollup cadence, the process costs roughly 6 hours per week and 26 hours per 30 days at an assumed rate of $52/hour, equating to $15,600/year. Steps 1, 2, 3, 4, 5, 6, 8, 9, and 10 are fully replaced by the automation. Step 7 (manager review and decision) is retained as the single intentional human gate. The three bottleneck steps (1, 3, and 10) account for 135 of the 275 minutes per cycle and are the highest-priority targets for automation.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical

02Agent specifications

Vendor Data Collection Agent

Pulls invoice and delivery data from Xero on each trigger event, cross-references purchase orders, and extracts the fields needed for scoring without any manual export. This agent fires on every new Xero invoice record or PO status change to received, making it the entry point for the entire automation chain. It is responsible for data completeness: if a required field is missing or a PO cannot be matched, the agent must park the record and raise an alert rather than pass incomplete data downstream.

Trigger
New invoice created in Xero OR purchase order status changes to 'received'
Tools
Xero (source), Airtable (destination)
Replaces steps
Step 1 (Collect Delivery Data) and Step 2 (Pull Invoice Data)
Estimated build
10 hours — Moderate complexity
// Input
Xero webhook payload OR poll result {
  invoice_id: string,
  invoice_number: string,
  contact_id: string,          // maps to vendor_id in Airtable
  contact_name: string,
  invoice_date: ISO8601,
  due_date: ISO8601,
  total_amount: float,
  line_items: [{ description, quantity, unit_amount, account_code }],
  po_reference: string,        // must match PO number in Xero
  status: 'AUTHORISED' | 'PAID' | 'DRAFT'
}

// PO cross-reference lookup (Xero PurchaseOrders endpoint)
purchase_order {
  po_number: string,
  expected_delivery_date: ISO8601,
  actual_delivery_date: ISO8601 | null,
  ordered_amount: float,
  ordered_line_items: [{ description, quantity, unit_amount }]
}

// Output — structured vendor event record written to Airtable
vendor_event_record {
  vendor_id: string,            // Airtable Vendors table record ID
  vendor_name: string,
  event_date: ISO8601,
  invoice_number: string,
  billed_amount: float,
  ordered_amount: float,
  invoice_variance: float,      // billed_amount minus ordered_amount
  expected_delivery_date: ISO8601,
  actual_delivery_date: ISO8601 | null,
  delivery_date_variance_days: integer | null,
  data_complete: boolean,       // false triggers a parse-error alert
  raw_xero_invoice_id: string
}
  • Xero API tier required: Standard or higher. The PurchaseOrders endpoint is not available on the Xero Starter plan. Confirm the client's Xero subscription before build.
  • OAuth 2.0 scopes required: accounting.transactions.read and accounting.contacts.read. Do not request write scopes for this agent.
  • PO matching logic: match invoice po_reference field to PurchaseOrder.PurchaseOrderNumber. If no match is found, set data_complete to false, write a partial record to Airtable with a 'PO_UNMATCHED' flag, and post a warning to the ops Slack channel. Do not halt the run.
  • Dedupe: check Airtable VendorEvents table for an existing record with the same invoice_number before writing. If a duplicate is found, skip and log. Do not upsert.
  • Fallback for missing actual_delivery_date: set delivery_date_variance_days to null and flag as 'DELIVERY_DATE_PENDING'. The Scoring Agent must handle null variance gracefully.
  • Polling interval if webhooks are unavailable: every 15 minutes via Xero API poll on AUTHORISED invoices modified in the last 20 minutes.
  • PO numbering conventions must be reviewed and standardised in Xero before build begins. Inconsistent PO references are the single most likely cause of failed matches.
Performance Scoring Agent

Applies a configurable scoring model to each vendor event record created by the Data Collection Agent, identifies threshold breaches, and writes a plain-English performance summary to the vendor's Airtable record and the Google Sheets scorecard. Scoring weights and threshold values must be confirmed by the ops manager before this agent is built. The agent treats null delivery_date_variance_days as a neutral score contribution (zero penalty, zero bonus) rather than an error, to avoid penalising vendors for delivery records still in transit.

Trigger
New vendor_event_record created in Airtable VendorEvents table (watch trigger on Created Time field)
Tools
Airtable (read event record, write score and summary), Google Sheets (write scorecard row and flag)
Replaces steps
Step 3 (Update Scorecard), Step 4 (Calculate Scores), Step 5 (Identify Underperformers), Step 9 (Update Vendor Record with Outcome)
Estimated build
10 hours — Moderate complexity
// Input — vendor_event_record from Airtable
vendor_event_record {
  vendor_id: string,
  vendor_name: string,
  invoice_variance: float,
  delivery_date_variance_days: integer | null,
  data_complete: boolean
}

// Scoring model (configurable — values to be confirmed with ops manager)
scoring_weights {
  on_time_delivery:   40,       // percent of total score
  invoice_accuracy:   40,
  quality_flags:      20
}
thresholds {
  flag_below_score:   65,       // vendor flagged if rolling 90-day score < 65
  invoice_variance_pct_cap: 5,  // >5% variance = full penalty on invoice_accuracy
  late_days_cap: 3              // >3 days late = full penalty on on_time_delivery
}

// Output — written to Airtable Vendors table and Google Sheets scorecard
scoring_output {
  vendor_id: string,
  event_score: integer,         // 0 to 100 for this single event
  rolling_score_90d: float,     // recalculated from last 90 days of events
  flagged: boolean,             // true if rolling_score_90d < threshold
  performance_summary: string,  // plain-English paragraph, max 120 words
  scorecard_row: {
    vendor_name, event_date, event_score, rolling_score_90d, flagged
  }
}
  • Airtable base must contain: a Vendors table (with VendorID, VendorName, RollingScore90d, LastEventDate, FlaggedStatus fields) and a VendorEvents table (with all vendor_event_record fields plus EventScore).
  • Google Sheets scorecard tab name must be confirmed before build. Recommended tab name: 'VendorScorecard'. The agent appends one row per event; it does not overwrite existing rows.
  • Rolling 90-day score calculation: query VendorEvents table filtered by vendor_id and event_date within the last 90 days, average the EventScore values. Minimum 3 events required to set flagged status; fewer than 3 events results in flagged: null (not false).
  • Quality flags field: populated manually by the ops coordinator via Airtable form when a quality issue is reported. Scoring Agent reads this field on each run. Confirm with ops manager whether to include quality flags in the initial build or phase them in.
  • Threshold values listed in the IO block are provisional. The ops manager must sign off exact values before go-live. Thresholds are stored as a configuration record in Airtable (ScoringConfig table) so they can be updated without rebuilding the agent.
  • If data_complete is false on the inbound record, skip scoring and log a skipped_event entry in the VendorEvents table. Do not write a score of zero.
Developer Handover PackPage 2 of 4
FS-DOC-04Technical
Escalation and Reporting Agent

Handles all downstream communication and reporting: sending Slack alerts when a vendor is flagged, drafting vendor emails for manager approval via Gmail, and assembling the monthly review report from Airtable data into Google Sheets. This agent has two distinct trigger paths: the real-time escalation path fires whenever the Scoring Agent sets flagged to true on a vendor record; the monthly reporting path fires on a scheduled trigger on the last business day of each calendar month. Both paths share Airtable as their data source but write to different outputs.

Trigger
Path A: Airtable Vendors record updated with flagged = true. Path B: Scheduled trigger, last business day of month at 08:00 local time.
Tools
Slack (alert message), Gmail (draft vendor email), Airtable (read vendor and event data), Google Sheets (append monthly report)
Replaces steps
Step 6 (Notify Ops Manager), Step 8 (Draft Vendor Communication), Step 10 (Prepare Monthly Report)
Estimated build
8 hours — Moderate complexity
// Input — Path A (escalation)
flagged_vendor {
  vendor_id: string,
  vendor_name: string,
  rolling_score_90d: float,
  performance_summary: string,
  last_event_date: ISO8601,
  flagged: true
}

// Input — Path B (monthly report)
report_payload {
  report_month: string,         // e.g. 'January 2025'
  vendor_events: [ vendor_event_record ],   // all events this month
  vendor_scores: [ { vendor_id, vendor_name, rolling_score_90d, flagged } ]
}

// Output — Path A: Slack alert
slack_message {
  channel: '#ops-alerts',
  text: 'Vendor [vendor_name] flagged. 90-day score: [rolling_score_90d]. Last event: [last_event_date]. Summary: [performance_summary]'
}

// Output — Path A: Gmail draft awaiting approval
gmail_draft {
  to: vendor_email,             // sourced from Airtable Vendors.ContactEmail
  subject: 'Performance Review Notice — [vendor_name]',
  body: populated from approved template,
  draft_id: string              // stored in Airtable for manager retrieval
}

// On approval — ops manager confirms via Slack interactive button or email reply
approval_action {
  approved: boolean,
  manager_name: string,
  approval_timestamp: ISO8601
}
// If approved: Gmail draft is sent. If rejected: draft is deleted, Airtable note updated.

// Output — Path B: Google Sheets monthly report row
monthly_report_row {
  report_month, vendor_name, event_count, avg_event_score,
  rolling_score_90d, flagged_this_month: boolean, escalation_sent: boolean
}
  • Slack workspace must have an incoming webhook configured for the '#ops-alerts' channel. Confirm channel name with the ops manager before build. If Slack interactive buttons are used for the approval flow, a Slack app with the chat:write and im:write scopes must be created in the client's workspace.
  • Gmail send-on-behalf authorisation: the automation must send from the ops manager's Gmail address or a shared ops mailbox. OAuth 2.0 scope required: gmail.compose (for drafts) and gmail.send (for approved sends). Confirm which address the vendor email should originate from before build.
  • Vendor contact email field: Airtable Vendors table must include a ContactEmail field populated before the escalation path is tested. Flag any vendors with missing email as 'ESCALATION_BLOCKED' in Airtable rather than erroring the run.
  • Monthly report scheduled trigger: last business day of month is calculated dynamically. If the last calendar day falls on a weekend, the trigger fires on the preceding Friday. Confirm timezone with the ops team (assume local business timezone of the client).
  • Email draft template: a plain-text approved template must be supplied and signed off by the ops manager before go-live. Template variables are: [vendor_name], [rolling_score_90d], [specific_issue_summary], [response_deadline]. Store the template in Airtable (EmailTemplates table) for easy updates.
  • Do not send a vendor email without an explicit approval action. The approval gate is non-negotiable and must be tested end-to-end in UAT before launch.
  • Deduplication for Slack alerts: if a vendor was flagged and alerted within the last 7 days, suppress the Slack message and log a 'ALERT_SUPPRESSED' entry in Airtable to avoid alert fatigue.
Total estimated agent build hours: Vendor Data Collection Agent (10 hrs) + Performance Scoring Agent (10 hrs) + Escalation and Reporting Agent (8 hrs) = 28 hours of agent build, consistent with the confirmed build effort figure. End-to-end integration testing and UAT add additional time accounted for in the build stack section.
Developer Handover PackPage 3 of 4
FS-DOC-04Technical

03End-to-end data flow

Full trigger-to-output data flow for the Vendor Performance Tracking automation. Field names match the Airtable schema and Xero API response keys.
// ============================================================
// VENDOR PERFORMANCE TRACKING — END-TO-END DATA FLOW
// Volume: ~40 vendor events/month | Runs this month: 220
// ============================================================

// TRIGGER
// Xero fires webhook (or poll detects change) on:
//   - New invoice with status AUTHORISED
//   - PurchaseOrder.DeliveryDate populated (PO marked received)
Xero.invoice_created | Xero.po_received
  -> payload: { invoice_id, invoice_number, contact_id, contact_name,
                invoice_date, due_date, total_amount, po_reference,
                line_items[], status }

// ============================================================
// AGENT HANDOFF 1: Trigger -> Vendor Data Collection Agent
// ============================================================

VendorDataCollectionAgent.run(invoice_id, po_reference)
  // Step 1: Fetch invoice detail
  Xero.GET /invoices/{invoice_id}
    -> { invoice_number, contact_id, total_amount, line_items[],
         due_date, invoice_date }

  // Step 2: Cross-reference purchase order
  Xero.GET /purchaseorders?PurchaseOrderNumber={po_reference}
    -> { po_number, expected_delivery_date, actual_delivery_date,
         ordered_amount, ordered_line_items[] }

  // Step 3: Derive variance fields
  invoice_variance         = total_amount - ordered_amount
  delivery_date_variance   = actual_delivery_date - expected_delivery_date (days)
                             // null if actual_delivery_date not yet set

  // Step 4: Resolve Airtable vendor_id
  Airtable.GET Vendors WHERE XeroContactID = contact_id
    -> { vendor_id, vendor_name }

  // Step 5: Dedupe check
  Airtable.GET VendorEvents WHERE invoice_number = invoice_number
    -> if record exists: HALT, log 'DUPLICATE_SKIPPED'

  // Step 6: Write vendor event record
  Airtable.POST VendorEvents {
    vendor_id, vendor_name, event_date, invoice_number,
    billed_amount: total_amount, ordered_amount,
    invoice_variance, expected_delivery_date, actual_delivery_date,
    delivery_date_variance_days, data_complete, raw_xero_invoice_id
  }
    -> new_record_id: string

// ============================================================
// AGENT HANDOFF 2: VendorEvents record created -> Performance Scoring Agent
// ============================================================

PerformanceScoringAgent.run(new_record_id)
  // Step 1: Read event record
  Airtable.GET VendorEvents/{new_record_id}
    -> { vendor_id, invoice_variance, delivery_date_variance_days,
         data_complete }

  // Guard: if data_complete == false -> log 'SCORING_SKIPPED', HALT

  // Step 2: Read scoring config
  Airtable.GET ScoringConfig[0]
    -> { flag_below_score, invoice_variance_pct_cap, late_days_cap,
         weight_on_time, weight_invoice_accuracy, weight_quality }

  // Step 3: Calculate event_score (0–100)
  on_time_score      = score_delivery(delivery_date_variance_days, late_days_cap)
  invoice_score      = score_invoice(invoice_variance / ordered_amount, variance_pct_cap)
  quality_score      = Airtable.GET VendorEvents.quality_flags -> score_quality()
  event_score        = (on_time_score * weight_on_time)
                     + (invoice_score * weight_invoice_accuracy)
                     + (quality_score * weight_quality)

  // Step 4: Calculate rolling 90-day score
  Airtable.GET VendorEvents
    WHERE vendor_id = vendor_id
    AND event_date >= TODAY - 90 days
    -> [event_score_list]
  rolling_score_90d  = AVERAGE(event_score_list)   // min 3 records to set flagged

  // Step 5: Determine flag status
  flagged = (COUNT(event_score_list) >= 3) AND (rolling_score_90d < flag_below_score)

  // Step 6: Generate plain-English summary (template fill)
  performance_summary = build_summary(vendor_name, rolling_score_90d,
                                      on_time_score, invoice_score)

  // Step 7: Write score back to Airtable
  Airtable.PATCH Vendors/{vendor_id} {
    rolling_score_90d, flagged, last_event_date: event_date,
    performance_summary
  }
  Airtable.PATCH VendorEvents/{new_record_id} { event_score }

  // Step 8: Append row to Google Sheets scorecard
  GoogleSheets.appendRow('VendorScorecard') {
    vendor_name, event_date, event_score, rolling_score_90d, flagged
  }

// ============================================================
// AGENT HANDOFF 3A: flagged == true -> Escalation and Reporting Agent (Real-time path)
// ============================================================

EscalationReportingAgent.escalate(vendor_id)
  // Guard: check Airtable AlertLog WHERE vendor_id AND alert_date >= TODAY - 7 days
  //   -> if exists: log 'ALERT_SUPPRESSED', HALT

  // Step 1: Post Slack alert
  Slack.postMessage(channel='#ops-alerts') {
    text: 'Vendor [vendor_name] flagged. Score: [rolling_score_90d]. [performance_summary]'
    // Include: approve/reject interactive buttons if Slack app configured
  }

  // Step 2: Create Gmail draft
  Gmail.createDraft {
    to: Airtable.Vendors.ContactEmail,
    subject: 'Performance Review Notice — [vendor_name]',
    body: Airtable.EmailTemplates['vendor_escalation'].body
           with { vendor_name, rolling_score_90d, performance_summary,
                  response_deadline: TODAY + 5 business days }
  }
    -> draft_id: string

  // Step 3: Store draft reference
  Airtable.PATCH Vendors/{vendor_id} { pending_draft_id: draft_id }
  Airtable.POST AlertLog { vendor_id, alert_date: TODAY, draft_id }

// ============================================================
// HUMAN GATE: Ops Manager reviews Slack alert, approves or rejects
// ============================================================

OpsManager.decision(approval_action)
  -> { approved: boolean, manager_name, approval_timestamp }

  if approved:
    Gmail.sendDraft(draft_id)
    Airtable.PATCH Vendors/{vendor_id} {
      escalation_sent: true, escalation_date: approval_timestamp,
      escalation_approved_by: manager_name
    }
  else:
    Gmail.deleteDraft(draft_id)
    Airtable.PATCH Vendors/{vendor_id} {
      escalation_sent: false, escalation_rejected_reason: 'manager_declined'
    }

// ============================================================
// AGENT HANDOFF 3B: Scheduled trigger -> Escalation and Reporting Agent (Monthly path)
// ============================================================

EscalationReportingAgent.generateMonthlyReport(report_month)
  // Step 1: Fetch all events for the month
  Airtable.GET VendorEvents
    WHERE event_date >= FIRST_DAY(report_month)
    AND event_date <= LAST_DAY(report_month)
    -> [vendor_event_list]

  // Step 2: Aggregate per vendor
  for each vendor_id in DISTINCT(vendor_event_list.vendor_id):
    event_count         = COUNT(events for vendor)
    avg_event_score     = AVERAGE(event_score for vendor)
    rolling_score_90d   = Airtable.Vendors[vendor_id].rolling_score_90d
    flagged_this_month  = ANY(flagged == true in events for vendor)
    escalation_sent     = Airtable.Vendors[vendor_id].escalation_sent

  // Step 3: Append summary rows to Google Sheets
  GoogleSheets.appendRows('MonthlyReport') {
    report_month, vendor_name, event_count, avg_event_score,
    rolling_score_90d, flagged_this_month, escalation_sent
  }

// ============================================================
// END OF FLOW
// ============================================================

04Recommended build stack

Orchestration layer
A workflow automation platform running one workflow per agent (three workflows total): VendorDataCollectionWorkflow, PerformanceScoringWorkflow, and EscalationReportingWorkflow. All three workflows share a single credential store so that Xero, Airtable, Google Sheets, Slack, and Gmail tokens are managed centrally and rotated in one place. Workflows are version-controlled and named with the prefix 'VPT_' for easy identification.
Webhook configuration
Xero webhook registered for the AccountingInvoice and PurchaseOrder event types, pointing to the VendorDataCollectionWorkflow webhook URL. Webhook secret verified on every inbound request using HMAC-SHA256 signature validation. Fallback: a 15-minute poll on Xero GET /invoices?where=Status==AUTHORISED&ModifiedAfter={last_run_timestamp} runs if the webhook has not fired within the polling window. Airtable watch trigger on VendorEvents.CreatedTime feeds the PerformanceScoringWorkflow. Scheduled trigger (last business day of month, 08:00) feeds the EscalationReportingWorkflow monthly path.
Templating approach
Vendor escalation email body stored as a record in an Airtable EmailTemplates table with named variable placeholders: {{vendor_name}}, {{rolling_score_90d}}, {{performance_summary}}, {{response_deadline}}. Template is filled at runtime by the EscalationReportingWorkflow using a simple string interpolation step. Plain-English performance summaries generated by the PerformanceScoringWorkflow use a fixed narrative template with score-band conditionals (e.g. 'Score above 80: strong performer', 'Score 65–79: satisfactory with noted issues', 'Score below 65: flagged for review') rather than a language model, for deterministic and auditable output.
Error logging
All error states (PO_UNMATCHED, DUPLICATE_SKIPPED, SCORING_SKIPPED, ESCALATION_BLOCKED, ALERT_SUPPRESSED) are written to a dedicated Airtable table named 'AutomationErrorLog' with fields: error_code, workflow_name, vendor_id, invoice_number, error_timestamp, error_detail. A Slack alert to '#ops-alerts' fires for any error_code of PO_UNMATCHED or ESCALATION_BLOCKED, as these require human resolution. DUPLICATE_SKIPPED and ALERT_SUPPRESSED are informational and do not trigger Slack alerts.
Testing approach
All three workflows are built and tested against a dedicated Xero demo company and a cloned Airtable base (suffix '_SANDBOX') before any connection to production data. Google Sheets sandbox tab named 'VendorScorecard_TEST' is used for scorecard write tests. Gmail draft creation is tested without sending. Slack alerts are tested against a '#ops-test' channel. Production credentials are only loaded after all three workflows pass the full QA test plan. UAT is run with the ops coordinator and ops manager against the last 30 days of real historical vendor data in a read-only replay mode.
Estimated total build time
Vendor Data Collection Agent: 10 hours. Performance Scoring Agent: 10 hours. Escalation and Reporting Agent: 8 hours. End-to-end integration and error-path testing: 5 hours. UAT support and handoff documentation: 3 hours. Discovery, data audit, and configuration review: 2 hours. Total confirmed build effort: 28 hours (agent build) plus approximately 10 hours integration and delivery overhead, consistent with the 3 to 4 week delivery timeline and $3,800 Standard build price.
Before any build work begins, FullSpec requires the following to be confirmed by the process owner: (1) Xero subscription tier and PO numbering conventions reviewed and standardised; (2) scoring thresholds and weighting percentages signed off by the ops manager and stored in the Airtable ScoringConfig table; (3) OAuth credentials provisioned for Xero, Gmail, Airtable, Google Sheets, and Slack; (4) vendor contact email addresses populated in the Airtable Vendors table; (5) the escalation email template reviewed and approved. Build will not proceed past the Discovery and Data Audit stage until all five items are confirmed. Contact support@gofullspec.com with any questions.
Developer Handover PackPage 4 of 4

More documents for this process

Every document generated for Vendor Performance Tracking.

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