The problem
Freelancers and small agencies get paid in milestones, but the evidence that a milestone is done — an email thread, a delivery note, a chat message saying "the homepage redesign is approved" — lives far away from the invoice. So the invoice is written by hand, late, from memory, and when the client pays part of it, or pays late, nobody reconciles the payment against the milestone without digging through statements. Money that was earned leaks into "I'll chase it next week."
MilestonePay is for freelancers and small studios who bill per milestone: it turns the milestone evidence you already have into an invoice draft a human approves, sends it through PayPal, and keeps a ledger that updates itself from PayPal's own records as payments arrive.
What it does
1. Evidence in, structured draft out. MilestonePay reads free-text milestone evidence and extracts a structured invoice draft — client, milestone, deliverables, amount, currency, due terms — with a confidence score and the source quotation for every field. A field the evidence does not support is left empty and flagged, never filled in by guess. Vague deliverables and missing amounts raise explicit flags instead of silently passing.
2. No invoice without a human. The draft cannot become an invoice on its own. A human reviews the extracted fields and the flags, then approves or rejects; the decision is stored on the draft and written to an append-only audit log. The invoicing code itself enforces the gate: it refuses unapproved drafts, rejected drafts, and approved drafts that still have blocking gaps such as a missing amount.
3. A real PayPal draft after approval. An approved draft becomes a draft invoice through PayPal's Invoicing API v2, using the same payload shape and client code that our sandbox sessions proved live (see the status notes below). Creating the draft is the agent's step; sending stays a separate, deliberate human step.
4. A ledger that reconciles itself. MilestonePay receives real PayPal webhook deliveries over a public URL, verifies each signature via PayPal's verify-webhook-signature API (verification_status SUCCESS), and updates its ledger only from the canonical invoice record fetched from PayPal — never from the webhook payload. Duplicate deliveries are ignored by event id, so the ledger is updated exactly once per event.
What is proven today (PayPal sandbox)
- The invoicing pipeline creates and sends real PayPal sandbox invoices via the Invoicing API v2, and reads their canonical state back.
- The webhook pipeline registered for six INVOICING.* event types, received real deliveries over a public URL, and verified their signatures successfully.
- One real delivered payment event has been applied to the ledger: USD 40.00 recorded against a USD 100.00 invoice — a payment recorded through the API as an external payment — reconciling the invoice to PARTIALLY_PAID with USD 60.00 remaining. The ledger state came from the canonical invoice record, not the event payload.
Honest status — what this entry does not claim
- We do not claim that a buyer has paid in full through PayPal checkout and that we reconciled it. The one applied payment event above came from an API-recorded external payment; a full-payment reconciliation line is still outstanding and is a scheduled live-session step, not a result.
- The extraction layer is provider-based. The provider used in the demo and tests is a deterministic, offline stand-in provider, labelled as such wherever it appears; it is pattern-based and is not an AI model. An LLM adapter for an OpenAI-compatible endpoint is built behind the same interface — environment-configured, fail-closed when unconfigured, and its output is validated against the evidence (a field whose quoted source does not appear in the evidence is capped at low confidence and flagged) — but we do not claim it has run against a live model: so far it has been exercised only against an injected fake transport in tests.
- The agent's own PayPal client is wired to the proven invoicing code path but has not yet made a live call itself; a live smoke test in the sandbox is scheduled before the filing is treated as complete.
- Everything runs against the PayPal sandbox. No production endpoint, no real money.
Why it matters
The design rule is "the machine drafts, the human decides, the ledger trusts only the source." Extraction is not allowed to invent a number, invoicing is not allowed to happen without a recorded approval, and reconciliation is not allowed to trust anything except PayPal's canonical record. That is the shape an invoicing agent needs before anyone should let it near real receivables.
Tools used
- PayPal Invoicing API v2 (sandbox) — invoice draft creation, sending, and canonical read-back of invoice state.
- PayPal Webhooks API + verify-webhook-signature API — subscription for INVOICING.* events, real delivery intake, and per-event signature verification before any ledger change.
- Python 3 — the whole build: webhook listener and reconciliation pipeline, the agent package (extraction, approval gate, invoice hand-off), and two self-contained test suites (webhook pipeline, agent pipeline) that run locally with no network.
- OpenAI-compatible chat-completions adapter — built as the second extraction provider behind the provider interface; environment-configured and fail-closed (see the honesty notes above for its exact status).
- SSH reverse tunnel (localhost.run) — the temporary public HTTPS URL the sandbox webhook deliveries were received on during live sessions.