Project ROUNDTABLE Docs
API reference

Government API

Government ledger, publishing, and administration route handlers

Every route in this page starts with a government session check. A government session is not enough for every mutation: readonly users, POCs, command leads, administrators, and system administrators have different gates. Examples use admin@navy.mil, sysadmin@navy.mil, NAVSEA, NIWC PAC, and Pelagic Dynamics.

Admin commands

GET /api/gov/admin/commands

Auth: admin-tier government session (GOV_COMMAND_LEAD, GOV_ADMIN, or GOV_SYSTEM_ADMIN). Success: 200.

curl http://localhost:3000/api/gov/admin/commands -H 'cookie: next-auth.session-token=SESSION'

Example: { "data": [{ "id": "clx-navsea", "name": "Naval Sea Systems Command", "abbreviation": "NAVSEA", "echelon": "SYSCOM", "parentId": null, "location": "Washington, DC", "mission": "..." }] }.

Errors are 401, 403 where the route helper denies the role, and 500 for database failure.

POST /api/gov/admin/commands

Auth: GOV_SYSTEM_ADMIN; success: 201.

FieldTypeRequired?Constraints
namestringYesTrimmed, 1–200.
abbreviationstringYesTrimmed, 1–50.
echelonstringYesTrimmed, 1–100.
parentIdstring/nullNo1–255 or null.
locationstring/nullNoMax 200; nullish.
missionstring/nullNoMax 4,000; nullish.
curl -X POST http://localhost:3000/api/gov/admin/commands \
  -H 'content-type: application/json' -H 'cookie: next-auth.session-token=SYSTEM_SESSION' \
  -d '{"name":"Demo Maritime Integration Office","abbreviation":"DEMO MIO","echelon":"Office","parentId":"clx-navsea","location":"San Diego, CA","mission":"Coordinate pilot demonstrations."}'

Success: { "data": { "id": "clx-command", "name": "...", "abbreviation": "DEMO MIO" } }. Errors: 400 invalid body, 401/403 gate, 409 duplicate name/abbreviation, 500.

GET|PATCH|DELETE /api/gov/admin/commands/[id]

Auth: GET uses government visibility; PATCH/DELETE require system-admin authority.

PATCH fields:

FieldTypeRequired?Constraints
namestringNoTrimmed, 1–200.
abbreviationstringNoTrimmed, 1–50.
echelonstringNoTrimmed, 1–100.
locationstring/nullNoMax 200.
missionstring/nullNoMax 4,000.
curl -X PATCH http://localhost:3000/api/gov/admin/commands/clx-demo \
  -H 'content-type: application/json' -H 'cookie: next-auth.session-token=SYSTEM_SESSION' \
  -d '{"mission":"Updated pilot coordination mission"}'

GET returns { "data": <command with relations> }; PATCH returns { "data": <updated command> }; DELETE returns { "data": { "id": "clx-demo" } }. GET errors are 401/403/404/500; mutation errors add 400 and 409. Delete can fail when relations or policy make the command ineligible.

PATCH /api/gov/admin/commands/[id]/parent

Auth: GOV_SYSTEM_ADMIN; success: 200.

FieldTypeRequired?Constraints
parentIdstring/nullYes1–255 or null.
curl -X PATCH http://localhost:3000/api/gov/admin/commands/clx-nrl/parent \
  -H 'content-type: application/json' -H 'cookie: next-auth.session-token=SYSTEM_SESSION' \
  -d '{"parentId":"clx-onr"}'

Success returns { "data": <updated command> }. Errors include 400 invalid body/self-parent/cycle, 401/403, 404 missing command/parent, and 500 transaction failure. The operation checks the graph before applying the parent.

Admin users

GET /api/gov/admin/users

Auth: admin-tier government session. Success: 200.

curl http://localhost:3000/api/gov/admin/users -H 'cookie: next-auth.session-token=ADMIN_SESSION'

