Crews that work on call are run like it's 1999

Event setups, catering, removals, small construction: the work is done by freelancers booked for a day or two. The person running the job books them one by one on WhatsApp, keeps the plan in their head, writes hours on paper, guesses the overtime, and pays everyone weeks later with manual bank transfers. The best people stop answering after the second late payment.

I built Quotely for the same hackathon to close the gap between "yes" and "paid" on the customer side. Shiftly closes it on the crew side: book the crew, run the job, pay everyone the same day.

What Shiftly does

An agent runs the whole cycle. A human approves every step that cannot be undone.

  1. Describe the job like you'd text your crew: "Trade fair stand build for Orbis Expo in Rho, 14-15 November, two shifts per day, 5 installers and 1 certified electrician each…". The agent extracts dates, venue, shifts and roles, and shows exactly what it assumed and what is missing. Nothing is hidden behind a magic button.
  2. Plan with AI. The hard rules live in code, not in the model: right role, availability, no double booking, 11 hours of rest between working days. OpenAI chooses only among the valid candidates and writes a one-sentence reason for every pick. The plan appears on a Bryntum Scheduler: people on the rows, shifts on the timeline, drag & drop to reassign, and the same rules checked while you drag ("Sara is not qualified for this role").
  3. Book the crew. One click sends every freelancer a signed link. They accept or decline from their phone. When Luca declines, the agent rebalances only his shifts, proposes a replacement, says why, and waits for your approval. When everyone has answered, the crew is confirmed and the schedule locks.
  4. Reconcile the hours. After the job, actual hours are prefilled from the plan and adjustable in 15-minute steps. Anything more than 15 minutes off is flagged by the agent, overtime is computed per working day, and approval is blocked until every flag is reviewed.
  5. Pay crew. You see every name and amount before you confirm. Shiftly sends one PayPal Payouts batch and reads the result back from PayPal: a webhook and a reconciliation pass converge on the same idempotent function, so nobody is ever paid twice, statuses never move backwards, and Paid appears only when PayPal says so. Thirty seconds later the money is in the freelancer's PayPal account.
  6. Payroll. Every batch, item, PayPal id and transaction id. Unclaimed and failed items are shown as such; Retry failed creates a new batch for exactly those people.

Every decision, by the agent or by you, is written to an activity log with its reason.

Try it: the live demo runs on the PayPal sandbox with seeded data for a fictional Milan setup company, "Fieramente Allestimenti". Create a job from a brief, plan it, send the links, open two of them yourself, approve the hours and pay the crew: the payouts are real sandbox transfers to real sandbox accounts.

How it's built

  • Next.js 16 (App Router) + TypeScript + Tailwind 4, Prisma + PostgreSQL on Render, money as integer cents, every state transition in an activity log with its actor (operator, agent, crew, PayPal).
  • PayPal REST: OAuth client credentials, Payouts API (POST /v1/payments/payouts, GET /v1/payments/payouts/{id}) with PayPal-Request-Id = sender batch id for idempotency, webhook with signature verification (/v1/notifications/verify-webhook-signature) for the 11 payouts-batch and payouts-item events, plus POST /api/paypal/reconcile behind a shared secret. Both paths re-fetch the batch and call the same applyBatchStatus. Sandbox host hard-coded: this app can never touch live money.
  • Bryntum Scheduler 7 (trial) themed to the design system: role colours, proposed vs confirmed vs declined states, conflict markers, drag validation through Bryntum's renderer and drag hooks. To keep the build light, the prebuilt module is served from /public/vendor and loaded at runtime instead of going through the bundler (that alone took the production build from over 8 GB of memory to 1.3 GB).
  • OpenAI (structured JSON output) for three narrow tasks: brief extraction, choosing among pre-validated candidates with a reason, and writing the invitation text. The model never computes money and never sees PayPal credentials.
  • APIMatic PayPal Context Plugin installed in Claude Code during development for SDK and API lookups; Payouts is not covered by the TypeScript server SDK, so that part uses plain REST.
  • Design system first: tokens, type scale (Manrope + IBM Plex Mono), status pills, Scheduler theming contract and all screen specs written before the first component, then implemented from them.
  • Deterministic pay engine (minutes × rate, overtime per working day, cents rounding) and idempotency rules covered by 49 unit tests.

Challenges

  • Rules belong in code. The first planner let the model reason about rest hours; it was eloquent and wrong. Now code computes the candidates and the model only chooses and explains. The same validator runs on plan, on drag, on drop and on rebalance.
  • Two shifts, one working day. An 11-hour rest rule applied naively between a morning and an afternoon shift means nobody can work a full day. Rest is now measured between working days, and overtime on the paid hours of the whole day.
  • An agent that refuses to invent numbers. The model's first plan summaries stated costs it had made up. Every figure shown now comes from code; the model gets text, not arithmetic.
  • Never trust a webhook body. Deliveries can be repeated or out of order. The webhook only takes the batch id, re-fetches the batch from PayPal, and applies the same idempotent transition as the periodic check.
  • Keeping money moves honest in a demo. Four of the seven freelancers in the demo have no PayPal account, so their payouts come back Unclaimed. Instead of hiding that, Shiftly shows it, explains it, and refuses to pay those people twice.

What I'm proud of

  • A full loop that really runs in the sandbox, end to end, on a public URL: brief → plan → invitations → rebalance → hours → PayPal Payouts → money in the freelancer's account, with an audit trail at every step.
  • The restraint of the agent: it proposes, explains and waits. Money moves only after a human has read the names and the total.
  • The Scheduler integration: a real commercial scheduling component, themed, with drag validation wired to the same rules as the planner.

What's next

  • Real delivery of invitations (email/WhatsApp) with PayPal's own "claim your money" flow for unclaimed payouts.
  • Rates and rules per job and per client, multi-currency.
  • A freelancer app: my shifts, my hours, my payouts.
  • Packaging the payout engine (idempotent batches + webhook/reconcile convergence) as a reusable module for any PayPal Payouts integration.

Built by Jacopo Gonzales, Gonzales Advisory, Italy. Solo project, PayPal AI Hackathon 2026. Sister project: Quotely (quote-to-cash with PayPal Invoicing).

Built With

  • apimatic
  • bryntum-scheduler
  • next.js
  • openai
  • paypal-payouts
  • paypal-webhooks
  • postgresql
  • prisma
  • render
  • tailwind
  • typescript
Share this project:

Updates

Submission history