Turn a recorded discovery call into a branded, sendable proposal in ~4 minutes.
An open-source AI workflow that connects to your meeting recorder, extracts the deal from the transcript, and generates a fully-branded Quote + Statement of Work as PDFs — ready for e-signature.
Meeting webhook ─► extract (Haiku 4.5) ──► pricing-readiness gate
(Read.ai / │ │
Fathom / ▼ ▼ (not ready: stop)
Fireflies) generate (Sonnet 4.6) confirmation tool
│
▼
critique (Sonnet 4.6) ──► passes? proceed : revise once
│
▼
render PDF (React-PDF) ──► Supabase Storage (signed URL)
│
▼
/d/[id] dashboard ──► View Proposal · Send · Delete · Edit name
│
▼
Send-for-signature (CLI today; in-product v2)
DocuSign · PandaDoc · SignWell · BoldSign · OpenSign · Wet Sign
Cost per proposal: ~$0.30–0.50 in LLM spend. ~10–12 page branded output. Inngest-orchestrated, durable, retriable.
- Real production pipeline. Webhook signature verification, idempotent ingestion (unique partial index on
(source, external_id)), atomic draft writes via Postgres RPC, signed-URL serving for private PDFs. Not a toy. - Schemas as the contract. Every LLM call validates against a Zod schema. Failed validation triggers a retry-with-temperature-nudge before failing the row. No "hope the JSON parses" coding.
- Eval-driven prompt iteration. 5 scorers (schema validation, required fields, length discipline, pricing consistency, LLM-judge) run against a golden set. CI eval-regression gate fails any merge that drops avg score > 2%.
- Operator-tunable templates. Each vertical has a TypeScript template module, but pricing tiers, services catalog, ROI anchors, branding, and terms clauses all live in an editable
/settingspage that feeds the LLM prompt at runtime. No code commit needed to tweak your Pilot price. - 6 e-signature providers + 3 meeting recorders + multi-tenant-ready schema (single-tenant in v1;
tenant_idcolumn on every table; RLS placeholder policies). - Adversarial-review workflow. Every plan goes through
/plan-eng-review: architecture/code-quality/tests/performance sections + outside-voice cross-model challenge via Claude subagent + Codex (when available). Past review reports live indocs/.
| Path | Purpose |
|---|---|
/ |
Discovery calls list. Status filter chips, inline client-name edit, View Proposal / Send / Delete per row. |
/d/[id] |
Per-call detail. Status-aware: queued / processing → live polling stepper; ready → PDF download + (v1.1) inline edit composer. |
/d/new |
Manual paste — pipe any transcript through the same Inngest function the webhook uses. |
/connections |
Meeting recorders (Read.ai / Fathom / Fireflies) + e-sign providers (DocuSign / PandaDoc / SignWell / BoldSign / OpenSign / Wet Sign). |
/connections/{provider} |
Per-provider detail with env-var pills, "Test connection" or "Send test event" button, signed-cookie session, in-process rate limit. |
/settings |
7-section operator config: Brand, Operator, Pricing, Services, Scope, Terms, ROI. File upload for logo + signature image. Editable list of pricing tiers (1–4) with services catalog cross-referencing them. |
- Read.ai — webhook + signed transcript ingestion
- Fathom — webhook + signed transcript ingestion
- Fireflies — webhook + signed transcript (inline-transcript toggle required; GraphQL fetch is v1.1)
| Provider | Cost (approx) | Auth | Notable |
|---|---|---|---|
| DocuSign | $25+/mo | JWT-grant access token | Market leader, enterprise features |
| PandaDoc | $19+/mo | API key | Document workspace + templates |
| SignWell | $8/mo | API key | Cheapest hosted option |
| BoldSign | $10/mo | API key (US + EU regions) | Cost-effective with region choice |
| OpenSign | Free self-hosted | Parse-style (App Id + REST Key) | Open source; cloud option ~$10/mo |
| Wet Sign | $0 | none | Signed-locally PDF, operator emails manually. Always-available fallback. |
selectProvider() resolves the active provider in env-pinned → DocuSign → PandaDoc → … → Wet Sign order.
- Next.js 16 App Router on Vercel (Fluid Compute)
- Inngest for durable workflow orchestration (extract → gate → generate → critique → revise → render → upload)
- Supabase Postgres with RLS-ready schema; service-role admin client server-side
- Supabase Storage for PDFs (
quotes-sows, private + signed-URL serving) and brand assets (brand-assets, public for logo/signature URLs in rendered PDFs) - Vercel Blob for ephemeral pipeline-state polling JSON
- Vercel AI SDK +
@ai-sdk/anthropicfor LLM calls;generateObjectwith Zod schemas for structured output - Langfuse for LLM observability (lazy-singleton with zero-overhead passthrough when env keys absent)
- React-PDF for proposal PDFs
- Zod for every schema boundary
- Vitest for tests (currently 421 passing)
transcripts ──┐
├── 1:N drafts (current proposal lives here; is_latest=true)
└── 1:N transcript_events (audit log; ON DELETE CASCADE)
operator_settings ── 1 row per tenant_id (JSONB; 7-section operator config)
Migrations in supabase/migrations/0001…0004_*.sql are declarative and idempotent.
git clone https://github.com/janderswag/proposal-agent.git
cd proposal-agent
npm installYou'll need accounts for:
- Anthropic (Claude API)
- Supabase (Postgres + Storage)
- Vercel (hosting + Blob for state-store) — optional for CLI mode
- Inngest (function orchestration) — optional for CLI mode
cp .env.local.example .env.local
# Fill in:
# ANTHROPIC_API_KEY=sk-ant-...
# NEXT_PUBLIC_SUPABASE_URL=https://<your-project>.supabase.co
# SUPABASE_SERVICE_ROLE_KEY=...
# Apply migrations (Supabase SQL editor or migration runner)
# Provision Storage:
npm run supabase:storage:setupSave a meeting transcript to transcript.txt (any plain-text format). Then:
npm run prepare-proposal -- /path/to/transcript.txtYou'll get a PDF + an email draft in outputs/. The pipeline auto-opens the PDF in macOS Preview.
npm run dev # Next.js dev server on :3000
npm run inngest:dev # Inngest dev server on :8288 (separate terminal)Then:
- Open
http://localhost:3000— the discovery-calls list (empty initially) - Open
http://localhost:3000/d/new— paste a transcript, click Generate, watch the pipeline - Open
http://localhost:3000/settings— tune your branding + pricing tiers + services catalog - Open
http://localhost:3000/connections— wire up Read.ai / Fathom / Fireflies webhooks + e-sign providers
Push to a Vercel-connected repo:
- Vercel auto-detects Next.js
- Add
ANTHROPIC_API_KEY,NEXT_PUBLIC_SUPABASE_URL,SUPABASE_SERVICE_ROLE_KEY,INNGEST_EVENT_KEY,INNGEST_SIGNING_KEY,CONNECTION_TEST_SECRETto Vercel env - Connect Inngest to your Vercel deployment (Inngest cloud → New App → enter
/api/inngestURL) - Click
Send test eventon/connections/read-aito validate the wire-up
app/
api/
connections/{provider}/test/ POST: test-connection routes (signed cookie + rate limit)
transcripts/[id]/ GET/PUT/DELETE: row CRUD
settings/ GET/PUT: operator settings persistence + file upload
webhooks/{read-ai,fathom,fireflies}/ Inbound webhook routes (HMAC-verified)
inngest/ Inngest serve endpoint
proposal-pdf/[…]/ Signed-URL minting for private PDFs
generate/ Manual-paste pipeline entry
components/dashboard/ Header, TranscriptRow, modals, overflow menu
connections/ Server-rendered Connections pages
d/[id]/ Per-call detail surface
d/new/ Manual-paste UI
settings/ Editable Settings page + ImageUploadField
lib/
schemas/ Zod: MeetingExtract, ProposalDraft, CritiqueReport, OperatorSettings
prompts/ Versioned system + user prompts (extract, generate, critique)
pipelines/ Pipeline step functions + Inngest orchestrator + CLI runner
templates/ Per-vertical TemplateModule factories (ai_automation, professional_services)
Settings-driven prompt composition via compose.ts
render/ React-PDF components + design tokens
esign/ Provider-agnostic envelope-send interface
ingest/ Per-provider adapters (Read.ai, Fathom, Fireflies)
dashboard/ Transcript repo, state-store, queries, settings repo
observability/ Langfuse wrapper (lazy singleton)
notify/ Slack webhook + email draft builder
eval/ Runner + scorers + golden set
docs/
design.md v1 design doc
settings-v1-plan-2026-05-25.md Settings page plan (post /plan-eng-review)
connections-v1-plan-2026-05-25.md Connections page plan
coding-guidelines.md House rules
supabase/migrations/
0001_initial_schema.sql transcripts + drafts + events
0002_webhook_idempotency.sql unique partial index on (source, external_id)
0003_create_draft_atomic.sql atomic demote-then-insert RPC
0004_operator_settings.sql operator_settings JSONB table
tests/evals/ Golden-set (transcript, ideal_proposal) pairs
.github/workflows/eval-gate.yml CI gate — fails merge on > 2% eval-score drop
| Command | What it does |
|---|---|
npm run prepare-proposal -- <transcript.txt> |
CLI pipeline end-to-end |
npm run dev |
Next.js dev server |
npm run inngest:dev |
Inngest dev server |
npm run typecheck |
tsc --noEmit |
npm test |
Vitest (~421 tests, ~1s) |
npm run lint |
ESLint |
npm run eval |
Eval suite against tests/evals/ (real Sonnet calls) |
npm run eval:gate |
Eval + baseline-regression gate (used in CI) |
npm run eval:baseline |
Capture current eval scores as the new baseline |
npm run calibrate-threshold |
One-off script: critique-score distribution sweep for the v1.1 needs-review threshold |
npm run supabase:storage:setup |
Provision the quotes-sows + brand-assets Storage buckets |
npm run send-for-signature -- <draftId> |
CLI: send a rendered proposal via the configured e-sign provider |
- Schemas first. Never write LLM-calling code before the Zod schema exists. The schema is the contract — generate, validate, retry, repair.
- Eval before prompt. Never ship a prompt change without running the eval suite. CI fails on > 2% regression. The threshold is in
tests/evals/baseline-scores.json. - Single source of truth for pricing. Pricing lives in
ProposalDraft.investment.tiers— every section that references prices pulls from there at render time. No hardcoded prices in template prose. - Templates are data. Per-vertical structural prose lives in
lib/templates/{vertical}.ts. Operator-tunable values (tiers, roles, anchors, terms) live inoperator_settings. Templates compose the two at prompt-build time vialib/templates/compose.ts. - Prose only in slots. The LLM writes inside section slots. Section ordering, structure, and presence are deterministic code.
- Treat transcripts as untrusted. Webhook payloads wrapped in
<transcript>delimiters. Transcript content never flows into tool-use decisions. - Human in the loop, always. No proposal sends without operator approval. Send-for-signature is a deliberate operator action.
- No browser storage in artifacts. No localStorage/sessionStorage anywhere in the pipeline.
- Boil the lake when AI makes it cheap. Test coverage, edge cases, error paths — the marginal cost is near-zero, so do the complete thing.
| Metric | Value |
|---|---|
Commits on main |
55+ |
| Tests | 421 passing |
| LLM cost per proposal | ~$0.30–0.50 |
| End-to-end latency (P95) | < 5 minutes |
| Meeting-recorder integrations | 3 (Read.ai, Fathom, Fireflies) |
| E-sign provider surfaces | 6 (DocuSign, PandaDoc, SignWell, BoldSign, OpenSign, Wet Sign) |
| Postgres migrations | 4 (initial + idempotency + atomic-RPC + operator-settings) |
| Eval golden set | 15 cases (10 synthetic + 5 anonymized real) |
| Vertical templates | 2 registered (ai_automation, professional_services); home_services + b2b_agency enum-only |
- Auto-send proposals. Human approval required, always.
- Dashboard-initiated Send-for-signature. CLI works today; in-product button is v2.
- Payment collection. v3+.
- Multi-tenancy. Single-tenant v1;
tenant_idcolumns + RLS placeholders ready for v2 Clerk integration. - Mobile UI. Web only.
- Client-facing portals / CRM features. Not the product.
- RAG over past proposals. v3.
If a feature isn't on this list, it's not in v1. Don't argue with the doc; argue with the doc before writing code.
This project ships under the philosophy that every plan goes through /plan-eng-review and every PR goes through /review — both are skills in this repo's gstack toolset. The plan-review workflow surfaces architecture-level catches before code lands; the PR-review workflow runs adversarial passes (Claude subagent + Codex when available) on the diff.
If you want to contribute:
- Open an issue describing the problem
- Wait for a design discussion (or write a design doc yourself)
- Run
/plan-eng-reviewagainst the design before implementing - Run
/reviewagainst the PR before opening
See docs/ for past plan + review artifacts.
MIT. Use it, fork it, embed it in your own AI agency tooling. PRs welcome.
Built with care by @janderswag for Hawkify, an AI implementation agency. The dogfood scenario: every Hawkify discovery call produces a draft proposal automatically. The open-source goal: any operator running a service business should be able to spin this up against their own Anthropic / Supabase / Inngest stack and have the same workflow.