How it is built
Every tool in Giftwise, the one job it does, and where it lives in the code. Live status is on the Status page.
- PayPal
Moves the money: the donor pays the shop or the clinic directly, never her and never Giftwise; the ABLE share goes through her ABLE plan's own gift page.
How it works and where it lives
Orders v2 with a payee on each purchase unit, so one checkout pays several recipients. PayPal JavaScript SDK v6 buttons, server-side capture with PayPal-Request-Id idempotency, approval links that capture on return, Payouts batches for funds reconciled with Transaction Search and the Balances API, signature-verified webhooks, and sandbox negative testing for the failure cases.
paypal/orders.tspaypal/capture.tspaypal/payouts.tspaypal/reporting.tswebhook/route.tsgift/PayPalPay.tsx
- Claude Opus 5.5
Reads bills, quotes and fundraiser pages, and explains each decision in plain words.
How it works and where it lives
claude-opus-5-5 with adaptive thinking, vision and structured outputs validated by Zod, with server-side fallbacks. It never does the benefit math: the rules engine does.
- AG Studio
The dashboard of Maya's year: every gift, how it reached her, and her check each month compared with cash.
How it works and where it lives
ag-studio-react 3.0 with gifts and months data sources on a shared calendar, a theme built from the site tokens, a custom widget that runs the rules engine on whatever the filters leave in view, and the Studio Agent Framework running on Claude through our own adapter, with the rules engine as an extra tool.
dashboard/FamilyStudio.tsxdashboard/CheckWidget.tsxdashboard/studioAi.tsstudio-ai/translate.ts
- Render
Hosts the site and its Postgres database, and checks the live demo from outside every night with Render Workflows.
How it works and where it lives
A web service, a managed Postgres database, a Render Workflow and a cron job, all in one render.yaml. The workflow fans out one task per check (each failure case against the PayPal sandbox, each page on the product path, each public API promise) with retries, and files the run with the app; the cron job starts it nightly with the Render SDK, and /evidence can start it on demand.
render.yamlworkflows/src/main.tsworkflows/src/checks.tsstart/route.tsmonthly/route.ts
- Render Workflows: background jobs
Plans every monthly gift on the 1st, and rebuilds the rulebook's search index each week.
How it works and where it lives
monthlyRun fans out one planMonthly task per active monthly gift, each with its own retries, so every giver's page already shows that month's split and what changed; rulebookIndex rebuilds the Elastic index from the rule files. The nightly cron starts them on the 1st and on Sundays. The workflow calls the app with CHECKS_SECRET and handles gift ids, never givers' private links.
workflows/src/main.tsworkflows/src/trigger.tsplan/route.tsrulebook/route.ts
- Channel3
The gift finder: real products for a need, each checked against the SSI rules before it can go on a page.
How it works and where it lives
POST /v1/search (in stock, with a price cap) from the organizer's view and the find_gift MCP tool. Giftwise maps each product's category to the rules, runs the rules engine on it paid straight to the retailer, and holds back gift cards, which SSA counts like cash when they can buy food or shelter. Without a key, a sample catalog on .example domains stands in.
- Elasticsearch
Ask the rulebook: finds the passages that answer a question about SSI and gifts.
How it works and where it lives
Hybrid retrieval over the regulations, SSA's operating manual, its worked examples and the rule table: multi_match plus a semantic query on a semantic_text field with jina-embeddings-v3, fused by reciprocal rank (RRF), then the top 20 reranked by jina-reranker-v3. Claude answers from only those passages and a program checks every quote word for word. Without Elastic, an in-app BM25 ranking stands in.
- Postman
The Giftwise API as a collection anyone can import and run.
How it works and where it lives
16 requests in five folders, each with tests: gift checks, a quarter's exclusions, the report and the page that checks it, the rulebook, the MCP server and its discovery documents. The same collection runs in the test suite with Newman, 20 of 20 assertions.
scripts/postman-collection.mjsscripts/run-postman.mjspostman/giftwise.postman_collection.json
- Zapier
Tells the family the moment a gift arrives.
How it works and where it lives
Each PayPal capture posts a signed gift.received event to the organizer's webhook. A Zapier Catch Hook turns it into a text, an email, a Slack message or a spreadsheet row, with no code. HMAC-SHA256 signatures with a timestamp; private network addresses are refused.
- Bryntum Calendar
Maya's benefit calendar: each gift on the day it arrived, each report to Social Security on its due date, and the check each 1st of the month pays.
How it works and where it lives
Bryntum Calendar 7.3 (trial) in month view. Dragging a gift to another day reruns the rules engine in the browser and lists which checks changed; a form does the same without dragging. A planner agent moves cash gifts on the calendar itself, by up to a week, so more of each falls under SSI's monthly and quarterly exclusions, and never into a month with no check. The light or dark Bryntum theme is loaded to match the site.
calendar/BenefitCalendar.tsxcalendar/model.tscalendar/schedule.tsbryntum/[file]/route.ts
- APIMatic
Generated the PayPal Server SDK that creates every Giftwise order.
How it works and where it lives
@paypal/paypal-server-sdk, generated by APIMatic, creates every order (one purchase unit and payee per recipient, with a PayPal-Request-Id), with typed requests. Reads and capture are plain REST. The SDK checks each answer against its schema, and PayPal returns a captured order with several payees with an empty shipping address on each unit, which that schema turns away, so readOrder() in src/lib/paypal/orders.ts reads orders over REST. Capture in src/lib/paypal/capture.ts sends PayPal's negative-testing header for the failure cases and retries a 5xx with the same PayPal-Request-Id.
Every PayPal API Giftwise calls
13 PayPal APIs and tools, each with one job. All run against the PayPal sandbox. PAYPAL_ENV=live moves the REST API, the JavaScript SDK and PayPal’s MCP server together (src/lib/paypal/env.ts), with the live app’s keys. Each shop then joins through PayPal’s Partner Referrals and is paid by its merchant ID in src/lib/paypal/orders.ts; this sandbox app gets 403 NOT_AUTHORIZED from Partner Referrals until PayPal approves it as a partner, so the demo pays each shop by email address.
All 13 PayPal APIs and tools, with the call and the file for each
| PayPal | Call | What it does here | Where |
|---|---|---|---|
| OAuth 2.0 | POST /v1/oauth2/token | One cached access token for every call below. | paypal/client.ts |
| Orders v2: create | POST /v2/checkout/orders | One order with a purchase unit and payee for each recipient, through the APIMatic-generated Server SDK. | paypal/orders.ts |
| Orders v2: capture | POST /v2/checkout/orders/{id}/capture | Captures every payee once, with PayPal-Request-Id, so a double click cannot pay twice. Plain REST, so the failure cases can send PayPal's negative-testing header. | paypal/capture.ts |
| Orders v2: show | GET /v2/checkout/orders/{id} | Check with PayPal now on every receipt, the report check, capture's read back, and the assistant's read when PayPal's MCP server cannot run. Plain REST, so a captured order with several payees reads as PayPal sends it. | paypal/orders.ts |
| Orders v2: confirm payment source | POST /v2/checkout/orders/{id}/confirm-payment-source | Pays with a sandbox test card in the failure cases and in the nightly check that pays a gift split across its payees, standing in for the giver. | lib/card-check.ts |
| Payments v2: refund | POST /v2/payments/captures/{id}/refund | The refund failure case: PayPal lets only the shop send money back. | cases/scenarios.ts |
| JavaScript SDK v6 | web-sdk/v6/core | The PayPal button on each need and on Give any amount, and PayPal's guest Debit or Credit Card button where the SDK finds it eligible. | gift/PayPalPay.tsx |
| Payouts | POST /v1/payments/payouts, GET /v1/payments/payouts/{id} | A fund pays many households' needs in one batch. | paypal/payouts.ts |
| Transaction Search | GET /v1/reporting/transactions | Confirms each payout in PayPal's own transaction report. | paypal/reporting.ts |
| Balances | GET /v1/reporting/balances | Shows the fund's PayPal balance before it sends a batch. | paypal/reporting.ts |
| Webhooks | POST /v1/notifications/verify-webhook-signature | Captures a payment if the giver never comes back, after checking PayPal's signature. | webhook/route.ts |
| Negative testing | PayPal-Mock-Response header | Makes the sandbox decline on purpose, for the failure cases. | paypal/capture.ts |
| PayPal MCP server | @paypal/mcp over stdio, tool get_order | Reads orders back from PayPal itself, twice: the assistant on /agents reads each checkout it starts before June approves it, and the nightly card check reads every gift it pays to confirm each capture, amount and payee. Giftwise runs PayPal's own server package; PayPal's hosted server is the second choice. If neither answers, the Orders API does, and the page says which one did. | agent/paypal-mcp.ts |