Client Health Check
When this applies
- Automatically during
sync {client}or auto-sync (parallel with Gmail/GCal/Attention) - Automatically during
prep me for {client}(uses cached data if fresh) - Explicitly when the user says "health check {client}"
Prerequisites
avoca-client.jsonmust exist in the client folder with anavoca_client_idfield- If no
avoca_client_id, skip the health check silently
Workflow
Step 1 — Get team ID
Read avoca-client.json from the client folder. Extract avoca_client_id. This is the numeric team ID used in all Supabase queries.
If the file doesn't exist or has no avoca_client_id, skip the health check and note: _Health check skipped — no team ID._
Step 2 — Get product context
Read context.md to determine which products the client has (from **Products**: in Quick facts). This determines which sections of the health check to run:
| Product keyword | Sections to check |
|---|---|
| Responder | Core setup, Pre-onboarding, Testing readiness |
| Outbound | Core setup + Outbound-specific |
| Coach | Core setup only |
| Simple Scheduler | Core setup + Simple Scheduler-specific |
| STL / Speed to Lead | Core setup + Outbound-specific (subset) |
If products are blank or unknown, run Core setup + Pre-onboarding + Testing readiness (Responder is assumed default).
Step 3 — Run consolidated SQL query
Run ONE query against Supabase. Replace {TEAM_ID} with the numeric team ID.
- MCP server:
user-Supabase - Tool:
execute_sql - Project ID:
wmizcewjcybhvkpwpmim
SELECT
-- Team basics
t.id AS team_id,
t.name AS team_name,
t.crm,
t.completed_onboarding,
t.features,
t.ai_assistant_id,
t.deactivated_at,
-- ST credentials
(SELECT COUNT(*) FROM service_titan st
WHERE st.team_id = t.id
AND st.tenant_id IS NOT NULL
AND st.client_id IS NOT NULL) AS st_creds_count,
-- Responder webhook config
(SELECT json_agg(json_build_object(
'live', rwc.live,
'assistant_id', rwc.assistant_id,
'uses_booking_windows', rwc.uses_booking_windows
)) FROM responder_webhook_configs rwc
WHERE rwc.team_id = t.id) AS responder_configs,
-- Employee contacts (active associates)
(SELECT COUNT(*) FROM associates a
WHERE a.team_id = t.id AND a.active = true) AS active_associates,
-- Transfer destinations
(SELECT COUNT(*) FROM transfer_destinations td
WHERE td.team_id = t.id AND td.active = true) AS active_transfers,
-- On-call departments
(SELECT COUNT(*) FROM on_call_departments ocd
WHERE ocd.team_id = t.id AND ocd.active = true) AS on_call_depts,
-- On-call ST sync
(SELECT json_build_object(
'enabled', ocs.enabled,
'last_synced_at', ocs.last_synced_at,
'last_sync_error', ocs.last_sync_error
) FROM on_call_service_titan_sync_configs ocs
WHERE ocs.team_id = t.id) AS on_call_sync,
-- Booking windows (arrival windows)
(SELECT COUNT(*) FROM arrival_windows aw
WHERE aw.team_id = t.id) AS booking_windows_count,
-- Service areas
(SELECT COUNT(*) FROM service_areas sa
WHERE sa.team_id = t.id) AS service_areas_count,
-- Dispatch fees
(SELECT COUNT(*) FROM dispatch_fees df
WHERE df.team_id = t.id) AS dispatch_fees_count,
-- Phone numbers
(SELECT json_agg(json_build_object(
'phone_number', pn.phone_number,
'has_vapi', pn.vapi_phone_number_id IS NOT NULL
)) FROM phone_numbers pn
WHERE pn.team_id = t.id) AS phone_numbers,
-- VAPI assistant configs
(SELECT COUNT(*) FROM assistant_configs ac
WHERE ac.team_id = t.id
AND ac.vapi_assistant_id IS NOT NULL) AS vapi_assistant_count,
-- Feature flags for this team
(SELECT json_agg(json_build_object(
'key', fft.flag_key,
'enabled', fft.enabled
)) FROM feature_flag_targets fft
WHERE fft.target_type = 'team'
AND fft.target_id = t.id::integer) AS feature_flags,
-- Knowledge base
(SELECT COUNT(*) FROM knowledge_bases kb
WHERE kb.team_id = t.id) AS knowledge_base_count,
-- CRM sync config (crm_service_titan uses integer team_id)
(SELECT json_agg(json_build_object(
'entity', sc.entity,
'enabled', sc.is_enabled
)) FROM crm_service_titan.sync_config sc
WHERE sc.team_id = t.id::integer) AS crm_sync_config,
-- Outbound text agents
(SELECT COUNT(*) FROM text_conversation_configs tcc
WHERE tcc.team_id = t.id AND tcc.enabled = true) AS outbound_agents_count,
-- Workflows
(SELECT COUNT(*) FROM workflows w
WHERE w.team_id = t.id) AS workflows_count,
-- Campaigns
(SELECT COUNT(*) FROM workflow_campaigns wc
WHERE wc.team_id = t.id) AS campaigns_count,
-- Notifications (uses integer team_id)
(SELECT COUNT(*) FROM notification_configurations nc
WHERE nc.team_id = t.id::integer) AS notification_configs_count,
-- Simple scheduler
(SELECT COUNT(*) FROM simple_scheduler_config ssc
WHERE ssc.team_id = t.id::text) AS simple_scheduler_count,
-- Onboarding completion tracking
(SELECT json_build_object(
'booking_windows_completed', tob.booking_windows_completed,
'service_areas_completed', tob.service_areas_completed
) FROM team_onboarding tob
WHERE tob.team_id = t.id) AS onboarding_status
FROM teams t
WHERE t.id = {TEAM_ID};
Step 4 — Interpret results
Use the query results to build a health check report. Apply these rules:
Core setup (all products):
| Check | Pass condition | Fail message |
|---|---|---|
| Team exists | Row returned, deactivated_at is null |
Team not found / deactivated |
| CRM configured | crm is not null |
CRM not set |
| ST API credentials | st_creds_count >= 1 |
ST credentials not configured |
| Responder webhook | responder_configs is not null and has at least one entry |
No responder config |
| Responder live | Any entry in responder_configs has live = true |
Responder not live |
| VAPI assistant | vapi_assistant_count >= 1 OR ai_assistant_id is not null |
No VAPI assistant linked |
| Products enabled | features array is non-empty |
No products enabled — check features |
Pre-onboarding (Responder, Outbound):
| Check | Pass condition | Fail message |
|---|---|---|
| Service areas | service_areas_count >= 1 |
No service areas configured |
| Booking windows | booking_windows_count >= 1 |
No booking windows configured |
| Fees | dispatch_fees_count >= 1 |
No dispatch fees configured |
| Knowledge base | knowledge_base_count >= 1 |
Knowledge base not populated |
| Phone numbers | phone_numbers is not null |
No phone numbers provisioned |
Testing readiness (Responder):
| Check | Pass condition | Fail message |
|---|---|---|
| Employee contacts | active_associates >= 1 |
No employee contacts configured |
| Transfer destinations | active_transfers >= 1 |
No transfer destinations configured |
| On-call calendar | on_call_depts >= 1 |
No on-call calendar configured |
| On-call ST sync | on_call_sync.enabled = true and last_sync_error is null |
On-call sync not enabled / has errors |
Outbound-specific:
| Check | Pass condition | Fail message |
|---|---|---|
| CRM sync enabled | crm_sync_config has entries with enabled = true |
CRM sync not enabled |
| Outbound agent | outbound_agents_count >= 1 |
No outbound agent configured |
| Workflows | workflows_count >= 1 |
No workflows created |
| Campaigns | campaigns_count >= 1 |
No campaigns created |
| Notifications | notification_configs_count >= 1 |
No notifications configured |
Simple Scheduler-specific:
| Check | Pass condition | Fail message |
|---|---|---|
| SS config | simple_scheduler_count >= 1 |
Simple Scheduler not configured |
| SS feature flag | feature_flags contains simple_scheduler with enabled = true |
Feature flag not enabled |
Step 5 — Write to context.md
Write the results into the ## System health section of context.md. Replace the entire section contents each time.
Format — use [x] for passing checks, [ ] for failing, with counts/details in parentheses:
## System health
<!-- health_checked: {ISO 8601 timestamp} -->
### Core setup
- [x] Team exists (ID: 2297, Name: OHA)
- [x] CRM: ServiceTitan
- [x] ST API credentials configured
- [x] Responder webhook configured (live)
- [x] VAPI assistant linked
- [x] Products: Responder, Outbound
### Pre-onboarding
- [x] Service areas (3 configured)
- [x] Booking windows (12 configured)
- [x] Fees (2 dispatch fees)
- [ ] Knowledge base — not populated
- [x] Phone numbers (2 provisioned, 1 with VAPI)
### Testing readiness
- [x] Employee contacts (4 active)
- [x] Transfer destinations (2 active)
- [ ] On-call calendar — no departments configured
- [ ] On-call ST sync — not enabled
### Outbound
- [x] CRM sync enabled (customers, jobs, invoices)
- [ ] Outbound agent — not configured
- [ ] Workflows — none created
- [ ] Campaigns — none created
- [ ] Notifications — not configured
Only include product-specific sections when the client has that product.
Step 6 — Surface in prep/sync
When this runs as part of prep me for {client}:
- Include a "Missing config" section in the brief listing only the unchecked items
- Frame them as talking points: "You may want to address: on-call calendar, knowledge base"
When this runs as part of sync {client}:
- Write silently to
context.md - Only mention in response if there are notable changes (e.g., something that was missing is now configured)
When run standalone (health check {client}):
- Show the full health check report to the user
- Highlight any items that need attention
Error handling
- If the Supabase query fails, skip the health check and note:
_Health check failed — Supabase query error._ - If a specific sub-query returns unexpected data, mark that check as
[?] {check} — could not verifyand continue - Never block the sync/prep workflow because of a health check failure
Caching
The <!-- health_checked: ... --> timestamp controls freshness:
- During auto-sync: always re-run (it's one cheap query)
- During prep: use cached data if
health_checkedis within 6 hours; otherwise re-run - Standalone
health check: always re-run (fresh data)