Response is { "data": [{ "id": "...", "email": "poc.ai@navy.mil", "name": "Dr. Marcus Vela", "role": "GOV_POC", "commandId": "...", "title": "AI/ML Portfolio Lead" }] }. Password hashes are not returned. Errors: 401/403/500.

POST /api/gov/admin/users

Auth: admin-tier policy; assignable roles are validated by the route.

FieldTypeRequired?Constraints
emailemail stringYesTrimmed, max 255.
namestringYesTrimmed, 1–200.
passwordstringYes14–128 and password regex.
roleenumYesAssignable government roles; cannot bypass tier policy.
commandIdstring/nullNo1–255 or null.
titlestring/nullNoMax 200.
curl -X POST http://localhost:3000/api/gov/admin/users \
  -H 'content-type: application/json' -H 'cookie: next-auth.session-token=ADMIN_SESSION' \
  -d '{"email":"demo.poc@navy.mil","name":"Demo POC","password":"Roundtable2026!Demo","role":"GOV_POC","commandId":"clx-niwcpac","title":"Pilot POC"}'

Success 201 returns a sanitized user shape. Errors: 400 validation or role/command policy, 401/403, 409 duplicate email, 500.

PATCH /api/gov/admin/users

Auth: admin-tier policy.

FieldTypeRequired?Constraints
userIdstringYes1–255.
roleenumNoAssignable role.
commandIdstring/nullNo1–255 or null.
titlestring/nullNoMax 200.
curl -X PATCH http://localhost:3000/api/gov/admin/users \
  -H 'content-type: application/json' -H 'cookie: next-auth.session-token=ADMIN_SESSION' \
  -d '{"userId":"clx-user","role":"GOV_COMMAND_LEAD","commandId":"clx-navsea","title":"NAVSEA lead"}'

Success is { "data": <sanitized user> }. Errors: 400, 401/403, 404, policy protection for last system admin, and 500. The next session refresh sees changed authorization attributes from Prisma.

Publishing

POST /api/gov/announcements

Auth: government publisher (route helper). Success: 201.

FieldTypeRequired?Constraints
titlestringYesTrimmed, 1–200.
bodystringYesTrimmed, 1–8,000.
curl -X POST http://localhost:3000/api/gov/announcements \
  -H 'content-type: application/json' -H 'cookie: next-auth.session-token=ADMIN_SESSION' \
  -d '{"title":"ROUNDTABLE pilot update","body":"Industry office hours are available next week."}'

Success { "data": { "id": "clx-announcement" } }. Errors: 400, 401/403, 500. The route creates a published row, audits it, and notifies industry users.

POST /api/gov/calls

Auth: government publisher. Success: 201.

FieldTypeRequired?Constraints
titlestringYesTrimmed, 1–200.
descriptionstringYesTrimmed, 1–8,000.
techTagsstringNoMax 400; normalized lowercase CSV.
closesAtdate stringNoYYYY-MM-DD.
curl -X POST http://localhost:3000/api/gov/calls \
  -H 'content-type: application/json' -H 'cookie: next-auth.session-token=ADMIN_SESSION' \
  -d '{"title":"Maritime AI for Predictive Maintenance","description":"Share relevant maintenance analytics capabilities.","techTags":"ai/ml,predictive maintenance","closesAt":"2026-04-30"}'

Success { "data": { "id": "clx-call" } }; errors 400, 401/403, 500. Creation uses OPEN status and notifies industry users.

PATCH /api/gov/calls/[id]

Auth: government publisher.

FieldTypeRequired?Constraints
statusenumYesOPEN or CLOSED.
curl -X PATCH http://localhost:3000/api/gov/calls/clx-call \
  -H 'content-type: application/json' -H 'cookie: next-auth.session-token=ADMIN_SESSION' \
  -d '{"status":"CLOSED"}'

Success { "data": { "id": "clx-call", "status": "CLOSED" } }; errors 400, 401/403, 404, 500.

Engagements

