UniSchema

July 24, 2026 · View on GitHub

CI License: MIT Node DOI

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.

UniSchema mapping canvas — visually map vendor webhook fields onto the ConstituentEvent master schema


Who it's for

  • 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).


Ecosystem

flowchart LR
  Vendors[VendorWebhooks] --> UniSchema[UniSchema]
  UniSchema --> Egress[LocalOrS3Egress]
  Egress --> Features[FeatureTable]
  Features --> PhilanthroPy[PhilanthroPy]
  Features --> CRM[CRMGoldenRecord]
  CRM --> PhilanthroPy
  PhilanthroPy --> Scores[AffinityAndLapseScores]
ProjectRole
UniSchema (this repo)Ingest webhooks → validate → map → egress ConstituentEvent
PhilanthroPysklearn-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.


Quick start (~15 minutes)

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
  1. Open http://localhost:3000 — mapping canvas + API together
  2. In another terminal: bash scripts/demo-webhook.sh (single webhook) or bash scripts/demo-webhook.sh --multi (all vendors)
  3. See ConstituentEvent JSON under data/egress/
  4. Prove downstream value: bash scripts/downstream-demo.sh (needs python3)
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

Choose your guide

I am…Start here
New adopter — first webhook in ~15 minQuick start above
Admin / analyst — drawing mapping lines on the canvasdocs/admin-guide.md
Operator — secrets, S3 egress, cloud deploydocs/operator-guide.md
Developer — adding vendor #9docs/adding-a-vendor.md
Data engineer — warehouse + dbtdocs/downstream-pipeline.md
Data scientist / ML engineer — PhilanthroPy scoringdocs/philanthropy-integration.md
All docsdocs/README.md

Canvas vs code (what needs a deploy)

The visual mapper overrides fields on registered vendors — it does not create new webhook routes.

TaskCanvasRequires deploy
Remap fields → normalizedMetadataYesNo
Override built-in field wiringYesNo
New POST /webhooks/{vendor} routeNoYes (6-file checklist)
HMAC secret + Zod payload schemaNoYes

Details → docs/canvas-vs-code.md


Maturity & limits

StageStackThroughput (typical)Guide
Pilot (~15 min)Docker + SQLite + local egress~600–900 req/min (Docker, limit raised)Quick start
ProductionFly/Railway + S3 + Postgres optional~120 req/min/IP default; tune for giving dayOperator guide
ScalePostgres + Redis + multi-instanceBenchmark before peak — npm run benchmarkBenchmarks
Today (v0.4.2)Limits
8 vendors: GiveCampus, Cvent, iModules, Blackbaud, NPSP, Slate, Ellucian, CiviCRMTier 3 — verify with real payloads
Tier 1: GiveCampus, Cvent · Tier 2: iModules · Tier 3: Blackbaud, NPSP, Slate, Ellucian, CiviCRMEllucian + CiviCRM are bootstrap Tier 3
SQLite default + optional PostgresHorizontal scale needs Postgres + Redis
HMAC webhook verification~120 req/min/IP default
Visual canvas + metadata mappingsOpinionated master schema — details
Local + S3 egress → PhilanthroPy ML bridgeML requires optional pip install -r examples/downstream/requirements-philanthropy.txt
Drift queue + experimental LLM agentHuman review requiredai-agent-loop
3 event types: registration, donation, email clickNew types via RFC — schema-governance

Vendor registry (8 built-in) → docs/README.md#vendor-registry


Downstream and ML

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

Citing

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.


API (summary)

MethodPathDescription
GET/healthHealth check (version, egress, driftPendingCount)
GET/api/vendorsVendor registry with tier metadata
POST/webhooks/{vendor}Vendor webhooks (202)
GET/webhooks/ingestions/:idPoll async status (Bearer auth in production)
POST/api/mappings/syncSave canvas mapping (Bearer auth in production)
POST/api/mappings/previewPreview ConstituentEvent from artifact
GET/api/mappings/:vendorLoad canvas mapping
GET/api/drift/eventsSchema 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.


Project layout

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)

Testing

npm test                  # backend
npm run validate          # full CI parity (backend + frontend + build)

Cloud deploy

Deploy on Fly.io Deploy on Railway

Docker image: ghcr.io/PhilanthroPy-Project/unischema:0.4.2

PlatformDocs
Fly.iodeploy/fly.toml + deploy/README.md
Railwaydeploy/railway.toml
Any hostDockerfile

Minimum production env vars.env.example and operator guide.


License

MIT