Inspiration
A marketing agency sits in the middle of every campaign's money. It collects fees from the brand, then pays a roster of influencers and freelancers. In most small agencies that runs on spreadsheets, WhatsApp screenshots and bank transfers, and it fails in three expensive ways:
- Paying out before getting paid. Clients on net-30 to net-120 terms often take about 65 days to pay, so agencies end up fronting creator fees with their own cash.
- Slow creator payments. Someone has to check every post by hand (brand tagged, hashtags, posted on time) before releasing money. Creators are typically paid 60–90 days after delivery, and in a survey of 750 creators, up to 87% had been paid late, short-paid or not paid at least once.
- Invoice mismatches slipping through. A freelancer invoice that doesn't match the contract is only caught if someone cross-checks it.
Influencer suites pay creators on behalf of brands, and payout services move money, but none of them protects the agency in the middle. That's the gap On-Track fills.
What it does
On-Track is an internal payment desk for an agency's finance and ops team. One rule makes it safe: the AI reads, code decides, a person approves.
- Contract in, timeline out. Upload a signed client contract (PDF). Claude extracts the client, fee, deposit and balance split, due dates, and every creator's fee and deliverable. You review and correct every field, then the campaign is created.
- Client billing through PayPal. The deposit invoice goes out through PayPal Invoicing in one click. When the client pays, PayPal's webhook marks it paid and the creator payouts on that campaign unlock, with no reload. Until then, code refuses every payout on the campaign.
- Influencers. Upload the post screenshot the creator sent. The AI pre-checks it against the brief (brand tagged, hashtags, posted inside the window) with a reason for each item, and a person confirms. A verified post queues the contracted fee for approval.
- Freelancers. Claude reads the invoice (PDF or image), and plain code compares it with the contract: amount, payee, PayPal email, deliverable and currency. A mismatch is held with both values side by side until a corrected invoice arrives or someone overrides it with a written reason.
- Approvals and payouts. Every payout needs a person's approval: one by one, in a batch, or from the agent's proposal. Approved payouts go out through PayPal Payouts on their payout date, or straight away with Send now. A payout can be lowered or moved before it's sent, never raised above the contract.
- An agent that proposes, never pays. Ask it in plain language, for example "Approve every creator with a verified post". It returns a plan, and each item is dry-run through the same guardrail code before a person approves.
- One shared picture. A Bryntum Scheduler (one row per creator plus the client) and a Bryntum Calendar show every post, deliverable, invoice and payout, colour-coded by state and updating live. Drag a deliverable and its payout date moves. If it now falls after the campaign end, On-Track proposes pushing the client's balance invoice, and approving updates the invoice in PayPal.
- Many campaigns, one queue. Every campaign has its own timeline, deposit lock and creators, with a single approval queue across all of them, a campaign summary (billed, received, paid, owed, margin) and an audit log of every AI read, approval and payment.
How we built it
- App: Next.js 16 (App Router, Server Actions), React 19, TypeScript, Tailwind and shadcn/ui, PostgreSQL with Drizzle.
- PayPal Developer Platform (sandbox):
- Invoicing v2: create and send the deposit invoice, create the balance invoice as a draft, update its due date, check payment status.
- Payouts v1: my own REST client, because the PayPal Agent Toolkit and MCP server have no Payouts tools.
sender_batch_idis the payout's id (with an attempt suffix on a retry), so a retry can never pay twice. - Webhooks: every event's signature is verified with PayPal before anything changes. Events are idempotent (the event id is stored in the same transaction as its effects), and late events from an earlier failed attempt are ignored.
- Fallbacks for missed webhooks: Check payment asks PayPal about an invoice, Check status asks about a sent payout, and the daily run does both.
- AI: Claude Haiku 5.5 reads contracts, invoices and post screenshots through structured outputs. The AI only extracts; every comparison and decision is plain, unit-tested code.
- Guardrails in code:
canPay()runs at approval and again right before any payout is sent. It blocks a payout while the client's deposit is unpaid, without a passing verification, above the contract fee, or a second time for the same work. Money is stored as integer cents. - Bryntum Scheduler and Calendar read the same event data. Dragging in either view goes through the same server action and audit trail.
- Quality: 110 unit tests and 77 integration tests against a real Postgres, plus an AI eval set. On Claude Haiku 5.5 it caught 5 of 5 mismatched invoices with 0 of 5 false holds, and extracted every field correctly from 3 of 3 contracts. Median read time is 2.2 s per invoice and 5.6 s per contract.
Challenges we ran into
- Payouts aren't in the PayPal MCP / Agent Toolkit, so payouts needed their own client and careful idempotency: a timeout must never lead to paying a creator twice.
- Sandbox webhooks don't always arrive. I added status checks against PayPal for both invoices and payouts, which settle through the same code path as the webhook.
- Dates and time zones. The agency is in UTC+8, so payout dates are stored and compared as
YYYY-MM-DDstrings and never turned into JavaScriptDateobjects. - Keeping the AI out of the decisions while still making it useful: it pre-fills and proposes, and code plus a person decide.
- Live views versus dragging. Both views refresh every 3 seconds, which must not cancel a drag in progress.
Accomplishments that we're proud of
- A complete money flow on the real PayPal sandbox: invoice the client, unlock on payment, verify, approve, pay out, settle by webhook.
- Guardrails that hold no matter what the AI says, with a full audit trail.
- A measured AI eval instead of "it seemed to work": every planted mismatch caught, no false holds.
- Built solo, in about eight hours a week.
What we learned
- The best split for AI in payments: let the model read messy documents, and let code and people make the money decisions.
- Webhooks need a backup path. Idempotency and "late event" handling matter as much as the happy path.
- PayPal access tokens cache their scopes: after enabling a feature on an app, you need a fresh token.
What's next for On-Track
- Logins and roles: preparers versus approvers, with approval limits.
- Pulling post proof straight from Instagram and TikTok instead of screenshots.
- More currencies, and Hyperwallet for creators who prefer bank payouts.
- Creator suggestions from the roster for a new brief.
- A pilot with a small agency in Kuala Lumpur.
Built With
- bryntum
- claude
- docker
- drizzle
- nextjs
- paypal
- typescript