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.
| Field | Type | Required? | Constraints |
|---|---|---|---|
name | string | Yes | Trimmed, 1–200. |
abbreviation | string | Yes | Trimmed, 1–50. |
echelon | string | Yes | Trimmed, 1–100. |
parentId | string/null | No | 1–255 or null. |
location | string/null | No | Max 200; nullish. |
mission | string/null | No | Max 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:
| Field | Type | Required? | Constraints |
|---|---|---|---|
name | string | No | Trimmed, 1–200. |
abbreviation | string | No | Trimmed, 1–50. |
echelon | string | No | Trimmed, 1–100. |
location | string/null | No | Max 200. |
mission | string/null | No | Max 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.
| Field | Type | Required? | Constraints |
|---|---|---|---|
parentId | string/null | Yes | 1–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.
| Field | Type | Required? | Constraints |
|---|---|---|---|
email | email string | Yes | Trimmed, max 255. |
name | string | Yes | Trimmed, 1–200. |
password | string | Yes | 14–128 and password regex. |
role | enum | Yes | Assignable government roles; cannot bypass tier policy. |
commandId | string/null | No | 1–255 or null. |
title | string/null | No | Max 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.
| Field | Type | Required? | Constraints |
|---|---|---|---|
userId | string | Yes | 1–255. |
role | enum | No | Assignable role. |
commandId | string/null | No | 1–255 or null. |
title | string/null | No | Max 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.
| Field | Type | Required? | Constraints |
|---|---|---|---|
title | string | Yes | Trimmed, 1–200. |
body | string | Yes | Trimmed, 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.
| Field | Type | Required? | Constraints |
|---|---|---|---|
title | string | Yes | Trimmed, 1–200. |
description | string | Yes | Trimmed, 1–8,000. |
techTags | string | No | Max 400; normalized lowercase CSV. |
closesAt | date string | No | YYYY-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.
| Field | Type | Required? | Constraints |
|---|---|---|---|
status | enum | Yes | OPEN 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.
| Field | Type | Required? | Constraints |
|---|---|---|---|
title | string | Yes | Trimmed, 1–200. |
type | enum | Yes | Nine EngagementType values. |
status | enum | Yes | PLANNED, COMPLETED, FOLLOW_UP, CLOSED. |
date | date string | Yes | YYYY-MM-DD. |
location | string | No | Max 200 or empty. |
summary | string | No | Max 4,000 or empty. |
organizationId | string | Yes | Nonempty ID. |
commandId | string | Yes | Nonempty 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.
| Field | Type | Required? | Constraints |
|---|---|---|---|
body | string | Yes | Trimmed, 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.
| Field | Type | Required? | Constraints |
|---|---|---|---|
status | enum | Yes | NEW, 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.
| Field | Type | Required? | Constraints |
|---|---|---|---|
body | string | Yes | Trimmed, 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
| Handler | Body/query | Passing audience | Typical denial |
|---|---|---|---|
| Admin command list | None | Government list policy | 401 or 403. |
| Admin command create/update/delete | JSON command fields | System admin | 403 for admin or command lead. |
| Parent reassignment | parentId string/null | System admin | 400 cycle, 403 role, 409 serialization conflict. |
| Admin users GET/POST/PATCH | User fields | Admin tier policy | 403 tier or protected-account rule. |
| Announcement create | title, body | Government publisher | 403 readonly/POC. |
| Call create/status | Call fields or status | Government publisher | 403 readonly/POC. |
| Engagement create/update/delete | Engagement fields | Government writer | 403 readonly/industry. |
| Engagement note | body | Government writer | 403 readonly/industry. |
| Organization status | status | Government writer | 403 readonly/industry. |
| Organization dialogue | body | Government writer | 403 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:
- A row is persisted with the current government author or open status.
- An audit event records the operation and actor.
- Industry
Notificationrows 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
| Family | Typical audit event or behavior |
|---|---|
| Command hierarchy | Reparent event includes previous and new parent IDs. |
| User administration | Role/assignment changes are recorded by admin helper. |
| Organization status | Status change includes organization and new status. |
| Dialogue and engagement notes | Author and parent record are attributable. |
| Engagement create/update | Creator and operation are recorded. |
| Announcement/call publication | Publisher 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 caller | Request | Expected |
|---|---|---|
| Industry contact | POST /api/gov/engagements | 403. |
GOV_READONLY | POST /api/gov/announcements | 403. |
GOV_POC | POST /api/gov/calls | 403 under publisher policy. |
lead.navsea@navy.mil | Edit NSWC DD | 403 exact-scope denial. |
admin@navy.mil | Reparent command | 403 system-admin-only. |
sysadmin@navy.mil | Reparent valid command | 200 if body and cycle checks pass. |
lead.navsea@navy.mil | Export CSV | 403 enterprise-export gate. |
admin@navy.mil | Export CSV | 200. |
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.