Kindly chases, negotiates and settles unpaid invoices through PayPal. You set the limits once, in plain language, and the agent works inside them.

I built Kindly for freelancers and small studios. The work is usually fine. The follow-up is what hurts. You finish a project, send the invoice, and then have to become the person who asks for money.

The problem

Late payment is not an edge case.

  • 59% of small businesses have invoices more than 30 days overdue, and the average business waiting on payment is owed $17,700 (Intuit QuickBooks, 2026 Small Business Late Payments Report).
  • Small businesses wait 29.3 days to get paid, roughly 9 days late (Xero Small Business Insights, Q2 2026).
  • 85% of freelancers are affected by late payment, and 42% have missed personal bills because a client paid late (Plutio / Freelancer Playbook, 2026).
  • FreshBooks and Stripe's 2026 State of Financial Flow names the pain directly: "uncomfortable client conversations." Freelancers build reminder automations so the ask comes from a system, not from them.

Reminder tools send the email and stop. The freelancer still has to decide what to concede, say it, and chase whatever was agreed. Kindly does that part. And because the money moves through PayPal, it can also show which action caused the payment.

What it does

A freelancer opens the desk and sees their receivables. Kindly reads the situation (how late, how this client usually pays, what they have already said) and proposes a move: a nudge, a firmer ask, a payment plan, an escalation, or closing the invoice as uncollectible. Every proposal goes through the mandate before anything is sent.

The client gets a signed, expiring link. No account. They can read the letters, pay with PayPal, or ask for different terms in their own words. Kindly reads the intent, checks it against the mandate, and answers in the studio's voice.

If the client's side has an accounts-payable agent, Kindly negotiates with it directly: signed terms, a counter, an automatic refusal when the counter breaks the floor, and a human approval on each side before money moves.

How the limits work

The mandate is a small set of rules the owner writes once: contacts per week, quiet hours, the minimum up-front share, the discount ceiling, the number of instalments, how long a plan can run, and whether the agent may negotiate with another agent.

A deterministic policy engine evaluates every proposal against those rules and returns allow, needs approval, or blocked. The language model never authorizes anything. All three verdicts go into an append-only ledger with the reason attached, so a refusal is as visible as a success. In the demo you can watch it refuse Acme's counter because 50% up front is below the owner's 70% floor.

PayPal, end to end

  • Invoicing v2 creates real PayPal invoice objects, sends them, records payments, cancels, lists, and generates QR codes. If an app is not granted the Invoicing feature, Kindly falls back to a Checkout payment link and records why.
  • Checkout Orders v2 powers the portal's pay-now and pay-part buttons. The invoice id travels in custom_id, so a capture still settles the right invoice if the HTTP response is lost.
  • Webhooks for INVOICING.INVOICE.*, CHECKOUT.ORDER.APPROVED and PAYMENT.CAPTURE.COMPLETED are verified through PayPal's verify-webhook-signature endpoint against the exact delivered bytes, deduplicated by event id, and stored.
  • Every write carries a PayPal-Request-Id idempotency key and retries with backoff on 429/5xx.
  • The hosted demo runs against the sandbox. It has created invoice objects, opened checkout, captured a test-card payment and received the signed capture event. The testing instructions below let you do the same.

Where AI is used

Kindly separates authority, judgment, and language.

  • TypeSafe System One (Jev) answers typed questions: a Choice over a closed intent set, a Noul asking whether the message addresses the system rather than the business, and a Choice over the candidate moves. Every answer carries a probability, and a low-confidence answer falls back to a deterministic reader instead of acting on a coin flip.
  • I calibrated the instruction-attempt threshold on eleven real replies. Attempts scored 0.81-0.98; ordinary correspondence scored 0.02-0.14. The 0.60 cutoff sits inside that gap.
  • Prose is optional. Without a model, Kindly writes from templates. With an OpenAI-compatible endpoint, the letters are generated, but the plan parameters are still clamped in code against the mandate.

What I verified

  • 101 Rust tests, 39 end-to-end checks against the simulation, 11 browser checks against the deployed pair.
  • scripts/verify-live.py reads every mirrored invoice back from PayPal with the app's own credentials, confirms every recorded delivery was signature-verified, and opens a Checkout order without approving it.
  • The video shows a test-card payment on the live sandbox, the capture, the signed webhook, and the attribution ("paid on their own, 10 days ahead of their usual").

Links

Built With

  • ag-charts
  • ag-grid
  • axum
  • docker
  • ed25519
  • fly.io
  • jev
  • next.js
  • paypal-checkout
  • paypal-invoicing-api
  • paypal-webhooks
  • playwright
  • react
  • rust
  • sqlite
  • sqlx
  • tokio
  • typesafe
  • typescript
  • vercel
Share this project:

Updates

Submission history