Tech Direct Support · Solution Architecture
Autonomous shift-to-cash automation for the security industry: request intake, rostering, guard allocation, client confirmation, billing, attendance, timesheet approval and executive reporting — with human control points at every point of risk.
Your 13 requirements decompose cleanly into 9 workflow segments. Each segment is a distinct decision domain with its own data, its own rules and its own failure mode — so each gets one specialist agent. A tenth agent, the Orchestrator, owns the shift state machine, retries and handoffs so no specialist has to know about any other.
A single "do everything" agent cannot be tested, tuned or trusted. Your brief demands 100% accuracy — that is only achievable when each step has a narrow scope, a measurable pass rate, and can be independently rolled back. Fatigue-rule logic and invoice logic failing together is an outage; failing separately is a ticket.
Every extra agent adds a handoff, and handoffs are where state gets lost. Anything that shares the same data, the same system-of-record and the same approver belongs in one agent. Ten is the point where each agent owns exactly one system-of-record write and one class of decision.
Ten agents, each with one job, one primary system-of-record, and a defined autonomy level.
| # | Agent | Owns | Primary systems | Autonomy | Brief ref |
|---|---|---|---|---|---|
| 0 | Orchestrator Agent | Shift state machine, sequencing, retries, timeouts, handoffs, full audit trail | Internal store, event log | Autonomous | 12 |
| 1 | Intake Agent | Reads inbound requests, classifies static / adhoc / core hours, extracts structured shift data | Outlook, WhatsApp, client portals | Semi — confidence gate | 1, 2 |
| 2 | Roster Agent | Resolves customer + site, creates the shift on the correct platform | Guardhouse, FieldService, Humanforce | Gated on new site/customer | 2, 3 |
| 3 | Allocation Agent | Ranks guards by site history, licence validity, availability; enforces the 36–48 hr fortnight fatigue rule | Rostering platforms, licence register | Semi — gated on supplier fill | 4, 5 |
| 4 | Engagement Agent | Multi-channel offers, first-accept-wins locking, escalation waves, offer withdrawal | WhatsApp, email, Humanforce / Guardhouse / FieldService push | Autonomous | 5, 6 |
| 5 | Client Comms Agent | Replies on the original email thread with guard details, ETA and shift reference | Outlook | Semi — per-client disclosure rules | 6 |
| 6 | Commercials Agent | Client billing profile, PO raise & send, invoice generation after approval | Accounting / invoicing platform, Outlook | Gated on new client + variance | 7 |
| 7 | Attendance Agent | Monitors clock in / out, late and no-show detection, triggers urgent backfill | Guardhouse, FieldService, Humanforce | Semi — human on no-show | 11 |
| 8 | Timesheet Agent | Rostered vs actual reconciliation, auto-approves clean sheets, classifies exceptions | Rostering platforms | Semi — exceptions to approver | 8, 9 |
| 9 | Insights Agent | Daily shift volume, $ value, invoicing pipeline, timesheet error queue, fatigue watchlist | Event log, all platforms | Autonomous | 10 |
One adapter per platform — Guardhouse, FieldService, Humanforce, Outlook, WhatsApp Business, accounting. Agents never call a vendor API directly, so a vendor change touches one adapter, not nine agents.
A single approval queue and notification path used by all 10 gates. One inbox for your ops team, not ten different prompts in ten different places.
Every agent action, every human decision, every API response — immutable and timestamped. This is what makes the "100% accuracy" claim defensible to a client, an auditor, or a court. It is also the data source for the dashboard.
The board view: one shift request, from client email to cash and dashboard.
flowchart TD
subgraph L1[LAYER 1 - DEMAND]
A["S1 Intake and Interpretation"] --> B["S2 Site Resolution and Shift Creation"]
end
subgraph L2[LAYER 2 - SUPPLY]
C["S3 Guard Matching and Fatigue Compliance"] --> D["S4 Offer and Acceptance"]
end
subgraph L3[LAYER 3 - COMMIT]
E["S5 Client Confirmation"] --> F["S6a Billing Profile and PO"]
end
subgraph L4[LAYER 4 - DELIVERY]
G["S7 Attendance and Clock In Out"] --> H["S8 Timesheet Reconciliation"]
end
subgraph L5[LAYER 5 - CASH AND VISIBILITY]
I["S6b Invoice Issue"] --> J["S9 Command Dashboard"]
end
B --> C
D --> E
F --> G
H --> I
O(["Agent 0 - Orchestrator
state, retries, handoffs, audit"]) -.-> L1
O -.-> L2
O -.-> L3
O -.-> L4
O -.-> L5
HC[/"Human Checkpoint Service
10 named gates"/] -.-> L1
HC -.-> L2
HC -.-> L3
HC -.-> L4
Solid arrows are the shift lifecycle. Dotted lines are cross-cutting control. Note that invoicing is split: PO before delivery, invoice after timesheet approval.
Turn an unstructured client email into a validated, machine-readable shift request.
flowchart TD
A["Client request lands in Outlook shared mailbox"] --> B{"Is this a shift or coverage request?"}
B -->|No| Z["Route to human inbox and stop"]
B -->|Yes| C["Intake Agent classifies request type"]
C --> C1["Static guard on site"]
C --> C2["Adhoc one off work"]
C --> C3["Ongoing core hours"]
C1 --> D["Extract fields"]
C2 --> D
C3 --> D
D --> E["Customer, site, address, date, start, finish, headcount,
licence class, rate, PO reference, client contact"]
E --> F{"Extraction confidence above threshold
and no required field missing?"}
F -->|No| G[/"CHECKPOINT 1
Human confirms or corrects parsed request"/]
F -->|Yes| H["Create Shift Request record with unique TDS reference"]
G --> H
H --> I["Acknowledge receipt to client on the same thread"]
I --> J["Hand to Roster Agent"]
Place the shift against the correct customer and site on the platform that client uses — never guess.
flowchart TD
A["Validated Shift Request"] --> B["Identify which platform this client uses"]
B --> C["Guardhouse / FieldService / Humanforce"]
C --> D{"Customer record exists?"}
D -->|No match| E[/"CHECKPOINT 2
Approve creation of new customer"/]
D -->|Fuzzy match| F[/"CHECKPOINT 2b
Confirm this is the right customer"/]
D -->|Exact match| G{"Site record exists under that customer?"}
E --> G
F --> G
G -->|No| H[/"CHECKPOINT 3
Approve creation of new site"/]
G -->|Yes| I["Create shift via platform API"]
H --> I
I --> J{"API call successful?"}
J -->|No| K["Retry 3 times with backoff"]
K --> L{"Still failing?"}
L -->|Yes| M[/"Escalate to ops - platform outage"/]
L -->|No| N["Write platform shift ID back to TDS record"]
J -->|Yes| N
N --> O["Hand to Allocation Agent"]
Find who has done this shift before, and confirm they can legally and safely do it again.
flowchart TD A["Shift live on platform"] --> B["Pull shift history for this site and this client"] B --> C["Rank candidates by site familiarity and reliability score"] C --> D["Hard filter - licence current, inductions valid,
no clash, right qualification class"] D --> E["Fatigue calculation - hours already rostered this fortnight plus this shift"] E --> F{"Total within the 36 to 48 hour threshold?"} F -->|No| G["Exclude guard and log overtime breach reason"] F -->|Yes| H["Add to TIER 1 - direct TDS guards"] G --> I{"Any Tier 1 candidates remaining?"} H --> I I -->|Yes| M["Build ranked offer list"] I -->|No| J["Escalate to TIER 2 - approved supplier guards"] J --> K{"Is supplier fill pre-approved for this client?"} K -->|Yes| M K -->|No| L[/"CHECKPOINT 4
Approve supplier fill and margin impact"/] L --> M M --> N["Hand ranked list to Engagement Agent"]
Reach the guard where they actually are, and lock the shift the moment someone says yes.
flowchart TD
A["Ranked offer list"] --> B["Send offer - wave 1 to top candidates"]
B --> C["WhatsApp message"]
B --> D["Email"]
B --> E["In app push - Humanforce, Guardhouse, FieldService"]
C --> F{"Response inside the SLA window?"}
D --> F
E --> F
F -->|Accepts| G["First accept wins - lock the shift immediately"]
F -->|Declines| H["Mark declined and move down the list"]
F -->|No response| I["Wave 2 - widen the pool and shorten the window"]
H --> J{"Candidates remaining?"}
I --> J
J -->|Yes| B
J -->|No| K[/"CHECKPOINT 5
Unfilled shift - ops decides: supplier, premium rate,
or advise client"/]
G --> L["Withdraw all outstanding offers for this shift"]
L --> M["Assign guard across every relevant platform"]
M --> N["Confirm to guard - site, address, ETA, contact, special instructions"]
N --> O["Hand to Client Comms Agent"]
Close the loop on the original email thread with exactly what that client is entitled to see.
flowchart TD
A["Guard assigned"] --> B["Load client communication profile"]
B --> C{"Profile defined for this client?"}
C -->|No| D[/"CHECKPOINT 6
Confirm what this client wants disclosed
and who is copied"/]
C -->|Yes| E["Assemble confirmation pack"]
D --> E
E --> F["Guard name and licence number"]
E --> G["Shift date, start, finish, ETA"]
E --> H["Site and TDS shift reference"]
E --> I["Escalation contact"]
F --> J["Reply on the ORIGINAL email thread - preserve subject and recipients"]
G --> J
H --> J
I --> J
J --> K["Log confirmation against the shift record"]
K --> L["Trigger Commercials Agent"]
Every client bills differently. The agent learns each profile once, from a human, then runs it.
flowchart TD
A["Shift confirmed to client"] --> B["Load client billing profile"]
B --> C{"Billing profile on file?"}
C -->|No| D[/"CHECKPOINT 7
Human defines: PO required, rate card, cycle,
consolidation, portal or email, approver"/]
C -->|Yes| E{"PO required before the shift is worked?"}
D --> E
E -->|Yes| F["Raise PO and send to client per profile"]
E -->|No| G["Flag shift as bill on completion"]
F --> H["Store PO number on the shift record"]
G --> H
H --> I(["PARK - wait for timesheet approval"])
I --> J["S8 returns an approved timesheet"]
J --> K["Generate invoice from approved actual hours"]
K --> L{"Variance against quoted value above tolerance?"}
L -->|Yes| M[/"CHECKPOINT 8
Approve invoice before issue"/]
L -->|No| N["Auto issue invoice per client cycle"]
M --> N
N --> O["Write to accounting platform and dashboard"]
The shift is only delivered when a guard is physically on site. This agent watches that in real time.
flowchart TD
A["Shift start approaching"] --> B["Pre shift reminder to guard - T minus 2 hours"]
B --> C{"Clocked in inside the grace window?"}
C -->|Yes| D["Mark on site and verify geolocation against site"]
C -->|No| E["Auto contact guard - WhatsApp and app prompt"]
E --> F{"Guard responds and is en route?"}
F -->|Yes| G["Notify client of revised ETA"]
F -->|No| H[/"CHECKPOINT 9
NO SHOW - ops authorises urgent backfill"/]
H --> I["Re inject into S3 Allocation as urgent priority"]
I --> J["Notify client of replacement guard"]
D --> K["Monitor mid shift welfare checks and patrol scans"]
G --> K
K --> L{"Clock out recorded at shift end?"}
L -->|Yes| M["Close attendance record with actual hours"]
L -->|No| N["Flag missing clock out as exception"]
M --> O["Hand to Timesheet Agent"]
N --> O
Your brief lists clock in/out at point 11, after timesheet approval at points 8–9. In practice attendance data is the source of truth a timesheet is reconciled against — so it must be captured first. This is the second re-sequencing correction under point 12.
Auto-approve what is clean. Escalate what is not, with the error already diagnosed.
flowchart TD
A["Shift completed"] --> B["Compare rostered versus actual against attendance record"]
B --> C{"Variance inside this client's tolerance?"}
C -->|Yes| D["Auto approve on the client's platform"]
C -->|No| E["Classify the exception"]
E --> F["Missing clock in or clock out"]
E --> G["Overtime or extended hours"]
E --> H["Wrong site or wrong guard clocked on"]
E --> I["Break, allowance or penalty rate mismatch"]
F --> J[/"CHECKPOINT 10
Approver is shown the error, the evidence
and the options: fix, approve as is, reject"/]
G --> J
H --> J
I --> J
J --> K["Apply the human decision on the platform"]
K --> L["Record decision and reason in the audit log"]
D --> M["Release to Commercials Agent for invoicing"]
L --> M
M --> N["Feed exception pattern back to Insights Agent"]
The single screen that tells you whether the business ran today.
flowchart TD A["Every agent writes to the shared event log"] --> B["Insights Agent aggregates"] B --> C["Shifts today - filled, unfilled, in progress, completed"] B --> D["Daily and month to date dollar value"] B --> E["Invoices pending, issued, overdue"] B --> F["Timesheets in error, grouped by client and by cause"] B --> G["Guard fatigue and overtime watchlist"] B --> H["Unfilled shift risk for the next 7 days"] B --> I["Autonomy score - percent completed without human touch"] C --> J["Live operations dashboard"] D --> J E --> J F --> J G --> J H --> J I --> J J --> K["Daily digest to leadership"] J --> L["Exception queue to ops"]
Your brief asked for the order to be corrected where it does not flow. Three changes.
| # | Your original order | Corrected order | Why it matters |
|---|---|---|---|
| 1 | Step 7 — invoicing runs immediately after client confirmation, before the shift is worked | Split: PO at commit, invoice after timesheet approval | Prevents invoicing hours that were never worked or that changed on the night. Removes credit notes and client disputes. |
| 2 | Step 11 — clock in/out checked last, after timesheet approval | Attendance moves before timesheet approval | Attendance data is the evidence a timesheet is reconciled against. Approving first and checking later is backwards. |
| 3 | Step 4–5 — guard history, then overtime check | Merged into one gate: match then hard-filter on licence, availability and fatigue in a single pass | Avoids offering a shift to a guard who was always ineligible — a common source of guard frustration and wasted cycle time. |
Ten named gates. Each one exists because the cost of an agent error at that point exceeds the cost of a human click.
| Gate | Segment | Fires when | Decision required | Retires when |
|---|---|---|---|---|
| CP1 | S1 Intake | Parse confidence below threshold or required field missing | Confirm or correct the parsed request | Never fully — threshold tightens with volume |
| CP2 | S2 Roster | Customer not found or only fuzzy-matched | Approve new customer / confirm match | As customer master data matures |
| CP3 | S2 Roster | Site not found under that customer | Approve new site creation | As site register matures |
| CP4 | S3 Allocation | No compliant direct guard — supplier fill needed | Approve supplier and margin impact | Per-client standing approval can be set |
| CP5 | S4 Engagement | Offer list exhausted, shift still unfilled | Supplier, premium rate, or advise client | Never — commercial judgement call |
| CP6 | S5 Client Comms | No disclosure profile for this client | Define what is shared and with whom | Once per client, permanently |
| CP7 | S6 Commercials | No billing profile for this client | Define PO rules, rate card, cycle, approver | Once per client, permanently |
| CP8 | S6 Commercials | Invoice value varies beyond tolerance | Approve, adjust or hold the invoice | Never — financial control |
| CP9 | S7 Attendance | Guard has not clocked in and is unreachable | Authorise urgent backfill and client notification | Never — service failure event |
| CP10 | S8 Timesheet | Rostered vs actual variance outside tolerance | Fix, approve as-is, or reject | Tolerances widen as data quality improves |
You do not build ten agents at once. You build the spine, prove accuracy, then extend.
| Phase | Agents live | What it proves | Exit criteria |
|---|---|---|---|
| 1 | 5 agents — Orchestrator + merged Intake/Roster + merged Allocation/Engagement + Attendance + Insights | A shift request can go from email to a filled, worked shift without manual rostering | 95%+ of requests parsed and rostered correctly · zero fatigue breaches · one platform integrated end to end |
| 2 | +2 — split Client Comms out, add Timesheet Agent | The client loop closes automatically and clean timesheets self-approve | Timesheet auto-approval rate above 70% · client confirmations sent within SLA |
| 3 | +1 — Commercials Agent with PO and invoice split | Shift-to-cash is complete; billing profiles captured for top clients | Billing profiles on file for clients covering 80% of revenue · invoice accuracy 100% |
| 4 | Full 10 — de-merge Phase 1 agents, add remaining platforms | Multi-platform, multi-client scale with independent agent tuning | All three platforms live · autonomy score trending above 80% |