Clients API
All endpoints require authentication and clients tool permissions.
List Clients
Section titled “List Clients”GET /api/clientsQuery parameters:
| Param | Type | Description |
|---|---|---|
status | string | Filter by status (comma-separated: prospect,active,dormant,alumni) |
tier | string | Filter by tier (comma-separated: platinum,core,standard,review) |
assigned_dm_id | string | Filter by assigned delivery manager user ID |
health_score_min | number | Minimum health score |
health_score_max | number | Maximum health score |
search | string | Search by company name |
sort | string | Sort field: health_score (default), name, tier, last_qbr_date |
page | number | Page number (default: 1) |
per_page | number | Items per page (default: 50) |
Response: { clients, total, page, pages }
Each client includes computed is_qbr_overdue, qbr_overdue_days, is_score_stale, tier_track, tier_score, and tier_scored_at fields.
Get Client Stats
Section titled “Get Client Stats”GET /api/clients/statsReturns badge counts for the sidebar: { overdue_qbr_count, low_health_count }
Promote Company to Client
Section titled “Promote Company to Client”POST /api/clientsBody:
{ "company_id": "string (required)", "status": "string (default: prospect)", "assigned_dm_id": "string (optional)"}Get Client Detail
Section titled “Get Client Detail”GET /api/clients/:companyIdReturns { client, stats, contacts, group, parent_client } where:
statsincludesactive_projects,open_deals,total_revenue,meetings_count,contacts_count,last_meeting_dateclientincludes all tier scoring fields:tier_track,tier_score,tier_revenue_score,tier_profitability_score,tier_strategic_score,tier_relationship_score,tier_growth_score,tier_adoption_score,tier_revenue_override,tier_override_reason,tier_scored_at,tier_scored_by,tier_scored_by_name,is_score_stalegroupcontainschildrenandaggregatedstats for parent companiesparent_clientis set if this company is a child in a group
Update Client Profile
Section titled “Update Client Profile”PATCH /api/clients/:companyIdBody: Any combination of status, assigned_dm_id, next_qbr_date, last_qbr_date, onboarding_started_at, onboarding_completed_at, offboarding_started_at, offboarding_completed_at.
Writes appropriate history events for each changed field.
Update Tier Score
Section titled “Update Tier Score”PUT /api/clients/:companyId/tier-scoreSubmits human dimension scores, auto-calculates revenue, computes weighted tier score, and derives tier assignment.
Body:
{ "tier_track": "delivery | product (required)", "profitability_score": "number 1-5 (Track A only)", "strategic_score": "number 1-5 (Track A only)", "relationship_score": "number 1-5 (both tracks)", "growth_score": "number 1-5 (Track B only)", "revenue_override": "number 1-5 or null", "override_reason": "string (required if override is set)"}Response:
{ "tier": "platinum | core | standard | review", "tier_score": 4.25, "tier_track": "delivery", "revenue": { "annual_total": 285000, "score": 4, "source": "nucleus | productive | none", "override": null }, "dimensions": { "revenue": 4, "profitability": 3, "strategic": 4, "relationship": 5 }}On tier change: auto-sets next_qbr_date based on tier cadence, creates a notification for the assigned DM, and logs a history entry.
Get Tier History
Section titled “Get Tier History”GET /api/clients/:companyId/tier-historyReturns { history } — array of scoring events for trend charts. Each entry includes all dimension scores, tier, source (manual or auto), and scorer name.
Get Revenue Summary
Section titled “Get Revenue Summary”GET /api/clients/:companyId/revenue-summaryReturns { annual_total, revenue_score, source, monthly } where monthly is an array of { month, total } for the last 12 months.
Recalculate Revenue Scores (Admin)
Section titled “Recalculate Revenue Scores (Admin)”POST /api/clients/recalculate-revenueRequires clients:manage permission. Recalculates revenue scores for all active clients with a tier track set. Returns { updated, tier_changes }.
Also runs automatically via cron on the 1st of each month at 2am UTC.
Record Health Score
Section titled “Record Health Score”POST /api/clients/:companyId/health-scoresBody: { score: number (0-100), factors?: string[], assessed_by?: string }
List QBRs
Section titled “List QBRs”GET /api/clients/:companyId/qbrsReturns { qbrs } sorted by date descending.
Record QBR
Section titled “Record QBR”POST /api/clients/:companyId/qbrsBody: { date, attendees?, summary?, outcomes?, next_qbr_date?, granola_meeting_id? }
List Goals
Section titled “List Goals”GET /api/clients/:companyId/goalsCreate Goal
Section titled “Create Goal”POST /api/clients/:companyId/goalsBody: { metric, target, baseline?, unit?, period? }
Update Goal Progress
Section titled “Update Goal Progress”PATCH /api/clients/:companyId/goals/:goalIdBody: { current_value: number }
Get Client History
Section titled “Get Client History”GET /api/clients/:companyId/historyReturns history events with resource_type = "client".
Generate AI Summary
Section titled “Generate AI Summary”POST /api/clients/:companyId/ai-summaryGenerates a relationship summary using Workers AI (Kimi K2.5) and caches it. Returns { ai_summary, ai_summary_generated_at }.
QBR Detail Endpoints
Section titled “QBR Detail Endpoints”Get QBR Detail
Section titled “Get QBR Detail”GET /api/clients/:companyId/qbr/:qbrIdReturns { qbr } with parsed JSON fields: content, source_data, portal_visibility.
Update QBR
Section titled “Update QBR”PATCH /api/clients/:companyId/qbr/:qbrIdBody: { summary?, outcomes?, content?, portal_visibility? }
Cannot edit a QBR with status presented.
Mark QBR as Presented
Section titled “Mark QBR as Presented”POST /api/clients/:companyId/qbr/:qbrId/presentSets QBR status to presented, updates client_profiles.last_qbr_date and optionally next_qbr_date.
AI QBR Prep
Section titled “AI QBR Prep”POST /api/clients/:companyId/qbr/prepareGenerates a draft QBR using AI from context since the last QBR (or 90 days). Creates a QBR record with status draft, including structured content and source data. Returns { qbr }.
Stripe Integration
Section titled “Stripe Integration”Search Stripe Customers
Section titled “Search Stripe Customers”GET /api/clients/:companyId/stripe/search?q=querySearches connected Stripe account for customers matching the query. Returns { customers }.
Link Stripe Customer
Section titled “Link Stripe Customer”POST /api/clients/:companyId/stripe/linkBody: { stripe_customer_id: string }
Links a Stripe customer to the company for revenue tracking. Verifies customer exists first.
Unlink Stripe Customer
Section titled “Unlink Stripe Customer”DELETE /api/clients/:companyId/stripe/linkRemoves the Stripe customer link from the company.
Success Profile
Section titled “Success Profile”Get Success Profile
Section titled “Get Success Profile”GET /api/clients/:companyId/success-profileReturns { profile, questionnaire_completed }.
Update Success Profile
Section titled “Update Success Profile”PATCH /api/clients/:companyId/success-profileBody: { financial_year_start?, financial_year_end?, key_trading_periods?, channel_online_pct?, channel_instore_pct?, channel_wholesale_pct?, celebration_vision?, growth_priorities_text?, company_direction_text? }
Creates or updates the success profile. On first completion, notifies the assigned DM.
List Wins
Section titled “List Wins”GET /api/clients/:companyId/winsQuery parameters:
| Param | Type | Description |
|---|---|---|
since | string | Filter wins after this date |
until | string | Filter wins before this date |
status | string | Filter by status (default: excludes dismissed) |
Returns { wins } with linked project and goal info.
Create Win
Section titled “Create Win”POST /api/clients/:companyId/winsBody: { title, description?, outcome_text?, project_id?, metric_key?, metric_delta?, metric_delta_unit?, won_at?, linked_goal_id?, source?, status? }
Source defaults to manual, status defaults to confirmed.
Update Win
Section titled “Update Win”PATCH /api/clients/:companyId/wins/:winIdBody: { title?, description?, outcome_text?, metric_delta?, metric_delta_unit?, linked_goal_id?, status?, snoozed_until? }
Delete Win
Section titled “Delete Win”DELETE /api/clients/:companyId/wins/:winIdRequires clients:manage permission.
Results Endpoints
Section titled “Results Endpoints”Get Aggregated Results
Section titled “Get Aggregated Results”GET /api/clients/:companyId/resultsQuery parameters:
| Param | Type | Description |
|---|---|---|
period_type | string | week, month (default), quarter, year |
period_end | string | End date (ISO, default: today) |
Response: { period, prior_period, metrics[], insight, connected_platforms[] }
Each metric includes value, avg_value, delta_pct, target, tracking_status (on_track/at_risk/off_track).
The insight object includes content (free text), content_json (structured analysis JSON), health_score, and overall_health when available.
Get Campaign Breakdown
Section titled “Get Campaign Breakdown”GET /api/clients/:companyId/results/campaignsQuery parameters:
| Param | Type | Description |
|---|---|---|
period_type | string | week, month (default), quarter, year |
period_end | string | End date (ISO, default: today) |
platform | string | Filter: meta_ads, google_ads, tiktok_ads |
sort_by | string | Sort column (default: spend) |
sort_dir | string | asc or desc (default) |
Response: { period, campaigns[], totals }
Each campaign includes: campaign_id, campaign_name, platform, campaign_status, campaign_objective, spend, roas, cpa, conversions, conversion_value, reach, frequency, ctr, cpm, cpc, top_creative_title, top_creative_body, spend_delta_pct.
Get Campaign Daily Data (Sparklines)
Section titled “Get Campaign Daily Data (Sparklines)”GET /api/clients/:companyId/results/campaigns/dailySame query parameters as campaign breakdown. Returns { daily } — a map of campaign_id to Array<{ date, spend }> for sparkline rendering.
Ask About Results
Section titled “Ask About Results”POST /api/clients/:companyId/results/askBody: { question: string }
Response: { answer: string }
Prepare QBR from Results
Section titled “Prepare QBR from Results”POST /api/clients/:companyId/results/prepare-qbrGathers current quarter metrics and latest AI insight, creates a QBR record. Requires clients:update.
Response: { qbr: { id, date, summary } }
Backfill Historical Data
Section titled “Backfill Historical Data”POST /api/clients/:companyId/connections/backfillBody: { days: 30 | 60 | 90 }
Triggers async backfill of campaign-level and account-level metrics for all connected ad platforms. Rate-limited to one backfill per company at a time.
Response: 202 Accepted with { message }
Trigger Manual Sync
Section titled “Trigger Manual Sync”POST /api/clients/:companyId/connections/syncTriggers async sync of all connected platforms for the company. Returns 202 Accepted.