Access the demo at demo.iacuc.com. The API endpoints Swagger.
Important
This environment is not intended for use in production. It is refreshed daily, and the data entered is accessible to everyone.
If this project helps you, please consider giving it a ⭐ star or sponsoring the repository to support ongoing development!
This is a working web application for managing animal research protocols — the kind of system a university's animal care committee (IACUC) uses to review, approve, and track studies that use animals. It is a real web app: a browser interface that talks to an API, which stores everything in a database.
It is built the way an IACUC office actually works: every protocol carries a full application, a review trail, and an animal-usage ledger.
Note
Technical setup, architecture, and deployment details are in Part 2 at the bottom of this page.
- Track protocols through their real lifecycle — draft, submitted, under review, approved, active.
- Complete a full IACUC application (the Appendix A form): purpose and harm-vs-benefit summaries, a 15-item procedures checklist, drug and dosing tables, animal use, experiments, and the Three Rs (replacement, refinement, reduction) with a required literature search.
- Submit with confidence. The app checks every required section before a protocol can be submitted, and tells you what's missing.
- Run committee review. Full-committee (FCR) and designated-member (DMR) methods, reviewer assignments, section comments, and live vote tallies.
- Track actual animal use. The register logs real orders and uses against a protocol's approved allowance and flags over-allowance.
- Check personnel compliance — CITI training and OHSP clearance at a glance for everyone on a protocol.
- Try everything in the hosted demo (link above) — no setup needed.
The database at a glance (Click to expand)
The app's data lives in 17 tables — one for each protocol, one set of reference lists (species, roles, personnel), and one set of child records that hang off each protocol (the application content, the review records, and the animal-usage ledger).
Full diagram — best viewed in its own tab so you can zoom in and out (Tip: hold Ctrl/Cmd and click the link to open it in a new tab):
Four things worth knowing without opening the diagram:
- One protocol, many details. Everything about a protocol — its application (procedures, drugs, animal use, experiments, 3 Rs entries), its review records (votes, reviewer assignments, comments), and its usage ledger — is stored as child rows that belong to that protocol and are removed with it.
- Personnel are shared. The same person (PI, committee member, vet) appears once in the personnel list and can be linked to any number of protocols.
- Planned vs. actual. The planned animal numbers live with the protocol (the approved allowance); the animal-usage register records the actual orders and uses. The register compares the two so staff can see at a glance whether a protocol is within or over its allowance.
- One unique case. Each protocol has exactly one research-search record; every other protocol child table can have many rows.
Install, run, and explore the API (Click to expand)
Requires Node 22.5 or newer.
npm installnpm workspaces installs both server/ and client/ dependencies in one
pass and links the workspace together (single package-lock.json at the
root). The server uses Node's built-in node:sqlite module rather than
a native addon package, so this install never needs to compile anything —
no Visual Studio Build Tools, no Python, no prebuilt-binary lookups.
copy server\.env.example server\.env # Windows
# cp server/.env.example server/.env # macOS/Linux
npm run seed # creates server/data/iacuc.db with sample protocols
npm run dev:server # http://localhost:4000In a second terminal:
npm run dev:client # http://localhost:5173Vite proxies any /api/* request to http://localhost:4000 in dev (see
client/vite.config.js), so the frontend never needs a hardcoded API URL.
Visit http://localhost:5173. Clicking a row on the list page navigates to
/protocols/:id (a real URL, so refresh/back/forward all work correctly)
and fetches that record from the API.
npm run seed --workspace=server
npm run build --workspace=client
cd server && npm run devThe full API surface is listed below. When the app is running locally, a
searchable interactive reference with a "try it" button is available at
http://localhost:4000/api-docs (Swagger UI).
Core protocol CRUD (Click to expand)
| Method | Path | Description | Status |
|---|---|---|---|
| GET | /api/health | Liveness check | ✓ |
| GET | /api/protocols | List protocols, optional ?q= search |
✓ |
| GET | /api/protocols/summary | Dashboard metric counts | ✓ |
| GET | /api/protocols/:id | Single protocol + related items | ✓ |
| POST | /api/protocols | Create a protocol (starts as Draft) | ✓ |
| PATCH | /api/protocols/:id | Update fields / advance workflow stage | ✓ |
| DELETE | /api/protocols/:id | Delete a protocol | ✓ |
Appendix A application content (per protocol) (Click to expand)
| Method | Path | Description | Status |
|---|---|---|---|
| GET | /api/protocols/:id/procedures | 15-item procedures checklist (with surgery detail fields) | ✓ |
| PUT | /api/protocols/:id/procedures | Replace the checklist for a protocol | ✓ |
| GET | /api/protocols/:id/drugs | Drug/dosing table | ✓ |
| POST | /api/protocols/:id/drugs | Add a drug row | ✓ |
| PATCH | /api/protocols/:id/drugs/:drugId | Update a drug row | ✓ |
| DELETE | /api/protocols/:id/drugs/:drugId | Remove a drug row | ✓ |
| GET | /api/protocols/:id/animal-use | Planned animal-use table (species/strain/sex/age/count) | ✓ |
| POST | /api/protocols/:id/animal-use | Add an animal-use row | ✓ |
| PATCH | /api/protocols/:id/animal-use/:rowId | Update an animal-use row | ✓ |
| DELETE | /api/protocols/:id/animal-use/:rowId | Remove an animal-use row | ✓ |
| GET | /api/protocols/:id/experiments | Experiments (endpoints, monitoring, husbandry) | ✓ |
| POST | /api/protocols/:id/experiments | Add an experiment | ✓ |
| PATCH | /api/protocols/:id/experiments/:expId | Update an experiment | ✓ |
| DELETE | /api/protocols/:id/experiments/:expId | Remove an experiment | ✓ |
| GET | /api/protocols/:id/rrr | Structured 3 Rs justifications (Replacement/Refinement/Reduction) | ✓ |
| POST | /api/protocols/:id/rrr | Add a 3 Rs entry | ✓ |
| PATCH | /api/protocols/:id/rrr/:entryId | Update a 3 Rs entry | ✓ |
| DELETE | /api/protocols/:id/rrr/:entryId | Remove a 3 Rs entry | ✓ |
| GET | /api/protocols/:id/alternatives | 3 Rs & alternatives summary (literature search, colleague consult, AV consult) | ✓ |
| PATCH | /api/protocols/:id/alternatives | Update the alternatives block | ✓ |
| GET | /api/protocols/:id/validation | Per-section submission completeness + overall |
✓ |
Animal usage register (the ledger) (Click to expand)
| Method | Path | Description | Status |
|---|---|---|---|
| GET | /api/protocols/:id/animal-usage | Per-species/pain-category/procedure tallies vs. the approved allowance | ✓ |
| POST | /api/protocols/:id/animal-usage | Log an ordering/usage transaction | ✓ |
Admin lookup lists (Click to expand)
| Method | Path | Description | Status |
|---|---|---|---|
| GET | /api/admin/species | List species | ✓ |
| POST | /api/admin/species | Create a species | ✓ |
| DELETE | /api/admin/species/:id | Delete a species (blocked if in use) | ✓ |
| GET | /api/admin/roles | List roles | ✓ |
| POST | /api/admin/roles | Create a role | ✓ |
| DELETE | /api/admin/roles/:id | Delete a role | ✓ |
| GET | /api/admin/personnel | List personnel | ✓ |
| POST | /api/admin/personnel | Create a personnel member | ✓ |
| DELETE | /api/admin/personnel/:id | Delete a personnel member | ✓ |
Committee / review workflow (Click to expand)
| Method | Path | Description | Status |
|---|---|---|---|
| GET | /api/committee/protocols | Protocols in review, with votes/assignments/comments | ✓ |
| GET | /api/committee/voters | Committee-eligible voters | ✓ |
| GET | /api/committee/protocols/:id/votes | Vote history + live tally for a protocol | ✓ |
| POST | /api/committee/protocols/:id/votes | Cast a vote | ✓ |
| GET | /api/committee/protocols/:id/reviews | Full review history (votes + assignments + comments) | ✓ |
| POST | /api/committee/protocols/:id/reviews | Submit a review (Approved / Modifications Required / Tabled) | ✓ |
| POST | /api/committee/protocols/:id/comments | Add a section-specific review comment | ✓ |
| PATCH | /api/committee/protocols/:id/assign | Upsert a reviewer assignment (Primary/Secondary/Designated Member) | ✓ |
| PATCH | /api/committee/protocols/:id/review-method | Set review method (FCR / DMR) |
✓ |
Personnel compliance (CITI training + OHSP clearance) (Click to expand)
| Method | Path | Description | Status |
|---|---|---|---|
| GET | /api/personnel/compliance | All personnel with derived training/OHSP/compliant status | ✓ |
| GET | /api/personnel/:id/training | A person's training records + overall status | ✓ |
| POST | /api/personnel/:id/training | Add a training record | ✓ |
| PATCH | /api/personnel/:id/training/:trainingId | Update a training record (e.g. extend an expiry) | ✓ |
| DELETE | /api/personnel/:id/training/:trainingId | Remove a training record | ✓ |
| GET | /api/personnel/:id/ohsp | OHSP clearance row (defaults to Pending) | ✓ |
| POST | /api/personnel/:id/ohsp | Upsert OHSP status (Pending/Cleared/Denied) |
✓ |
| GET | /api/protocols/:id/personnel | Per-listed-person compliance + all_compliant for a protocol |
✓ |
Transfer ownership (Click to expand)
Transfers are an approval workflow, not an instant reassignment: the request
sits in the IACUC-office queue (Pending) until approved, and approving
reassigns the protocol's principal investigator.
| Method | Path | Description | Status |
|---|---|---|---|
| GET | /api/transfers | The transfer queue, optional ?status= filter |
✓ |
| POST | /api/transfers | Bulk-transfer multiple protocols to one new PI (all-or-nothing) | ✓ |
| POST | /api/protocols/:id/transfers | Request a transfer for a single protocol | ✓ |
| PATCH | /api/transfers/:id | Decide a request (Approved reassigns the PI / Rejected) |
✓ |
Audit log (who changed what, when) (Click to expand)
Every write in the app records an entry in an append-only audit trail — the
admin page's "Audit log" panel shows it. Because there is no login yet, the
"who" is only trustworthy where a request already carries an identity (votes,
comments, reviewer assignments, personnel/OHSP actions); everything else is
recorded as system.
| Method | Path | Description | Status |
|---|---|---|---|
| GET | /api/audit | The trail, newest first; filters entity_type, entity_id, actor, action, provenance, from/to, limit, offset |
✓ |
Compliance reports (AAALAC-style) (Click to expand)
Canned reports aggregated from the Appendix A application content, shown on the "Reports" tab with a CSV export for each. Read-only.
| Method | Path | Description | Status |
|---|---|---|---|
| GET | /api/reports | Restraint by species, euthanasia methods by species, surgery locations/types, multiple major recovery surgery, analgesic/anesthetic drugs, use locations by species | ✓ |
Saved search filters (dashboard) (Click to expand)
Named, recallable filter sets built from the dashboard's filter-builder
(optional ?search_type=protocol|register scoping on the list call).
| Method | Path | Description | Status |
|---|---|---|---|
| GET | /api/saved-filters | List saved filters, optionally ?search_type= |
✓ |
| POST | /api/saved-filters | Save the current filter set (name + clause array) | ✓ |
| DELETE | /api/saved-filters/:id | Delete a saved filter | ✓ |
| GET | /api/protocols?q=&filters=[{field,op,value},…] | Dashboard list with the stackable filter-builder | ✓ |
| GET | /api/animal-usage?filters=[…] | Register-wide search with the filter-builder (the Register tab) | ✓ |
Not implemented yet (Click to expand)
Tracked in docs/UI-EXPANSION-PLAN.md (domains B, E, F) and ROADMAP.md.
Do not build against these yet; paths are subject to change as the schema
lands.
| Domain | Planned endpoint(s) | Source | Status |
|---|---|---|---|
| File attachments | Real uploads for protocol narratives, SOPs, training certs — not planned | ROADMAP item 7 | ✗ |
| AAALAC compliance reports | Restraint/euthanasia/surgery/drug reports by species | ROADMAP item 9 | ✗ |
Amendments & renewals, PAM & incidents, facilities & inspections, transfer
ownership, and audit logging were previously listed here but are now
implemented — see the API reference tables above and
docs/UI-EXPANSION-PLAN.md.
The full table definitions are in server/src/db.js, and the diagram is
generated from docs/database-schema.mmd (how-to in AGENTS.md).
Every route in server/src/routes/protocols.js only talks to the db
object exported from server/src/db.js. To move to Postgres:
npm install pg --workspace=server- Replace the contents of
db.jswith apg.Poolconnection and rewrite the handful of prepared statements inprotocols.jsas parameterizedpool.query(...)calls (mostly 1:1 — same SQL, different driver). - Point
DB_PATH/connection string at your Postgres instance via.env.
Nothing in the client needs to change, since it only ever talks to the
/api/protocols HTTP endpoints.
- Server: any Node host (Render, Fly.io, Railway, a VPS). Set
CLIENT_ORIGINto your deployed frontend's URL for CORS. - Client:
npm run build --workspace=clientproduces static files inclient/dist/that can be served from any static host (Vercel, Netlify, S3+CloudFront). Point its API calls at your deployed server URL instead of the dev proxy (e.g. via aVITE_API_URLenv var and updatingapi.js).
ROADMAP.md— the overall product roadmapdocs/UI-EXPANSION-PLAN.md— planned domains (amendments & renewals, facilities & inspections, incidents)