Inspiration

Typhoon Yagi made landfall in northern Vietnam on 7 September 2024. Heavy rain warnings were out days before, but cash aid usually reaches families weeks after a flood. Anticipatory action, paying a small amount on a trigger agreed in advance, lets families move belongings, buy food and leave in time. What stops organisers is trust and speed: who decided this province gets paid, can the trigger be changed afterwards, and how do you check long Vietnamese bulletins and household lists in a few hours? We wanted AI to make that faster without ever letting it decide who gets money.

What it does

Early Relief replays Typhoon Yagi with simulated data:

  1. Trigger (code): a rule locked before the season, "$20 per registered household when the forecast made up to 48 hours ahead shows at least 150 mm of rain in 24 hours", is checked against each forecast. Quang Ninh and Hai Phong cross it 48 hours ahead, Thai Binh 24 hours ahead.
  2. Evidence (AI, checked by code): AI reads each Vietnamese warning bulletin and returns provinces, hazard, warning level, valid time, rainfall and wind, each with a verbatim quote. Code checks every quote against the bulletin. A value whose quote is not in the text is marked "Check this"; a value the bulletin does not state stays empty.
  3. Amounts (code): one payout line per local partner, households × the fixed amount, in integer cents. AI output never reaches this step.
  4. Review (person): the review screen puts the locked rule, the forecasts against the threshold, the AI readings beside their source text, and every household with an Exclude button on one page. Code flags households that share a phone number or address, or were registered in a burst; AI can explain the flags in plain words, but a flag never excludes anyone. Totals are recalculated by the server.
  5. Approve once (person): approval restates the total and sends a fingerprint (hash) of the exact plan reviewed. If the plan changed, PayPal is not called. A plan can only be paid once, and a plan over $1,000 is refused.
  6. Pay (PayPal Payouts): one sandbox Payouts batch per plan, one item per partner. The screen shows the PayPal batch ID and each partner's status in plain language ("Sending", "Received", "Not claimed") until the money arrives.

Payouts go to registered partner organisations, which hand cash to households. That matches what Payouts is for and keeps it clear of person-to-person use.

How we built it

  • Next.js 16 (App Router), TypeScript and Tailwind CSS 4, deployed as one Render web service.
  • PayPal products / APIs:
    • Payouts v1 (sandbox): createPayoutBatch sends one batch per approved plan with the plan ID as sender_batch_id, so a retry cannot pay twice. getPayoutBatch reads per-partner item statuses, which the review screen polls with back-off until a final status.
    • OAuth 2.0 client credentials, server side.
    • Webhooks with postback signature verification (optional): a verified payout event triggers a fresh batch read, so late refunds or reversals are recorded.
    • Orders v2 create and capture were built and tested in the foundation but are not in the relief flow.
  • AI models / services:
    • Meta Model API, muse-spark-1.3, through its OpenAI-compatible chat completions endpoint. It reads Vietnamese bulletins into structured JSON with a quote per value. Any OpenAI-compatible model can be swapped in with AI_BASE_URL and AI_MODEL.
    • A chatJson helper validates every reply with a hand-written validator, retries once with the error, and fails closed. Bulletin text is wrapped as <data> with a policy that instructions inside it are ignored.
    • The same model explains household flags for the reviewer. It only sees household IDs, check results and amounts, and a summary that mentions an amount not in the plan is replaced by a template.
    • Without a key, a pattern-matching reader produces the same shape through the same quote check, so the demo works for anyone.
  • AI tools used to build it: Claude Code and OpenAI Codex helped write code, tests and docs. Their commits carry Co-authored-by trailers. Every change went through a pull request with lint, typecheck, unit tests (196) and CI.
  • Sponsor tools: none.

Challenges we ran into

  • Keeping AI useful but powerless. We split the system so the rules engine has no AI imports, and every AI value must quote its source sentence and pass a code check before it is shown as confirmed.
  • Paying exactly once. Double clicks, stale browser tabs, PayPal timeouts and retries all had to end in at most one batch. The plan hash, a shared in-flight approval, and the plan ID as sender_batch_id together cover those cases.
  • Payout statuses arrive late and out of order. Polling and webhooks share one reconciliation path per plan, and a settled item never goes back to pending.
  • Free AI models were rate limited upstream during testing, so the reader fails gracefully and the screen stays usable.

Accomplishments that we're proud of

  • The review screen: every number on it can be traced to a locked rule, a forecast, a quoted sentence or a person's action.
  • A real sandbox Payouts batch, verified per partner, from one approval.
  • It runs end to end with no keys at all, so anyone can clone it and try it.

What we learned

  • PayPal's pages list Vietnam as eligible for Payouts ("send, receive and withdraw") in USD, with withdrawal to a local bank for a fee. We have not tested a real Vietnam-registered account; everything ran on the sandbox, and live relief use would need PayPal's approval. Details and sources are in docs/payouts-spike.md.
  • A batch-level SUCCESS does not mean each recipient was paid. Only item statuses tell you that.
  • Asking a model for quotes, then checking them in code, is a cheap and effective guard against invented values.

What's next for Early Relief

  • Partner confirmation page with one-time receipt codes, and sampled phone callbacks.
  • Richer household checks, such as cross-partner duplicates and lists compared with official registers.
  • A donor report whose numbers are checked against recorded payouts.
  • Live forecast data instead of the replay, and a pilot with a Vietnamese relief organisation.

Built With

Share this project:

Updates

Submission history