Inspiration
Informal agreements happen where independent freelancers communicate: WhatsApp, Slack, DMs, and email threads. Turning those sign-off messages into an official invoice requires toggling between chat apps and the PayPal dashboard, manually re-typing line items, looking up payment terms, and calculating totals.
The biggest friction isn't just the typing—it's the fear of costly mistakes. A wrong currency code, an overlooked milestone price, or accidentally triggering an automated invoice email to an unprepared client damages professional relationships. Most "AI invoice" tools act as reckless black boxes that guess missing details or trigger external financial actions autonomously. We wanted to build an agentic commerce tool that respects freelancer sovereignty: AI prepares, links source evidence, and flags ambiguities—while the human approves every money-moving and client-facing gate.
## What it does Freelance Invoice Copilot guides freelancers through a transparent, guarded transaction lifecycle:
1. **Extraction with Character-Level Provenance Spans:**
* Paste an informal client agreement or project sign-off message.
* The copilot extracts client billing details, multi-deliverable
line items, amounts, currency, and payment terms or due dates.
* **Zero Guessing:** Every extracted field is linked to its exact
character offset (`sourceSpan: { start, end, text }`) in the
original message. Selecting any field in the review form highlights
the exact quote in the source text.
* **Explicit Clarification:** Ambiguous or unpriced items (such as
`"$20/month"` recurring maintenance, `"revisions included"`, or
missing currencies/payment terms) are never hallucinated or silently
dropped—they are surfaced as required clarification questions before
draft creation is allowed.
2. **Gate 1 — Create PayPal Draft (`POST /v2/invoicing/invoices`):**
* After reviewing or editing the extracted fields, the freelancer
explicitly approves **Gate 1**.
* Pending form edits are auto-saved, validated against strict
schemas, and sent to PayPal Invoicing v2 with a deterministic
`PayPal-Request-Id` idempotency key.
* PayPal creates an editable `DRAFT` invoice and returns its
official invoice ID without emailing or notifying the client.
3. **Gate 2 — Send Invoice to Client (`POST/v2/invoicing/invoices/{id}/send`):**
* Requires an existing PayPal draft and a second, separate human
confirmation dialog summarizing the recipient, total amount, and
payment terms.
* Once confirmed, PayPal transitions the invoice to `SENT` and
provides the hosted payer payment URL (`recipient_view_url`) and
invoicer dashboard link.
4. **Gate 3 — Follow-up & Reminder Copilot (`POST
/v2/invoicing/invoices/{id}/remind`):**
* For unpaid `SENT` invoices, the copilot drafts contextual
follow-up messages across three selectable tones (`Gentle`,
`Professional`, `Urgent`) grounded in the invoice's exact
deliverables, total amount, and due date.
* Requires explicit **Gate 3** confirmation before calling PayPal's
`/remind` endpoint, enforces PayPal's maximum of **2 reminders per
day** to prevent client spam, and automatically locks once the
invoice status reaches `PAID`.
5. **Zero-Friction Judge Evaluation (Recorded Sandbox Simulation
Mode):**
* Out of the box, judges can run `npm start` and evaluate the entire
3-gate workflow—including Gate 3 reminders and simulating buyer
payment (`SENT` -> `PAID`)—without needing PayPal Sandbox
credentials or paid LLM API keys. When `PAYPAL_CLIENT_ID`,
`PAYPAL_CLIENT_SECRET`, and `OPENAI_API_KEY` are configured, the app
seamlessly switches to live PayPal Sandbox and OpenAI-compatible AI
calls.
How we built it
* **Backend & Domain Layer (Node.js + TypeScript + Zod):**
* `src/schema.ts`: Zod data contracts, `SourceSpan` provenance
models, reminder notification schemas, and the PayPal Invoicing v2
payload builder.
* `src/approval.ts`: Pure, unit-testable state machine enforcing
transitions (`proposed` -> `draft_created` -> `sent`) and Gate 3
daily rate-limiting (`canSendReminder`, max 2/day).
* `src/extractor.ts` & `src/aiExtractor.ts`: Dual-engine extraction
architecture combining an offline `DeterministicExtractor` with an
`OpenAiExtractionProvider` that verifies every LLM-quoted substring
against the raw input text.
* `src/reminderDrafter.ts`: Context-aware
`DeterministicReminderDrafter` and `OpenAiReminderDrafter` supporting
`gentle`, `professional`, and `urgent` tones.
* `src/paypal.ts`: Direct REST client for PayPal Invoicing v2
(`/v1/oauth2/token`, `/v2/invoicing/invoices`, `/send`, `/remind`)
with in-memory OAuth token caching and a strict sandbox-by-default
guardrail.
* `src/workflow.ts`: `InvoiceWorkflowService` orchestrator with
per-session concurrency locks (`withSessionLock`) and
`RecordedPayPalGateway` for zero-key simulation.
* **Frontend Web UI (`public/`):**
* Zero-build-step, accessible Single-Page Application using semantic
HTML5 (native `<dialog>` confirmation modals, ARIA live regions), CSS
container queries, and interactive quote highlighting.
* **Automated Verification (`test/`):**
* **44 automated tests across 8 Vitest suites** (`approval`,
`extractor`, `aiExtractor`, `reminderDrafter`, `paypal`, `workflow`,
`server`, and `ui`) plus zero-error TypeScript static checking.
Challenges we ran into
1. **Official SDK Coverage Gap for Invoicing v2:**
* While inspecting `@paypal/paypal-server-sdk` v2.5.0, we found
controllers for Orders, Payments, and Subscriptions, but no
controller for **Invoicing v2**. We built a clean, strongly typed
native `fetch` REST client in `src/paypal.ts` with OAuth 2.0 token
caching, idempotency headers, `204 No Content` handling, and a
hostname guardrail that blocks accidental calls to live production
PayPal unless explicitly overridden.
2. **Eliminating Hallucinated Quotes & Silent Line-Item Drops:**
* LLMs frequently paraphrase text instead of quoting it verbatim,
and naive regex parsers silently ignore unpriced list items like
`"3. Monthly Maintenance: $20/month"`. We solved both by verifying
exact substring offsets against the source message and turning
unpriced or recurring list lines into explicit clarification items
that block Gate 1 until resolved.
3. **Preventing Race Conditions & Stale Form Approvals:**
* Double-clicking an approval button or clicking "Approve Draft"
with unsaved form edits could cause duplicate drafts or stale
invoices. We added per-session in-flight locks (`withSessionLock`),
reusable `PayPal-Request-Id` keys per draft version, and automatic
form-save synchronization prior to Gate 1.
Accomplishments that we're proud of
* **True Human-in-the-Loop Agentic Commerce:** Designing an AI workflow
that refuses to guess missing financial terms and enforces three
distinct human approval gates across draft creation, invoice sending,
and overdue follow-ups.
* **44 Automated Tests & Zero TypeScript Errors:** Comprehensive test
coverage across pure state machines, provenance preservation when line
items are edited or removed, REST endpoints, and UI controllers.
* **Turnkey 30-Second Judge Experience:** Anyone can clone the
repository, run `npm install && npm start`, and test the complete
lifecycle immediately without configuring API keys—or plug in PayPal
Sandbox and OpenAI credentials for live execution.
What we learned
* PayPal's Invoicing v2 REST API is rich and expressive, supporting
multi-item deliverables, standard payment terms (`DUE_ON_RECEIPT`,
`NET_10`, `NET_14`, `NET_30`, etc.), hosted payer views, and native
`/remind` notifications.
* In financial software, autonomy without clear boundaries is a
liability. Users want autonomous *extraction and drafting*, paired with
deliberate, well-informed *human approval* before external side effects
occur.
What's next for Freelance Invoice Copilot
* **PayPal Webhook Listener (`INVOICING.INVOICE.PAID` /
`INVOICING.INVOICE.SCHEDULED`):** Real-time status updates and
automated project handoff checklists the moment a client completes
payment.
* **Tax, Discount & Multi-Milestone Templates:** Supporting VAT/GST tax
profiles and splitting multi-phase project sign-offs into linked
deposit and final-milestone PayPal invoices.
* **Multi-Channel Thread Ingestion:** Browser extension and
email-forwarding hooks so freelancers can send threads directly from
Gmail, Slack, or WhatsApp into their Copilot workspace.
Built With
- node.js
- openai
- paypal
- rest-api
- typescript