API reference
Every endpoint lives under app/api/ as a Next.js route handler. There is no other HTTP surface.
Authentication
Three schemes, depending on the caller.
| Caller | Scheme | Endpoints |
|---|---|---|
Dashboard (browser) |
Supabase session cookie, resolved by |
Everything except webhooks, cron, broadcast and MCP |
Internal machine ( |
|
|
External AI orchestrator |
|
|
Provider webhook |
Provider signature (Svix, HubSpot HMAC, optional Nooks secret) |
|
verifyInternalSecret() fails closed: an unset INTERNAL_API_SECRET rejects every request rather
than allowing them through.
Tenant scoping
Most endpoints are scoped to the active workspace, resolved in this order:
-
x-subscription-idrequest header — an explicit override -
active_subscription_idcookie, set only byPOST /api/subscriptions/active
Membership is verified against SubscriptionUser on every request. See
Multi-tenancy.
Casing
Requests and responses use snake_case. The database uses camelCase, and client TypeScript uses
camelCase again via Zod inference — the snake_case shape exists only on the wire.
A handful of older list responses still leak camelCase keys (for example isAmazonSeller and
meetsMonthlyThreshold on campaign targets). Where a page documents a response, its keys are as the
route actually emits them.
Pagination
Standard envelope:
{ "data": [], "total": 0, "page": 1, "pageSize": 25 }
Query parameters are page (1-based) and pageSize, with per-endpoint maxima (commonly 100, 200 or
500). Some newer endpoints use page_size instead of pageSize — sequences, contact sequences and
emails do. Each page notes which.
Responses that carry aggregate context add sibling keys next to data, for example stats on
follow-up tasks and totals on daily stats, so a UI never has to re-sum the page.
Date filters
dateFrom / dateTo (and their snake_case variants) take YYYY-MM-DD and are anchored to
America/New_York — midnight and 23:59:59 respectively — by parseDateInNY in lib/utils.ts. This
is DST-aware and uses no external library. dateFrom=2026-01-15 becomes 2026-01-15T05:00:00Z
during EST.
Timestamps in responses are UTC ISO 8601. Call list and detail views render them in Eastern Time with the zone abbreviation so the display matches the filter.
Errors
{ "error": "Human readable message" }
| Status | Meaning |
|---|---|
|
Zod validation failed, or a required parameter is missing |
|
No session, or a bad bearer secret |
|
Authenticated but not a member of the workspace, or insufficient role |
|
Resource not found, or not visible in this workspace |
|
Unexpected error — the detail is logged, not returned |
Internal error messages are deliberately generic ("Unable to complete operation"); the cause goes
to the logs and to Sentry.
Endpoint index
| Area | Page |
|---|---|
Register, sign in/out, current user, workspaces and members |
|
Campaigns, targets, coverage, broadcast |
|
Calls, history, analysis, batch re-analysis |
|
AI agents, versions, analysis results, issues, test cases, Retell voices |
|
Contacts, sellers, phone and email registries, target list |
|
Email campaigns, recipients, variations, duplication |
|
Templates, variation library, domains, sending accounts, unsubscribe |
|
Email inbox and threads |
|
Sequence templates and enrollments |
|
Sales reps, follow-up tasks |
|
Outbound phone numbers |
|
Reports, activity feed |
|
Provider webhooks, cron jobs, Inngest, internal endpoints |
|
Model Context Protocol tool server |