Local development
Prerequisites
-
Node 20 (
.nvmrc) -
pnpm
-
A Supabase project — PostgreSQL and Auth
-
Redis, for the in-flight call counter
Setup
git clone <repository-url>
cd dialer-v2
nvm use
pnpm install
Create .env (git-ignored) with at minimum:
DATABASE_URL=
DIRECT_URL=
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY=
REDIS_URL=redis://localhost:6379
Then add integrations for whatever you are working on — see Environment variables.
pnpm prisma generate
pnpm prisma migrate deploy # or `pnpm prisma migrate dev` against a scratch database
pnpm dev
Visit http://localhost:3000.
Signing in
Signup requires a @payability.com address (or a subdomain) — isAllowedPayabilityEmail() in middleware
and a Supabase auth hook both enforce it. A different domain is signed out immediately with
?error=forbidden_domain.
POST /api/auth/register creates the Profile, Subscription and owner SubscriptionUser in one step, so a
fresh signup lands in a working workspace.
What works without every integration
| Missing | Consequence |
|---|---|
|
Browsing works; dialing fails at the Retell step and the target is marked |
|
Email campaigns can be configured but not sent |
|
Call analysis, variation generation, prompt suggestions and test runs all fail |
|
CRM publishes silently no-op; watch the logs |
BigQuery credentials |
Sequence data gates fail; other sequence channels still work |
|
Broadcast throttling misbehaves — fix this before running a campaign locally |
|
Broadcasts return |
The dashboard, contacts, registries, campaign and sequence configuration, and reporting (against whatever data exists) all work with just the database and Supabase.
Triggering work manually
Nothing schedules itself locally — there is no pg_cron. Invoke the loops by hand:
# one dial tick
curl -X POST http://localhost:3000/api/campaigns/<campaignId>/broadcast \
-H "Authorization: Bearer $INTERNAL_API_SECRET" \
-H "Content-Type: application/json" -d '{}'
# one email tick
curl -X POST http://localhost:3000/api/email-campaigns/<campaignId>/broadcast \
-H "Authorization: Bearer $INTERNAL_API_SECRET" \
-H "Content-Type: application/json" -d '{}'
The daily resets and the phone-number optimizer are Inngest cron functions
(phoneNumberDailyResetCron, sendingDomainDailyResetCron, phoneNumberOptimizerCron). Run them
with Invoke in the Inngest dev server UI (see below).
Inngest locally
Workflows need the Inngest dev server:
npx inngest-cli@latest dev -u http://localhost:3000/api/inngest
Its UI (usually http://localhost:8288) shows runs, step results and lets you replay. Without it, events are
sent and never consumed — which looks exactly like a broken workflow, so check the dev server first.
Replaying a webhook
Real payloads are already in the database:
select "eventType", "rawPayload"
from "WebhookEvent"
where source = 'Retell' and "eventType" = 'call_analyzed'
order by "receivedAt" desc
limit 1;
curl it back at the endpoint. The WebhookEvent upsert is idempotent on (source, eventId), so the row is
not duplicated, but the handler runs again — which is the point.
Data safety
.env typically points at a shared Supabase project. Assume writes are real:
-
Set
CampaignTarget.isTestTarget = trueon anything you plan to dial, which excludes it from CRM hand-off. -
Prefer
draftcampaigns; onlyrunningcampaigns broadcast. -
pnpm prisma migrate resetdestroys data — never run it against a shared database. -
Test email sends go to real inboxes and count against real daily caps.
The in-app docs site
The Fumadocs site at /docs is built from the .mdx files directly under docs/
(source.config.ts, lib/source.ts, app/docs/). It is public — no session required — and hot-reloads
in development.
This Antora component lives under docs/modules/ and is not part of that site. To preview it, point an
Antora playbook at the repository with start_path: docs; see
About this documentation.
Common setup problems
| Symptom | Fix |
|---|---|
|
|
Type errors on Prisma models after a schema edit |
|
Stale build after dependency changes |
|
Redirected to |
Missing or wrong Supabase anon config, or a non-Payability email |
Events sent but no workflow runs |
The Inngest dev server is not running |