POST /api/gov/engagements

Auth: requireGovWrite; success 201.

FieldTypeRequired?Constraints
titlestringYesTrimmed, 1–200.
typeenumYesNine EngagementType values.
statusenumYesPLANNED, COMPLETED, FOLLOW_UP, CLOSED.
datedate stringYesYYYY-MM-DD.
locationstringNoMax 200 or empty.
summarystringNoMax 4,000 or empty.
organizationIdstringYesNonempty ID.
commandIdstringYesNonempty ID.
curl -X POST http://localhost:3000/api/gov/engagements \
  -H 'content-type: application/json' -H 'cookie: next-auth.session-token=ADMIN_SESSION' \
  -d '{"title":"Pelagic Dynamics autonomy demo","type":"DEMO","status":"PLANNED","date":"2026-03-20","location":"NIWC Pacific","summary":"Review UUV autonomy capability.","organizationId":"clx-pelagic","commandId":"clx-niwcpac"}'

Success { "data": { "id": "clx-engagement" } }. Errors: 400 field/foreign-key validation, 401/403 role/scope, 404 referenced record, 500. Current user becomes createdBy; engagement_created is audited.

PATCH /api/gov/engagements/[id]

Auth: requireGovWrite; request fields are the same eight fields above and all are required by updateSchema.

curl -X PATCH http://localhost:3000/api/gov/engagements/clx-engagement \
  -H 'content-type: application/json' -H 'cookie: next-auth.session-token=ADMIN_SESSION' \
  -d '{"title":"Pelagic Dynamics autonomy demo","type":"DEMO","status":"COMPLETED","date":"2026-03-20","location":"NIWC Pacific","summary":"Completed technical demonstration.","organizationId":"clx-pelagic","commandId":"clx-niwcpac"}'

Success { "data": { "id": "clx-engagement" } }; errors 400, 401/403, 404, 500.

DELETE /api/gov/engagements/[id]

Auth: requireGovWrite. No body.

curl -X DELETE http://localhost:3000/api/gov/engagements/clx-engagement \
  -H 'cookie: next-auth.session-token=ADMIN_SESSION'

Success { "data": { "id": "clx-engagement" } }; errors 401/403, 404, 500. Related notes may be removed according to Prisma relation behavior in the route/schema.

POST /api/gov/engagements/[id]/notes

Auth: requireGovWrite.

FieldTypeRequired?Constraints
bodystringYesTrimmed, 1–4,000.
curl -X POST http://localhost:3000/api/gov/engagements/clx-engagement/notes \
  -H 'content-type: application/json' -H 'cookie: next-auth.session-token=ADMIN_SESSION' \
  -d '{"body":"Follow up with the autonomy portfolio lead."}'

Success 201 { "data": { "id": "clx-note" } }; errors 400, 401/403, 404, 500.

Organization government routes

PATCH /api/gov/organizations/[id]/status

Auth: requireGovWrite.

FieldTypeRequired?Constraints
statusenumYesNEW, UNDER_REVIEW, ACTIVE, WATCHLIST, INACTIVE.
curl -X PATCH http://localhost:3000/api/gov/organizations/clx-pelagic/status \
  -H 'content-type: application/json' -H 'cookie: next-auth.session-token=ADMIN_SESSION' \
  -d '{"status":"ACTIVE"}'

Success { "data": { "id": "clx-pelagic", "status": "ACTIVE" } }; errors 400, 401/403, 404, 500.

POST /api/gov/organizations/[id]/dialogue

Auth: requireGovWrite.

FieldTypeRequired?Constraints
bodystringYesTrimmed, 1–4,000.
curl -X POST http://localhost:3000/api/gov/organizations/clx-pelagic/dialogue \
  -H 'content-type: application/json' -H 'cookie: next-auth.session-token=ADMIN_SESSION' \
  -d '{"body":"Coordinate a follow-up with the undersea portfolio."}'

