v0.4.2 — Open-source webhook unification for university advancement & nonprofit fundraising teams.
UniSchema normalizes fragmented advancement webhooks into ConstituentEvent; PhilanthroPy scores propensity, lapse, and engagement on the features you build from that stream.
Pilot-ready, not production-hardened. Eight built-in vendors, SQLite or Postgres, self-hosted.
Read docs/limitations-and-roadmap.md before production donor data.
One URL: API + admin UI on the same port.
- Advancement analytics teams ingesting 2–4 webhook vendors (GiveCampus, Cvent, Slate, NPSP, etc.)
- Shops that want a single normalized event stream for warehouse, dashboards, or ML
- Teams comfortable self-hosting Node + secrets + S3 (or local egress for pilots)
Not for (today): a fully managed SaaS with vendor SLAs (UniSchema is self-host only) · bi-directional CRM sync (this is webhook ingest + normalize, not a CRM) · fully no-code vendor onboarding (new vendors need a one-time code deploy — see canvas vs code) · orgs whose constituent model differs significantly from ConstituentEvent (use normalizedMetadata or fork via RFC).
flowchart LR
Vendors[VendorWebhooks] --> UniSchema[UniSchema]
UniSchema --> Egress[LocalOrS3Egress]
Egress --> Features[FeatureTable]
Features --> PhilanthroPy[PhilanthroPy]
Features --> CRM[CRMGoldenRecord]
CRM --> PhilanthroPy
PhilanthroPy --> Scores[AffinityAndLapseScores]
| Project | Role |
|---|---|
| UniSchema (this repo) | Ingest webhooks → validate → map → egress ConstituentEvent |
| PhilanthroPy | sklearn-native ML for advancement (RFM, propensity, lapse) |
| dbt / Airflow (optional) | Warehouse staging and orchestration — downstream guide |
Full stack map → docs/ecosystem.md
The ConstituentEvent contract is published as a versioned JSON Schema — schema/constituent-event.schema.json — so downstream consumers validate against a fixed artifact instead of hand-copied field names. Regenerate with npm run schema:export; a drift test fails CI if it falls out of sync with the Zod schema.
Requires: Docker + Docker Compose, plus curl and jq for the demo scripts (python3 for the downstream demo).
git clone https://github.com/PhilanthroPy-Project/UniSchema.git
cd UniSchema
docker compose up --build- Open http://localhost:3000 — mapping canvas + API together
- In another terminal:
bash scripts/demo-webhook.sh(single webhook) orbash scripts/demo-webhook.sh --multi(all vendors) - See ConstituentEvent JSON under
data/egress/ - Prove downstream value:
bash scripts/downstream-demo.sh(needspython3)
GiveCampus POST → 202 Accepted → background map → data/egress/.../eventId.json
Without Docker
npm install # also installs the frontend workspace
npm run build
SERVE_FRONTEND=true npm start # long-running — leave this in its own terminal
npm run demo:multi # then run this in a second terminal| I am… | Start here |
|---|---|
| New adopter — first webhook in ~15 min | Quick start above |
| Admin / analyst — drawing mapping lines on the canvas | docs/admin-guide.md |
| Operator — secrets, S3 egress, cloud deploy | docs/operator-guide.md |
| Developer — adding vendor #9 | docs/adding-a-vendor.md |
| Data engineer — warehouse + dbt | docs/downstream-pipeline.md |
| Data scientist / ML engineer — PhilanthroPy scoring | docs/philanthropy-integration.md |
| All docs | docs/README.md |
The visual mapper overrides fields on registered vendors — it does not create new webhook routes.
| Task | Canvas | Requires deploy |
|---|---|---|
Remap fields → normalizedMetadata |
Yes | No |
| Override built-in field wiring | Yes | No |
New POST /webhooks/{vendor} route |
No | Yes (6-file checklist) |
| HMAC secret + Zod payload schema | No | Yes |
Details → docs/canvas-vs-code.md
| Stage | Stack | Throughput (typical) | Guide |
|---|---|---|---|
| Pilot (~15 min) | Docker + SQLite + local egress | ~600–900 req/min (Docker, limit raised) | Quick start |
| Production | Fly/Railway + S3 + Postgres optional | ~120 req/min/IP default; tune for giving day | Operator guide |
| Scale | Postgres + Redis + multi-instance | Benchmark before peak — npm run benchmark |
Benchmarks |
| Today (v0.4.2) | Limits |
|---|---|
| 8 vendors: GiveCampus, Cvent, iModules, Blackbaud, NPSP, Slate, Ellucian, CiviCRM | Tier 3 — verify with real payloads |
| Tier 1: GiveCampus, Cvent · Tier 2: iModules · Tier 3: Blackbaud, NPSP, Slate, Ellucian, CiviCRM | Ellucian + CiviCRM are bootstrap Tier 3 |
| SQLite default + optional Postgres | Horizontal scale needs Postgres + Redis |
| HMAC webhook verification | ~120 req/min/IP default |
| Visual canvas + metadata mappings | Opinionated master schema — details |
| Local + S3 egress → PhilanthroPy ML bridge | ML requires optional pip install -r examples/downstream/requirements-philanthropy.txt |
| Drift queue + experimental LLM agent | Human review required — ai-agent-loop |
| 3 event types: registration, donation, email click | New types via RFC — schema-governance |
Vendor registry (8 built-in) → docs/README.md#vendor-registry
After npm run downstream-demo:
pip install -r examples/downstream/requirements-philanthropy.txt
python3 examples/downstream/philanthropy_crm_pipeline.py data/egress samples/crm-golden-record.csv- Integration guide: docs/philanthropy-integration.md
- Notebook: examples/downstream/egress_report.ipynb
If you use UniSchema in academic or applied fundraising-analytics work, cite the release you used — a CITATION.cff ships with the repo, so GitHub shows a Cite this repository button in the sidebar. Every tagged release is archived on Zenodo with a citable DOI.
| Method | Path | Description |
|---|---|---|
GET |
/health |
Health check (version, egress, driftPendingCount) |
GET |
/api/vendors |
Vendor registry with tier metadata |
POST |
/webhooks/{vendor} |
Vendor webhooks (202) |
GET |
/webhooks/ingestions/:id |
Poll async status (Bearer auth in production) |
POST |
/api/mappings/sync |
Save canvas mapping (Bearer auth in production) |
POST |
/api/mappings/preview |
Preview ConstituentEvent from artifact |
GET |
/api/mappings/:vendor |
Load canvas mapping |
GET |
/api/drift/events |
Schema drift queue |
Admin routes are also available without the /api prefix. Local dev works without tokens when NODE_ENV is not production.
Full operator reference → docs/operator-guide.md.
UniSchema/
├── src/ # Hono API — mappers, egress, drift
├── frontend/ # React mapping canvas
├── tests/ # Vitest unit + integration
├── docs/ # Role guides + PhilanthroPy integration
├── examples/downstream/ # Analytics, dbt, PhilanthroPy pipelines
├── deploy/ # Fly.io, Railway, Terraform
├── samples/ # Demo webhook payloads
├── scripts/ # demo-webhook.sh, benchmarks
└── agents/ # Experimental drift agent (Python)
npm test # backend
npm run validate # full CI parity (backend + frontend + build)Docker image: ghcr.io/PhilanthroPy-Project/unischema:0.4.2
| Platform | Docs |
|---|---|
| Fly.io | deploy/fly.toml + deploy/README.md |
| Railway | deploy/railway.toml |
| Any host | Dockerfile |
Minimum production env vars → .env.example and operator guide.
MIT
