Governance
Manage governance through the REST API: policies, solutions (their workloads and applied policies), requirements and gate submissions, evidence uploads, reviewer decisions, waivers, go-live enforcement checks, metrics, and the audit trail. See Governance for how these fit together.
Every request needs an API key in the X-API-Key header:
governance:readforGETendpoints,governance:writefor everything that changes something.
Lists use the standard envelope { "data": [...], "meta": { "total", "limit", "nextCursor", "requestId" } } and accept limit (1 to 200, default 50) and cursor (the previous page's nextCursor). Single objects come back as { "data": {...}, "meta": { "requestId" } }. Errors are problem details: { "type", "title", "status", "detail", "code", "requestId" }.
| Status | code | When |
|---|---|---|
| 400 | validation-error | Invalid body or query (unknown category, stage that gates a type the policy does not apply to, approval gate without reviewers, value of the wrong type, and so on) |
| 403 | forbidden | Not allowed (for example editing someone else's policy, waiving without being an administrator, deciding a gate you are not a reviewer for) |
| 404 | not-found | Unknown or not visible to you |
| 409 | duplicate | The workload or policy is already on the solution |
| 413 | payload-too-large | Evidence file over the size limit |
| 422 | resource-in-use | Deleting a policy that a solution still applies |
| 422 | action-in-progress | The object is in the wrong state (applying a draft policy, requesting an approval that is already pending, waiving a requirement twice) |
Visibility. Administrators see everything. Other callers see policies they created plus every published policy in their organization, and solutions they own. Requirements of a solution are visible to everyone the solution can govern (the same organization, or everyone in a single-organization installation). The audit trail requires an administrator.
Policy object
{
"_id": "bMyhJrfArEfPzxEE6",
"name": "Production release checks",
"description": "Checks every release must pass",
"category": "Compliance",
"severity": "High",
"applicableResourceTypes": ["app", "workflow"],
"stages": [
{
"_id": "stage_intake",
"name": "Intake",
"description": "Describe the release",
"order": 1,
"gatedResourceTypes": [],
"fields": [
{
"_id": "field_risk",
"name": "risk_level",
"label": "Risk level",
"type": "dropdown",
"required": true,
"options": [{ "value": "low", "label": "Low" }, { "value": "high", "label": "High" }]
}
]
},
{
"_id": "stage_review",
"name": "Security review",
"description": "Security sign-off",
"order": 2,
"gatedResourceTypes": ["app"],
"fields": [],
"gate": {
"id": "gate_security",
"gateKind": "approval",
"name": "security_review",
"label": "Security review",
"description": "Security team sign-off",
"required": true,
"approvalConfig": {
"approvalLogic": "any",
"slaHours": 48,
"reviewers": [{ "type": "role", "identifier": "admin", "canDelegate": false, "isActive": true }]
}
}
}
],
"isActive": true,
"isDraft": false,
"tags": ["soc2"],
"version": 1,
"versionHistory": [],
"createdBy": "okRpaKPh8B9asdKb2",
"organizationId": "org-acme",
"createdAt": "2026-09-23T03:29:54.145Z",
"updatedAt": "2026-09-23T03:29:54.145Z"
}
| Field | Description |
|---|---|
category | Security, Compliance, Quality, or Operational. |
severity | Critical, High, Medium, Low, or Info. |
applicableResourceTypes | Resource type keys (see resource types). A stage can only gate these. |
isActive, isDraft | A policy is published when isActive is true and isDraft is false. Only published policies are enforced, produce requirements, and can be applied to solutions. |
version, versionHistory | version starts at 1 and goes up when stages, severity, or applicableResourceTypes change value. Each bump appends { version, changedBy, changedAt } to the history. |
createdBy, organizationId, createdAt, updatedAt, updatedBy | Set by the platform. |
Stages, fields, and gates
A stage is { _id?, name, description, order, fields, gatedResourceTypes, gate? }. name is required, order must be unique, and gatedResourceTypes lists the types the stage blocks (gating carries forward to later stages).
A field is { _id?, name, label, type, required, placeholder?, helpText?, options?, validation? }:
type:text,textarea,richtext,radio,checkbox,dropdown,multiselect,file,number,date,datetime,url,email.options(required forradio,checkbox,dropdown,multiselect):[{ "value", "label" }].validation:{ min?, max? }for numbers,{ minLength?, maxLength?, pattern? }for text, anderrorMessage?.- Only
required: truefields become requirements. A field's requirement id (gateId) is its_id, orfield-<stage order>-<field name>when it has none.
A gate is { id, gateKind, name, label, description?, required, ...config }. id must be unique within the policy. By gateKind:
gateKind | Config |
|---|---|
input | fieldType (a non-choice field type), placeholder, helpText, validation |
acknowledgment | none |
approval | approvalConfig: { approvalLogic, slaHours?, reviewers: [{ type, identifier, displayName?, isActive }] }. approvalLogic is any, all, or majority; reviewer type is user, role, or group; at least one reviewer. |
threshold | thresholdConfig: { metricName, operator, value, unit? }. operator is gte, lte, gt, lt, or eq. |
evidence | evidenceConfig: { allowedFileTypes?, maxFileSizeMB?, minFiles?, labels? } (minFiles defaults to 1) |
guardrail | guardrailConfig: { requiredGuardrailIds, minimumFilterLevel? } |
Reviewer identifiers: a user reviewer is the user's ID, a role reviewer a platform role name, and a group reviewer org:<orgId> (every member) or org:<orgId>:role:<memberRole> (owner, admin, member, or viewer). Take them from GET /api/v1/governance/reviewer-options, the same lists the Policy Builder offers. Saving a policy whose reviewer names no real user, role, or organization fails with 400 validation-error. The platform sets each reviewer's displayName from the record (for example Ada Park (ada@example.com), admin role, Members of Acme, Acme admins), so any displayName you send is replaced.
Policies
GET /api/v1/governance/policies
List the policies visible to you, newest first.
| Query | Description |
|---|---|
category, severity | Exact match. |
isActive, isDraft | true or false. |
tag | Only policies with this tag. |
q | Case-insensitive match on name or description. |
sort | Field list, - for descending (default -createdAt). |
limit, cursor | Paging. |
POST /api/v1/governance/policies
Create a policy. Body: name, description, category, severity, applicableResourceTypes, stages, isActive, isDraft (all required), and tags (optional). Any other field is rejected.
Response 201 Created: the created policy.
GET /api/v1/governance/policies/:id
Response 200 OK: the policy.
PATCH /api/v1/governance/policies/:id
Update a policy. Only its creator or an administrator can. Send any of name, description, category, severity, applicableResourceTypes, stages, isActive, isDraft, tags; stages replaces the whole list. The result is validated as a whole. Every solution applying the policy has its status recomputed.
Response 200 OK: the updated policy.
DELETE /api/v1/governance/policies/:id
Delete a policy with its submissions and evidence. Only its creator or an administrator can. Fails with 422 resource-in-use while a solution applies it.
Response 204 No Content.
GET /api/v1/governance/reviewer-options
What an approval reviewer can name, exactly as the Policy Builder offers it: the users you can see, the platform roles, and the organizations (every organization for an administrator, otherwise your own).
{
"data": {
"users": [{ "id": "okRpaKPh8B9asdKb2", "name": "Admin User (admin@strongly.ai)" }],
"roles": ["admin", "app", "developer", "reviewer"],
"organizations": [{ "id": "org-acme", "name": "Acme" }]
},
"meta": { "requestId": "5f0c2b8e-1a4d-4e7b-9c3f-8d2a6b1e4c70" }
}
GET /api/v1/governance/policies/:id/versions
{
"data": { "currentVersion": 2, "history": [{ "version": 1, "changedBy": "okRpaKPh8B9asdKb2", "changedAt": "2026-09-23T03:31:02.118Z" }] },
"meta": { "requestId": "5f0c2b8e-1a4d-4e7b-9c3f-8d2a6b1e4c70" }
}
Solution object
{
"_id": "HPce8npgxEveNLgrJ",
"name": "Support assistant",
"description": "Customer support assistant",
"workloads": [
{ "type": "app", "id": "Hk3bYw2kX9aPqL7cd", "name": "support-app" },
{ "type": "job", "id": "*", "name": "All Job resources" }
],
"policyIds": ["bMyhJrfArEfPzxEE6"],
"status": "in-progress",
"ownerId": "okRpaKPh8B9asdKb2",
"ownerName": "Admin User",
"organizationId": "org-acme",
"sharedWith": [],
"createdAt": "2026-09-23T03:29:55.037Z",
"updatedAt": "2026-09-23T03:29:55.471Z"
}
| Field | Description |
|---|---|
workloads | { type, id, name }. type is a resource type key; id "*" governs every resource of that type, including ones created later. |
policyIds | Applied policies. |
status | compliant (every requirement satisfied or waived, or no published policies), non-compliant (a requirement failed), or in-progress. |
Solutions
GET /api/v1/governance/solutions
List the solutions you own (administrators: all), most recently updated first.
| Query | Description |
|---|---|
status | compliant, non-compliant, or in-progress. |
policyId | Only solutions applying this policy. |
workloadType | Only solutions with a workload of this type. With workloadId, only solutions containing that exact workload (use * for type-level ones). |
q | Case-insensitive match on name or description. |
sort, limit, cursor | Sorting (default -updatedAt) and paging. |
POST /api/v1/governance/solutions
{
"name": "Support assistant",
"description": "Customer support assistant",
"workloads": [{ "type": "app", "id": "Hk3bYw2kX9aPqL7cd", "name": "support-app" }],
"policyIds": ["bMyhJrfArEfPzxEE6"]
}
name is required; workloads and policyIds are optional. Every policy must be visible to you and published. A type-level workload without a name is named "All Type resources".
Response 201 Created: the created solution.
GET /api/v1/governance/solutions/:id
Response 200 OK: the solution. Only its owner and administrators can read it here; see requirements for what others can see.
PATCH /api/v1/governance/solutions/:id
Rename a solution or change its description: { "name"?, "description"? }. Nothing else is editable here.
Response 200 OK: the updated solution.
DELETE /api/v1/governance/solutions/:id
Delete a solution with its submissions and evidence files. Response 204 No Content.
POST /api/v1/governance/solutions/:id/workloads
Add a workload: { "type": "app", "id": "<app id or *>", "name"?: "..." }. 409 duplicate if it is already there.
Response 200 OK: the updated solution.
DELETE /api/v1/governance/solutions/:solutionId/workloads/:workloadType/:id
Remove a workload (use * for a type-level one). Response 204 No Content.
POST /api/v1/governance/solutions/:id/policies
Apply a published policy: { "policyId": "..." }. Response 200 OK: the updated solution.
DELETE /api/v1/governance/solutions/:solutionId/policies/:id
Remove a policy and delete its submissions and evidence on this solution. Response 204 No Content.
POST /api/v1/governance/solutions/:id/recompute
Recompute and save the solution's status (it is also recomputed automatically after every change).
{
"data": { "status": "compliant", "total": 2, "satisfied": 2, "failed": 0, "pending": 0 },
"meta": { "requestId": "5f0c2b8e-1a4d-4e7b-9c3f-8d2a6b1e4c70" }
}
Requirements and gates
GET /api/v1/governance/solutions/:id/requirements
List every requirement (required fields and gates of the solution's published policies), in policy and stage order, each with its current submission. Paginated. Visible to everyone the solution can govern.
| Query | Description |
|---|---|
resourceType | Only the requirements that block this type: exactly what enforcement checks for it. |
{
"data": [
{
"gateId": "gate_security",
"gateKind": "approval",
"gateName": "Security review",
"gateLabel": "Security review",
"description": "Security team sign-off",
"policyId": "bMyhJrfArEfPzxEE6",
"policyName": "Production release checks",
"required": true,
"status": "pending",
"submission": null,
"gate": { "id": "gate_security", "gateKind": "approval", "approvalConfig": { "approvalLogic": "any", "slaHours": 48, "reviewers": [ ... ] } },
"stageName": "Security review",
"stageOrder": 2,
"gatedResourceTypes": ["app"],
"approvalProgress": {
"required": 1,
"current": 0,
"logic": "any",
"slaHours": 48,
"hoursElapsed": 0,
"reviewers": [{ "type": "role", "identifier": "admin" }]
}
}
],
"meta": {
"total": 1,
"limit": 50,
"nextCursor": null,
"requestId": "5f0c2b8e-1a4d-4e7b-9c3f-8d2a6b1e4c70"
}
}
status is pending, satisfied, failed, or waived. Field requirements carry the field definition and a synthetic input gate. Threshold requirements carry thresholdProgress (currentValue, requiredValue, operator, unit, met) and evidence requirements carry evidenceProgress (uploaded, required, allowedTypes, labels).
POST /api/v1/governance/solutions/:solutionId/gates/:id/submit
Submit one requirement. Body: { "policyId": "...", "data": { ... } }.
| Kind | data | Result |
|---|---|---|
| input | { "textValue" }, { "numberValue" }, { "dateValue" } (ISO date), or { "selectedOptions": [...] } for checkbox / multiselect fields | satisfied, or failed with failureReason when the value breaks the field's rules. A value of the wrong type is a 400. |
| acknowledgment | { "acknowledged": true } | satisfied |
| approval | {} | Requests approval: pending, reviewers are notified. 422 if approval is already pending or granted. Requesting again after a denial clears the previous decisions. |
| threshold | { "metricValue": 0.93 } | satisfied or failed |
| guardrail | { "guardrailsVerified": true } (optionally missingGuardrails) | satisfied (false records failed) |
| evidence | not accepted (400); use evidence upload |
The policy must be applied to the solution and published. Submitting again replaces the previous submission (and any waiver).
Response 200 OK: the gate submission.
{
"data": {
"_id": "NaFMYkuCJt9pB35fQ",
"solutionId": "HPce8npgxEveNLgrJ",
"policyId": "bMyhJrfArEfPzxEE6",
"gateId": "field_risk",
"gateKind": "input",
"status": "satisfied",
"textValue": "high",
"submittedBy": "okRpaKPh8B9asdKb2",
"submittedAt": "2026-09-23T03:29:56.643Z",
"organizationId": "org-acme",
"createdAt": "2026-09-23T03:29:56.643Z",
"updatedAt": "2026-09-23T03:29:56.643Z"
},
"meta": { "requestId": "5f0c2b8e-1a4d-4e7b-9c3f-8d2a6b1e4c70" }
}
Approval submissions also carry approvalStatus (pending, approved, denied), approvals ({ userId, userName, decision, comments, decidedAt, isAdmin }), and reviewerStatus (per reviewer entry: type, identifier, decision, decidedBy, decidedByName, decidedAt). Waived submissions carry waivedBy, waivedReason, waivedAt.
POST /api/v1/governance/solutions/:solutionId/gates/:id/evidence
Upload one evidence file to an evidence gate. Either:
multipart/form-datawith afilepart and apolicyIdfield, or- JSON
{ "policyId", "fileName", "contentType"?, "dataBase64" }(the JSON body is limited to 10 MB, about 7 MB of file once base64-encoded, so use multipart for larger files).
The file's extension must match the gate's allowed types, and its size must be within the gate's limit (at most 50 MB). The gate becomes satisfied once it has its minimum number of files.
Response 201 Created:
{
"data": { "_id": "evf123", "name": "report.pdf", "type": "application/pdf", "size": 52431, "url": "/api/v1/governance/evidence/evf123", "uploadedAt": "2026-09-23T03:40:11.201Z", "uploadedBy": "okRpaKPh8B9asdKb2" },
"meta": { "requestId": "5f0c2b8e-1a4d-4e7b-9c3f-8d2a6b1e4c70" }
}
Download and delete files with the evidence endpoints.
Reviews and waivers
GET /api/v1/governance/reviews/pending
Approval gates waiting for a decision that you can decide (administrators: all), newest first. Paginated list of gate submissions.
POST /api/v1/governance/reviews/:id/approve
Record a decision: { "decision": "approved" | "denied" | "conditional", "comments"?: "..." }.
commentsare required fordenied.- A reviewer can decide only for gates where they match a reviewer entry, and only while the gate is pending (they can change their own vote until then).
- Any reviewer denial fails the gate. The gate is satisfied when enough reviewer entries have approved for its
approvalLogic(majoritymeans more than half). - An administrator can decide any gate at any time, and an administrator's decision settles it (approved: satisfied, denied: failed).
conditionalis recorded with its comments and counts as neither approval nor denial.
Response 200 OK: the updated gate submission.
POST /api/v1/governance/reviews/:id/waive
Administrators only. { "reason": "..." } (required). The requirement must have been submitted at least once; a waived requirement counts as satisfied. Response 200 OK: the updated gate submission.
Check enforcement
GET /api/v1/governance/enforcement/check
Run the go-live check for a resource without going live.
| Query | Description |
|---|---|
resourceType | Required. A resource type key. |
resourceId | Required. The resource id. |
organizationId | Required. The organization the resource belongs to (or will be created in). Its solutions are the ones checked. In a multi-organization installation, only administrators can check another organization's resources (403 otherwise). |
{
"data": {
"resourceId": "Hk3bYw2kX9aPqL7cd",
"resourceType": "app",
"timestamp": "2026-09-23T03:29:55.989Z",
"allowed": false,
"message": "Deploy blocked: 1 pending requirement in solution \"Support assistant\".",
"pendingGates": [ { "gateId": "gate_security", "gateKind": "approval", "status": "pending", "...": "..." } ],
"failedGates": [],
"satisfiedGates": [],
"waivedGates": [],
"totalGates": 1,
"blockingSolutions": [{ "solutionId": "HPce8npgxEveNLgrJ", "solutionName": "Support assistant" }]
},
"meta": { "requestId": "5f0c2b8e-1a4d-4e7b-9c3f-8d2a6b1e4c70" }
}
The gate arrays hold requirements. A resource in no solution is allowed with totalGates: 0. In a multi-organization installation only the solutions of the resource's organization are checked, whoever runs the check.
Metrics
GET /api/v1/governance/metrics
Counts over what you can see:
{
"data": { "totalPolicies": 12, "activePolicies": 9, "totalSolutions": 4, "compliantSolutions": 2, "nonCompliantSolutions": 1, "inProgressSolutions": 1, "pendingApprovals": 3 },
"meta": { "requestId": "5f0c2b8e-1a4d-4e7b-9c3f-8d2a6b1e4c70" }
}
activePolicies counts published policies; pendingApprovals counts approval gates awaiting a decision on those solutions.
List resource types
GET /api/v1/governance/resource-types
The types solutions can govern and stages can gate:
{
"data": [ { "id": "app", "label": "App", "selectable": true }, { "id": "avatar", "label": "Avatar", "selectable": false } ],
"meta": { "requestId": "5f0c2b8e-1a4d-4e7b-9c3f-8d2a6b1e4c70" }
}
selectable: false types can only be added to a solution as "*". Keys: app, workflow, addon, workspace, volume, project, mlModel, fineTuningJob, automlJob, dataSource, agent, avatar, job, codeSession, abTest, marketplaceApp, aiGatewayModel, workflowTool, dataForge.
List audit entries
GET /api/v1/governance/audit
Administrators only. The governance audit trail, newest first.
| Query | Description |
|---|---|
entityType | policy, solution, gate-submission, or notification. |
entityId | Only entries for this entity. |
solutionId | Only entries about this solution or its requirements. |
action | Exact action, for example created, updated, workload_added, policy_removed, gate_submitted, gate_approved, gate_denied, gate_waived, gate_approved_admin_override. |
userId | Only entries by this user. |
q | Case-insensitive match on the entity name, entity type or ID, action, or user name or ID (what the Audit Log page's search box matches). |
startDate, endDate | ISO 8601 bounds on the entry time. |
sort, limit, cursor | Sorting (default -timestamp) and paging. |
{
"data": [
{
"_id": "FpbGki5MeJvZp5rYh",
"entityType": "gate-submission",
"entityId": "HPce8npgxEveNLgrJ:bMyhJrfArEfPzxEE6:gate_security",
"entityName": "Support assistant / SOC 2 readiness / Security review",
"action": "gate_approved",
"userId": "okRpaKPh8B9asdKb2",
"userName": "Admin User",
"metadata": {
"solutionId": "HPce8npgxEveNLgrJ",
"policyId": "bMyhJrfArEfPzxEE6",
"gateId": "gate_security",
"comments": "Pen test passed",
"isAdmin": true,
"isAdminOverride": false,
"previousStatus": "pending",
"resultingStatus": "satisfied"
},
"timestamp": "2026-09-23T03:29:59.962Z"
}
],
"meta": {
"total": 1,
"limit": 50,
"nextCursor": null,
"requestId": "5f0c2b8e-1a4d-4e7b-9c3f-8d2a6b1e4c70"
}
}
entityName is what the entry is about by name: the policy or solution name, or solution / policy / gate label for a requirement action. It is recorded when the entry is written, so it keeps the name the entity had then. Policy and solution entries carry previousState and newState instead of (or as well as) metadata.