Success 201 { "data": { "id": "clx-dialogue-note" } }; errors 400, 401/403, 404, 500. This note is internal government context.

Government status and role summary

HandlerBody/queryPassing audienceTypical denial
Admin command listNoneGovernment list policy401 or 403.
Admin command create/update/deleteJSON command fieldsSystem admin403 for admin or command lead.
Parent reassignmentparentId string/nullSystem admin400 cycle, 403 role, 409 serialization conflict.
Admin users GET/POST/PATCHUser fieldsAdmin tier policy403 tier or protected-account rule.
Announcement createtitle, bodyGovernment publisher403 readonly/POC.
Call create/statusCall fields or statusGovernment publisher403 readonly/POC.
Engagement create/update/deleteEngagement fieldsGovernment writer403 readonly/industry.
Engagement notebodyGovernment writer403 readonly/industry.
Organization statusstatusGovernment writer403 readonly/industry.
Organization dialoguebodyGovernment writer403 readonly/industry.

Error envelope examples

Invalid request:

{"error":"Invalid request"}

Unauthorized:

{"error":"Unauthorized"}

Forbidden:

{"error":"Forbidden"}

Not found:

{"error":"Not found"}

The exact punctuation varies between older portal handlers and shared government helpers, but callers should branch on HTTP status and treat the message as human-readable rather than a stable machine code.

Engagement date handling

The API accepts an ISO calendar date such as 2026-03-20, then stores a JavaScript DateTime. The UI and export code commonly display the date portion. Clients should send a date rather than a locale-formatted string such as 03/20/2026, because Zod's YYYY-MM-DD regular expression rejects locale formats.

Publishing side effects

Publishing an announcement or call has three observable outcomes:

  1. A row is persisted with the current government author or open status.
  2. An audit event records the operation and actor.
  3. Industry Notification rows may be created according to each recipient's settings.

If the database write succeeds but a notification fan-out fails, inspect server logs and the resulting record before retrying. Repeating a publish request can create another publication unless the client first checks current state.

Command safety

The parent endpoint accepts null to detach a command to the top level. It does not accept an empty string as a parent ID because the schema requires a nonempty string when non-null. The serializable transaction re-reads the current parent before writing so the audit detail reflects the value actually replaced.

Request construction notes

Government JSON routes reject malformed JSON before database work. Send content-type: application/json and serialize dates as YYYY-MM-DD. Do not send Prisma enum values with spaces or display labels: use exact values such as INDUSTRY_FORUM, FOLLOW_UP, UNDER_REVIEW, and GOV_SYSTEM_ADMIN.

IDs in examples are deliberately symbolic. Resolve them from a government list response or a seeded database; never infer a cuid from a display name. A route that accepts an ID still verifies that the referenced row exists and, where applicable, that the current role may operate on it.

Audit side effects by route family

FamilyTypical audit event or behavior
Command hierarchyReparent event includes previous and new parent IDs.
User administrationRole/assignment changes are recorded by admin helper.
Organization statusStatus change includes organization and new status.
Dialogue and engagement notesAuthor and parent record are attributable.
Engagement create/updateCreator and operation are recorded.
Announcement/call publicationPublisher and public title are recorded.

The audit event name is more stable than the human-facing success body. Consumers should use HTTP status and their own operation ID rather than scraping an error sentence.

Permission test matrix

Test callerRequestExpected
Industry contactPOST /api/gov/engagements403.
GOV_READONLYPOST /api/gov/announcements403.
GOV_POCPOST /api/gov/calls403 under publisher policy.
lead.navsea@navy.milEdit NSWC DD403 exact-scope denial.
admin@navy.milReparent command403 system-admin-only.
sysadmin@navy.milReparent valid command200 if body and cycle checks pass.
lead.navsea@navy.milExport CSV403 enterprise-export gate.
admin@navy.milExport CSV200.

Use disposable records for mutation tests. A 403 is evidence of the gate only when the request reached the intended route and the session identity is the account described.