FS-DOC-04Technical
Developer Handover Pack
Time Tracking and Billable Hours
Process
Time Tracking and Billable Hours
Build option
Standard (recommended)
Total build effort
48 hours across 3 weeks
Orchestration layer
FullSpec Automation
Primary integrations
Karbon, Xero, Slack, Gmail
Prepared for
[YourCompany.com]
01Process snapshot
The current manual process runs across six steps and consumes approximately 185 minutes per weekly close cycle. Three of those six steps are confirmed bottlenecks. Volume is roughly 30 timesheets per week, triggering approximately 410 automation runs per month once built. The automation eliminates 5 of the 6 steps, leaving only the partner approval as a human gate.
Step
Step Name
Owner
Tool
Time (min)
Bottleneck
Disposition
1
Remind Team To Log Time
Practice Manager
Slack
20
No
Automated
2
Chase Missing Timesheets
Practice Manager
Gmail
40
YES
Automated
3
Fill Gaps From Memory
Team Member
None
35
YES
Automated
4
Reconcile Against Jobs
Practice Manager
Karbon
45
YES
Automated
5
Approve Billable Hours
Partner
None
25
No
Human gate
6
Export For Billing
Practice Manager
Xero
20
No
Automated
Metric
Current state
After automation
Manual time per week
6 hrs/week
0.5 hrs/week
Annual staff cost
$12,600/year
$3,100/year
Timesheet completion rate
76%
98%
Annual hours saved
Baseline
225 hrs/year
Build cost (Standard)
N/A
$3,200 one-off
Payback period
N/A
4 months
Three bottleneck steps (Chase Missing Timesheets, Fill Gaps From Memory, Reconcile Against Jobs) account for 120 of the 185 manual minutes per cycle. These are the primary automation targets and must be validated against live data during the tuning period.
Developer Handover PackPage 1 of 4
FS-DOC-04Technical
02Agent specifications
Time Capture Agent
Captures time entries from calendars and work tools and assembles draft timesheets per team member at the start of each period close. Replaces the manual step of reminding staff to log time (Step 1). Targets Karbon as the primary data source and writes draft entries back to the timesheet layer ready for gap detection.
Trigger
A work period approaches close (weekly, schedule-based cron or Karbon webhook on period open)
Tools
Karbon, FullSpec Automation
Replaces steps
Step 1: Remind Team To Log Time
Estimated build time
12 hours
Implementation notes
Pull calendar events and Karbon task completions for each team member. Map duration and job code. Write draft timesheet rows. Flag rows with no job code as unresolved. A short tuning period is required against real Karbon data to calibrate job-code matching confidence thresholds.
// Input
karbon.period_close_event {
period_start: ISO8601,
period_end: ISO8601,
team_members: [ { id, name, karbon_user_id } ]
}
// Processing
FOR each team_member:
fetch karbon.time_entries(user_id, period_start, period_end)
fetch calendar.events(user_id, period_start, period_end)
merge_and_deduplicate(entries, events)
map_to_job_codes(merged) -> draft_rows[]
flag_unresolved(draft_rows where job_code == null)
// Output
draft_timesheets[] {
member_id: string,
period: ISO8601 range,
draft_rows: [ { date, duration_mins, job_code, description, status } ],
gap_flags: [ { date, expected_hrs, logged_hrs, delta_hrs } ],
confidence: float // 0.0 - 1.0
}Gap Chaser Agent
Identifies team members with missing or thin timesheet entries and sends targeted, specific nudges only to those individuals. Replaces both the manual email chase (Step 2) and the fill-from-memory step (Step 3). Nudges include the exact dates and expected hours that are missing, removing the need for the recipient to guess.
Trigger
Output from Time Capture Agent confirms gap_flags array is non-empty
Replaces steps
Step 2: Chase Missing Timesheets; Step 3: Fill Gaps From Memory
Estimated build time
14 hours
Implementation notes
Route nudges via Slack DM by default. Fall back to Gmail if the team member has no Slack account linked. Message must include specific gap dates and delta hours, not a generic reminder. Nudge frequency and tone are configurable. Do not send more than two nudges per person per period without escalating to the Practice Manager. Sensitive judgment around repeat offenders stays with the Practice Manager.
// Input
draft_timesheets[].gap_flags {
member_id: string,
date: ISO8601,
expected_hrs: float,
logged_hrs: float,
delta_hrs: float
}
// Processing
FOR each gap_flag:
resolve_channel(member_id) -> 'slack' | 'gmail'
compose_nudge_message(member_id, gap_dates, delta_hrs)
send_via_channel(channel, message)
record_nudge_sent(member_id, timestamp, channel)
IF nudge_count(member_id, period) > 2:
escalate_to_practice_manager(member_id, gap_summary)
// Output
nudge_log[] {
member_id: string,
channel: 'slack' | 'gmail',
message_ref: string,
sent_at: ISO8601,
gap_dates: [ ISO8601 ],
escalated: boolean
}Reconciliation Agent
Matches captured and gap-closed timesheet entries to jobs and clients in Karbon, calculates billable totals, and prepares a reconciled hours package for partner approval. Once approved, exports confirmed hours to Xero for invoicing. Replaces the manual reconciliation step (Step 4) and the billing export step (Step 6). Step 5 (partner approval) remains a human gate and is not automated.
Trigger
Timesheets are confirmed complete (gap_flags array cleared or nudge window expired) for the period
Tools
Xero, FullSpec Automation
Replaces steps
Step 4: Reconcile Against Jobs; Step 6: Export For Billing
Estimated build time
14 hours
Implementation notes
Match each draft_row to a Karbon job using job_code as primary key; fall back to client name fuzzy match if job_code is absent. Flag any row where match confidence is below 0.80 for human review. Aggregate billable hours per job and per client. Produce a partner-review summary and pause. On approval signal, push confirmed entries to Xero via the Xero Invoices API. Do not auto-post to Xero; always require explicit approval.
// Input
draft_timesheets[].draft_rows {
member_id: string,
date: ISO8601,
duration_mins: integer,
job_code: string | null,
description: string,
status: 'draft' | 'gap_filled'
}
// Processing
FOR each draft_row:
match = karbon.jobs.lookup(job_code) OR fuzzy_match(client_name)
IF match.confidence < 0.80: flag_for_review(draft_row)
ELSE: assign(draft_row, match.job_id, match.client_id)
aggregate_by_job_and_client(matched_rows) -> billing_summary
post_for_approval(billing_summary) -> AWAIT partner_approval_signal
ON approval:
xero.invoices.create(billing_summary) -> xero_invoice_ref
// Output
reconciliation_result {
period: ISO8601 range,
matched_rows: integer,
flagged_rows: integer,
total_billable_hrs: float,
billing_summary: [ { job_id, client_id, hrs, amount_usd } ],
xero_invoice_ref: string | null,
exported_at: ISO8601 | null
}Developer Handover PackPage 2 of 4
FS-DOC-04Technical
03End-to-end data flow
Full pipeline: period close trigger to Xero invoice export
// ─────────────────────────────────────────────────────────────────
// TRIGGER
// ─────────────────────────────────────────────────────────────────
CRON: every Friday 17:00 (or Karbon webhook: period.closing)
-> emit: period_close_event { period_start, period_end, team_members[] }
// ─────────────────────────────────────────────────────────────────
// STAGE 1: TIME CAPTURE AGENT
// ─────────────────────────────────────────────────────────────────
period_close_event
-> karbon.GET /time-entries?user={id}&from={period_start}&to={period_end}
-> calendar.GET /events?user={id}&from={period_start}&to={period_end}
-> merge_deduplicate(time_entries, calendar_events)
-> map_job_codes(merged) // confidence threshold: 0.80
-> write: draft_timesheets[]
-> evaluate: gap_flags[] = rows where logged_hrs < expected_hrs
// ─────────────────────────────────────────────────────────────────
// DECISION: GAPS FOUND?
// ─────────────────────────────────────────────────────────────────
IF gap_flags.length > 0:
-> STAGE 2 (Gap Chaser Agent)
ELSE:
-> STAGE 3 (Reconciliation Agent)
// ─────────────────────────────────────────────────────────────────
// STAGE 2: GAP CHASER AGENT
// ─────────────────────────────────────────────────────────────────
gap_flags[]
-> FOR each member_with_gaps:
resolve_channel(member_id) -> 'slack' | 'gmail'
IF slack:
slack.POST /chat.postMessage { channel: member.slack_id, text: nudge_msg }
IF gmail:
gmail.POST /messages/send { to: member.email, body: nudge_msg }
record nudge_log { member_id, channel, sent_at, gap_dates, delta_hrs }
IF nudge_count(member_id, period) > 2:
notify practice_manager via Slack (escalation)
-> AWAIT: timesheet updates OR nudge_window_expiry (48 hrs)
-> re-evaluate gap_flags[]
-> proceed: draft_timesheets[] (updated)
// ─────────────────────────────────────────────────────────────────
// STAGE 3: RECONCILIATION AGENT
// ─────────────────────────────────────────────────────────────────
draft_timesheets[]
-> FOR each draft_row:
karbon.GET /jobs?code={job_code} // primary lookup
IF no match: fuzzy_match(client_name, karbon.clients[])
IF match.confidence < 0.80: flag_for_review -> notify practice_manager
ELSE: assign { job_id, client_id, billable_hrs, billable_amount }
-> aggregate: billing_summary { per_job, per_client, total_hrs, total_amount }
-> notify partner: review summary (Slack or email)
// ─────────────────────────────────────────────────────────────────
// HUMAN GATE: PARTNER APPROVAL (Step 5, retained)
// ─────────────────────────────────────────────────────────────────
-> AWAIT: partner_approval_signal { approved: boolean, notes: string }
IF approved == false: return billing_summary to practice_manager for correction
IF approved == true: proceed to export
// ─────────────────────────────────────────────────────────────────
// EXPORT: XERO BILLING
// ─────────────────────────────────────────────────────────────────
billing_summary (approved)
-> xero.POST /api.xro/2.0/Invoices {
Type: 'ACCREC',
Contact: { ContactID: client.xero_contact_id },
LineItems: [ { Description, Quantity: hrs, UnitAmount, AccountCode } ],
Status: 'DRAFT'
}
-> store: xero_invoice_ref, exported_at
// ─────────────────────────────────────────────────────────────────
// OUTPUT
// ─────────────────────────────────────────────────────────────────
reconciliation_result {
period, matched_rows, flagged_rows,
total_billable_hrs, billing_summary[],
xero_invoice_ref, exported_at
}
// Pipeline complete. Hours exported to Xero as draft invoices.04Security and compliance requirements
The table below lists all security and compliance requirements identified for this automation. Items marked 'To confirm' require explicit sign-off from [YourCompany.com] before build begins. Items marked 'Required' are non-negotiable and must be implemented. Items marked 'Implemented' are handled by the FullSpec Automation platform by default.
Area
Requirement
Detail
Status
Authentication
OAuth 2.0 for all integrations
Karbon, Xero, Slack, and Gmail must all authenticate via OAuth 2.0. No plain-text credentials stored in workflow config.
Required
Credential storage
Secrets stored in encrypted vault
All API tokens and refresh tokens stored in FullSpec Automation credential store (AES-256 at rest). Never committed to version control.
Required
Data minimisation
Fetch only necessary fields
Time entries, user IDs, job codes, and billing amounts only. Do not pull personal notes, leave records, or salary data from Karbon.
Required
Data in transit
TLS 1.2 or higher on all API calls
Enforce TLS on every outbound HTTP request. Reject self-signed certificates in production.
Required
PII handling
Team member names and emails treated as PII
Names and email addresses used only for nudge routing. Not logged to persistent storage beyond the nudge_log retention window.
To confirm
Log retention
Nudge and reconciliation logs retained for 90 days
Logs older than 90 days purged automatically. Confirm retention period meets [YourCompany.com] policy and any applicable employment records obligation.
To confirm
Access control
Least-privilege API scopes
Karbon: read time entries and jobs only. Xero: create draft invoices only (no approve, no delete). Slack: send DMs only. Gmail: send only.
Required
Partner approval gate
No auto-post to Xero without human approval
The Reconciliation Agent must pause and await an explicit approval signal before calling the Xero Invoices endpoint. This control must not be bypassed.
Required
Audit trail
All agent actions logged with timestamps
Every Karbon fetch, nudge sent, reconciliation match, and Xero post must be written to the FullSpec Automation run log with actor, action, and timestamp.
Required
Error handling
Failed API calls must not silently drop data
On any 4xx or 5xx response, the agent must log the error, halt the affected sub-flow, and notify the Practice Manager. No silent retries beyond three attempts.
To confirm
Third-party data sharing
No timesheet data sent to external services beyond listed tools
Draft timesheet rows and billing summaries must not be forwarded to any tool not listed in the approved stack (Karbon, Xero, Slack, Gmail, FullSpec Automation).
To confirm
Xero OAuth scope review
Confirm Xero app registration allows draft invoice creation
The registered Xero app at [YourCompany.com] must have accounting.transactions scope granted. Verify no additional scopes are enabled that are not required.
To confirm
Five items carry an 'To confirm' status. These must be resolved with the client before the build begins. Do not proceed past Foundation week without written confirmation on PII handling, log retention, error notification routing, third-party data sharing policy, and Xero scope configuration.
Developer Handover PackPage 3 of 4
FS-DOC-04Technical
05Pre-build checklist
The pre-build checklist is split into two parts. The client ([YourCompany.com]) must provide the items in the first list before the developer begins any integration work. The developer must complete the items in the second list during Foundation week before any agent build commences.
Client to provide: the following items are required from [YourCompany.com] before build start.
Karbon API credentials : Provide an API key or OAuth client ID and secret for the Karbon account. Confirm the account tier supports API access (Karbon Team or higher required).
Xero OAuth app credentials : Create or share an existing Xero connected app with client ID and secret. Confirm the accounting.transactions scope is enabled for draft invoice creation.
Slack workspace access : Add the FullSpec Automation bot to the Slack workspace and grant it the chat:write scope. Provide the Slack user IDs or email-to-Slack mapping for all team members who will receive nudges.
Gmail send account : Provide a Gmail account (or Google Workspace service account) authorised to send nudge emails. Confirm the account is not used for other critical business email to avoid rate-limit conflicts.
Team member roster : Export or provide a list of all team members who submit timesheets, including: full name, email address, Karbon user ID, and Slack user ID (if applicable).
Karbon job code reference : Provide a current export of active job codes and associated client names from Karbon. This is used to configure the job-code matching confidence threshold.
Period close schedule : Confirm the day and time each weekly period closes (e.g. Friday 17:00 local time). Confirm the timezone. This drives the cron trigger.
Partner approval contact details : Confirm the Slack channel or email address where the partner-approval summary should be sent at the end of each reconciliation run.
PII and log retention policy confirmation : Provide written confirmation of the acceptable log retention window (default assumption: 90 days) and any applicable employment records obligations that affect timesheet data storage.
Xero contact IDs for billing clients : Confirm that all clients to be invoiced have existing Contact records in Xero. Provide a mapping of Karbon client names to Xero ContactIDs, or confirm the developer may fetch these via the Xero Contacts API.
Developer to set up: the following items must be completed by the developer during Foundation week before agent build begins.
Provision FullSpec Automation workspace : Create the project workspace in FullSpec Automation. Name it consistently with the process (e.g. 'time-tracking-billable-hours'). Enable run logging and error notifications.
Configure Karbon credential in FullSpec Automation : Add a Karbon API credential entry using the client-provided key or OAuth tokens. Test connectivity with a GET /time-entries request. Confirm pagination handling for large responses.
Configure Xero OAuth credential : Complete the Xero OAuth 2.0 flow via FullSpec Automation. Store access token and refresh token. Set token refresh logic to trigger at least 60 seconds before expiry. Scope: accounting.transactions only.
Configure Slack bot credential : Add the Slack bot token (xoxb-) to FullSpec Automation credentials. Test with a DM to a developer test account. Confirm chat:write scope is the only scope granted.
Configure Gmail send credential : Authorise the Gmail send account via OAuth 2.0 (or service account JSON key for Google Workspace). Test send to a developer inbox. Confirm daily send quota is sufficient for the nudge volume (max 30 emails per run).
Set up development and production environments : Create separate dev and prod workflow copies in FullSpec Automation. Dev environment must use test Karbon, Xero sandbox, and a #test-nudges Slack channel. Never point dev workflows at production Xero.
Validate Karbon job code export against live data : Run a sample fetch of Karbon time entries and jobs. Confirm job_code field is populated on at least 80% of recent entries. Document any inconsistencies for threshold calibration.
Map Karbon client names to Xero ContactIDs : Build or import the client-name-to-Xero-ContactID lookup table. Store as a reference dataset in FullSpec Automation. This is required by the Reconciliation Agent before any billing export can run.
Confirm period close cron schedule : Set the cron trigger in FullSpec Automation using the confirmed day, time, and timezone from the client. Test a manual trigger in the dev environment to confirm all downstream agents fire correctly.
Configure error notification routing : Set up the error handler in FullSpec Automation to notify the Practice Manager via Slack on any agent failure. Confirm the Slack channel name or user ID with the client before go-live.
06Recommended build stack
Tool
Role in automation
Auth method
Cost (to client)
Notes
FullSpec Automation
Orchestration layer, workflow engine, credential vault, run logging
Platform login (SSO available)
$129/month
Hosts all three agents. Provides error alerting and run history.
Karbon
Source of time entries and job data; write target for draft timesheets
OAuth 2.0 (Karbon API key also supported)
$0 additional (existing licence)
Karbon Team tier or higher required for API access. Confirm tier before build.
Xero
Billing export target; draft invoice creation
OAuth 2.0 (Xero connected app)
$0 additional (existing licence)
Sandbox environment available for dev testing at developer.xero.com.
Slack
Primary nudge delivery channel for Gap Chaser Agent; escalation and approval notifications
OAuth 2.0 (bot token, xoxb-)
$0 additional (existing licence)
Requires bot added to workspace by a Slack admin at [YourCompany.com].
Gmail
Fallback nudge delivery for team members without Slack
OAuth 2.0 (or Google Workspace service account)
$0 additional (existing licence)
Confirm daily send quota. Standard Gmail: 500/day. Google Workspace: 2,000/day.
Total estimated build effort
48 hours
Foundation and integrations
8 hours (Week 1)
Time Capture Agent
12 hours (Weeks 1 to 2)
Gap Chaser Agent
14 hours (Weeks 2 to 3)
Reconciliation Agent
14 hours (Week 3)
End-to-end QA and launch
Included in agent build hours; full regression in Week 3
Recommended delivery timeline
3 weeks (Standard build)
Build cost (Standard)
$3,200 one-off
Ongoing tooling cost
$129/month (FullSpec Automation only; all other tools are existing licences)
Payback period
4 months from go-live
The 48-hour build estimate assumes all pre-build checklist items are complete before Week 1 begins and that the client provides Karbon job code data and Xero ContactID mappings without delay. Any gap in credentials or reference data during the build will extend the timeline. A short tuning period of 2 to 3 live cycles is required after go-live to calibrate the job-code matching confidence threshold against real data.
Developer Handover PackPage 4 of 4