API Reference
The /api/v1 agent surface — authentication, error envelope, rate limits, the OpenAPI document, and every endpoint with request and response examples.
The agent API is a small, explicit REST surface. Each endpoint requires one
scope, maps to one
operation the web app already performs, and answers 405 Method Not Allowed
on every verb it does not implement — so a script can never mistake an HTML
page or a silent no-op for success.
Basics
| Base URL | https://api.bedrock-cmmc.com/api/v1 |
| Authentication | Authorization: Bearer bcmmc_<id>_<secret> on every request |
| Content type | JSON in and out (Content-Type: application/json on requests with a body) |
| Rate limit | 300 requests per minute per key |
| Discovery | GET /api/v1/openapi.json — OpenAPI 3.1, no authentication required |
The envelope
Every response — success or failure — is the same JSON envelope the rest of
the Bedrock platform uses: a data member and an error member, exactly one
of which is null.
{ "data": { "…": "the resource, an object or an array" }, "error": null }{ "data": null, "error": { "message": "this key lacks the poams:write scope", "code": "FORBIDDEN_SCOPE" } }Successful calls return 200 OK, or 201 Created for creates. Machine
clients should branch on error.code, and read results from .data (in
jq, .data[] for lists). Timestamps are RFC 3339 in UTC.
Examples on this page show the data member
To keep the examples readable, the response bodies below show only what is
inside data. Every real response is wrapped as above.
export BEDROCK_CMMC_TOKEN='bcmmc_3f9a1c2e8b7d4650_…'
curl -s https://api.bedrock-cmmc.com/api/v1/me \
-H "Authorization: Bearer $BEDROCK_CMMC_TOKEN" | jq .dataIdentifiers
{id}in a path is always the package id — theidfield fromGET /packages.{controlId}accepts the human-readable requirement code (03.01.03), which is what agents naturally have, or the control'sidfromGET /packages/{id}/controls. Codes resolve against the package's own catalog revision, so03.01.03means the right row for a Rev 2 and a Rev 3 package alike.{objectiveId}isrequirement.objectives[].idfrom the control detail response.{evidenceId}and{poamId}are theidfields from their list endpoints.
Package reach
A key sees exactly the packages its organization owns. A package id that
belongs to another organization — or does not exist — returns
404 NOT_FOUND for reads and writes alike. The API never answers 403 for a
package you cannot reach, so an id cannot be probed.
Errors
Every error carries a human-readable message and a stable machine-readable
code inside error, with data set to null:
{ "data": null, "error": { "message": "key was revoked on 2026-08-30T14:02:11Z", "code": "KEY_REVOKED" } }| HTTP | code | When |
|---|---|---|
| 400 | BAD_REQUEST | The body is not a JSON object, has a field of the wrong type, names an unknown field, fails validation (bad enum value, missing required field), or exceeds 256 KiB. The message says which. |
| 401 | UNAUTHORIZED | Missing or malformed Authorization header, unknown key id, or wrong secret. |
| 401 | KEY_REVOKED | The key was revoked. Mint a new one. |
| 401 | KEY_EXPIRED | The key's expiry has passed. Rotate or mint a new one. |
| 401 | AGENT_ISSO_DISABLED | An Owner has switched Agent ISSO off for the organization. Keys resume working when it is switched on again. |
| 403 | FORBIDDEN_SCOPE | The key lacks the scope this endpoint requires. The message names the scope. |
| 403 | ORG_CANCELLED | The organization's subscription was cancelled; the API is closed until it is restored. |
| 404 | NOT_FOUND | The package, control, objective, evidence, or POA&M is not reachable by this key. |
| 405 | METHOD_NOT_ALLOWED | Wrong verb for this path. The Allow header lists the verbs the path supports. |
| 409 | CONFLICT | The write collides with state — the package is in assessment mode and objective edits are locked, the objective or control has no status record yet to attach notes or evidence to, or the record already exists. |
| 409 | UPLOAD_NOT_FOUND | POST /evidence/{evidenceId}/confirm was called before the file bytes reached storage. |
| 409 | STORAGE_NOT_PROVISIONED | The organization has no file storage yet; contact support. |
| 409 | AGENT_KEY_LIMIT | Returned by the key-management UI when an organization already has 25 active keys. Not returned by /api/v1 itself; listed here because agents may surface it from logs. |
| 413 | STORAGE_QUOTA_EXCEEDED | POST /evidence would exceed the organization's storage. |
| 422 | POAM_NOT_ALLOWED | POST /poams named a requirement whose SPRS value forbids a POA&M (all 5-point and 3-point requirements, and a few 1-point ones). |
| 423 | ORG_SUSPENDED | The organization's subscription is suspended; reads work, writes are refused until billing is resolved. |
| 429 | RATE_LIMITED | More than 300 requests in a minute on this key. Back off and retry. |
| 500 | INTERNAL_ERROR | Something failed on our side. Retry once, then contact support with the request time. |
| 501 | NOT_IMPLEMENTED | The route exists in the document but is not live on this deployment yet. |
Validation failures name the field. An unknown body key, for example, is rejected rather than ignored — so a typo cannot silently drop a change:
{
"data": null,
"error": {
"message": "unknown field(s): notes (allowed: status, assessmentNotes, evidenceDescription, implementationStatement, responsibilityDescription, policyReference, procedureReference, inheritedStatus)",
"code": "BAD_REQUEST"
}
}Method handling
Every documented path answers 405 with an Allow header for any verb it
does not implement, including PUT on a PATCH route and DELETE on
everything:
curl -si -X DELETE https://api.bedrock-cmmc.com/api/v1/packages/pkg_01J9/poams/poam_01J9 \
-H "Authorization: Bearer $BEDROCK_CMMC_TOKEN"
# HTTP/2 405
# allow: GET, PATCH
#
# {"data":null,"error":{"message":"DELETE is not supported on this route; allowed: GET, PATCH","code":"METHOD_NOT_ALLOWED"}}The 405 is answered before authentication, so a client debugging a wrong
verb sees the real reason rather than a 401.
Endpoints
| Method | Path | Scope | Purpose |
|---|---|---|---|
| GET | /packages | packages:read | List packages |
| GET | /packages/{id} | packages:read | Package profile |
| GET | /packages/{id}/controls | controls:read | Controls with status and points; filter by family, status |
| GET | /packages/{id}/controls/{controlId} | controls:read | Control detail with objectives, evidence, ESP mappings |
| PATCH | /packages/{id}/objectives/{objectiveId} | controls:write | Set objective status and narrative fields |
| PATCH | /packages/{id}/controls/{controlId}/notes | controls:write | Edit control notes |
| GET | /packages/{id}/evidence | evidence:read | List evidence |
| POST | /packages/{id}/evidence | evidence:write | Create evidence record and get an upload URL |
| POST | /packages/{id}/evidence/{evidenceId}/confirm | evidence:write | Confirm the upload landed |
| POST | /packages/{id}/evidence/{evidenceId}/links | evidence:write | Link evidence to an objective or control |
| GET | /packages/{id}/poams | poams:read | List POA&Ms |
| POST | /packages/{id}/poams | poams:write | Create a POA&M |
| PATCH | /packages/{id}/poams/{poamId} | poams:write | Update a POA&M |
| GET | /packages/{id}/assets | assets:read | List assets |
| GET | /packages/{id}/conmon | conmon:read | ConMon freshness per control |
| GET | /packages/{id}/ssp | ssp:read | System Security Plan |
| GET | /packages/{id}/sprs | sprs:read | SPRS score and posture |
| GET | /me | any | Describe the calling key and its organization |
Plus GET /openapi.json, which needs no key.
Source of truth
The OpenAPI document served at GET /api/v1/openapi.json ships with the API
build and is authoritative for paths, verbs, and codes. This page tracks it;
if the two ever disagree, trust the document and
tell us.
Packages
GET /packages
Scope packages:read. Returns every package the key's organization owns,
newest first, as a thin list.
curl -s https://api.bedrock-cmmc.com/api/v1/packages \
-H "Authorization: Bearer $BEDROCK_CMMC_TOKEN"[
{
"id": "pkg_01J9X3M4V6Q8",
"name": "Acme Defense — CUI Enclave",
"cmmcLevel": "LEVEL_2",
"catalogRevision": "rev2",
"isSelfPackage": false,
"updatedAt": "2026-09-01T18:22:04Z"
}
]isSelfPackage marks an MSP's or partner firm's own compliance package as
opposed to a client's.
GET /packages/{id}
Scope packages:read. The full package profile — system name, boundary
description, owner, security officer, storage use, and counts.
{
"id": "pkg_01J9X3M4V6Q8",
"name": "Acme Defense — CUI Enclave",
"description": "Engineering enclave handling CUI for contract W91…",
"systemName": "ENG-CUI",
"systemBoundary": "Azure GCC High tenant plus 42 managed endpoints",
"systemOwner": "R. Patel",
"securityOfficer": "J. Doe",
"cmmcLevel": "LEVEL_2",
"catalogRevision": "rev2",
"isSelfPackage": false,
"maxStorageBytes": 53687091200,
"currentStorageBytes": 4211998720,
"createdAt": "2026-03-02T15:04:00Z",
"updatedAt": "2026-09-01T18:22:04Z",
"_count": { "requirementStatuses": 110, "evidence": 212, "poams": 6, "assets": 58 }
}Controls
GET /packages/{id}/controls
Scope controls:read. One row per requirement — 110 for Level 2, 17 for
Level 1 — with the derived control status, SPRS points, objective progress,
and evidence count. This is the same data as the Controls table in the
app.
| Query | Values |
|---|---|
family | A family code: AC AT AU CA CM IA IR MA MP PE PS RA SC SI |
status | COMPLIANT, NON_COMPLIANT, IN_PROGRESS, NOT_STARTED, NOT_APPLICABLE |
curl -s "https://api.bedrock-cmmc.com/api/v1/packages/pkg_01J9X3M4V6Q8/controls?family=AC&status=NON_COMPLIANT" \
-H "Authorization: Bearer $BEDROCK_CMMC_TOKEN"[
{
"id": "req_01HZ7A2K9C",
"requirementId": "03.01.03",
"familyCode": "AC",
"familyName": "Access Control",
"title": "Information Flow Enforcement",
"basicRequirement": "Control the flow of CUI in accordance with approved authorizations.",
"cmmcLevel": "LEVEL_2",
"sortOrder": 3,
"points": 1,
"requirementStatusId": "rs_01J9X4…",
"status": "NON_COMPLIANT",
"statusUpdatedAt": "2026-08-14T09:11:32Z",
"objectivesTotal": 4,
"objectivesMet": 2,
"evidenceCount": 1
}
]Status is derived from the objectives: all MET (or a mix of MET and
NOT_APPLICABLE) is COMPLIANT; any NOT_MET is NON_COMPLIANT; all
NOT_APPLICABLE is NOT_APPLICABLE; anything else in progress is
IN_PROGRESS; no objective assessed yet is NOT_STARTED.
GET /packages/{id}/controls/{controlId}
Scope controls:read. The control with its requirement text, every
assessment objective and that objective's current status record — including
narrative fields, linked evidence, and any inherited ESP mappings — plus the
evidence linked at the control level.
curl -s https://api.bedrock-cmmc.com/api/v1/packages/pkg_01J9X3M4V6Q8/controls/03.01.03 \
-H "Authorization: Bearer $BEDROCK_CMMC_TOKEN"{
"atoPackage": { "id": "pkg_01J9X3M4V6Q8", "name": "Acme Defense — CUI Enclave", "cmmcLevel": "LEVEL_2" },
"control": {
"id": "req_01HZ7A2K9C",
"requirementStatusId": "rs_01J9X4…",
"status": "NON_COMPLIANT",
"requirement": {
"requirementId": "03.01.03",
"title": "Information Flow Enforcement",
"revision": "rev2",
"family": { "code": "AC", "name": "Access Control" },
"objectives": [
{
"id": "obj_01HZ7A3D1M",
"objectiveId": "03.01.03.a",
"description": "Information flow control policies are defined.",
"sortOrder": 1,
"statuses": [
{
"id": "os_01J9X5…",
"status": "MET",
"assessmentNotes": "Policy AC-4 v3 approved 2026-05-10.",
"evidenceDescription": null,
"inheritedStatus": null,
"policyReference": "AC-4 Information Flow Policy v3",
"procedureReference": null,
"implementationStatement": "Flow policies defined in the enclave SSP §4.2.",
"responsibilityDescription": null,
"version": 2,
"officialAssessment": false,
"evidenceMappings": [
{ "evidenceId": "ev_01J9X6…", "fileName": "AC-4-policy-v3.pdf", "mimeType": "application/pdf", "fileSize": 184223, "uploadedAt": "2026-05-11T16:40:00Z" }
],
"espMappings": []
}
]
}
]
},
"evidence": []
},
"activeAssessment": null,
"navigation": { "prevId": "req_01HZ7A2K9B", "nextId": "req_01HZ7A2K9D" }
}PATCH /packages/{id}/objectives/{objectiveId}
Scope controls:write. Sets the objective's status and, optionally, its
narrative fields. Send only the fields you are changing; the body must be a
non-empty JSON object, and status is required the first time an objective
is assessed in a package. Any field outside this list is a 400.
| Field | Type | Notes |
|---|---|---|
status | string | MET, NOT_MET, NOT_APPLICABLE, NOT_ASSESSED |
assessmentNotes | string | Examine / interview / test notes — what you looked at and concluded |
evidenceDescription | string | Prose description of the evidence when no file is attached |
implementationStatement | string | How the objective is implemented (feeds the SSP) |
responsibilityDescription | string | Who is responsible — customer, provider, shared |
policyReference | string | Policy document that governs this objective |
procedureReference | string | Procedure document that operationalizes it |
inheritedStatus | string | Inheritance from an external service provider: NONE, PARTIAL, or FULL (an empty string clears it) |
curl -s -X PATCH https://api.bedrock-cmmc.com/api/v1/packages/pkg_01J9X3M4V6Q8/objectives/obj_01HZ7A3D1M \
-H "Authorization: Bearer $BEDROCK_CMMC_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"status": "MET",
"assessmentNotes": "Examined AC-4 policy v3 and the Purview DLP rule export dated 2026-08-30; both define CUI flow paths. Interviewed enclave admin.",
"policyReference": "AC-4 Information Flow Policy v3"
}'Returns 200 with the updated objective status record (the same shape as
one entry of statuses above). Setting an objective re-derives the parent
control's status immediately and writes the same objective change-log entry
a click in the portal would; the audit feed shows it with actor type
agent. If the package is in assessment mode and objective edits are
locked, the write fails with 409 CONFLICT, exactly as it would in the app.
This is a PATCH, not a PUT
The web app's own route is a PUT that replaces the whole record. The agent
API loads the current record first and replaces only the fields you sent,
so a script that only sets status cannot blank out the narrative your team
wrote — and existing service-provider mappings are preserved. PUT on this
path returns 405.
PATCH /packages/{id}/controls/{controlId}/notes
Scope controls:write. Edits the control-level notes shown at the top of
the control detail page. Send one or both; a field you omit is left alone.
| Field | Type |
|---|---|
implementationNotes | string |
assessmentNotes | string |
curl -s -X PATCH https://api.bedrock-cmmc.com/api/v1/packages/pkg_01J9X3M4V6Q8/controls/03.01.03/notes \
-H "Authorization: Bearer $BEDROCK_CMMC_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "implementationNotes": "Enforced by Purview DLP and the enclave egress firewall; see AC-4 policy." }'{ "controlId": "03.01.03", "requirementStatusId": "rs_01J9X4…", "updated": ["implementationNotes"] }A control that has never had an objective assessed in this package has no
status record to hold notes yet; the call returns 409 CONFLICT asking you
to set an objective status first.
Evidence
GET /packages/{id}/evidence
Scope evidence:read. Evidence records in the package (latest versions),
with the requirement statuses each is linked to. Paged: limit (default
100, max 500) and offset.
curl -s "https://api.bedrock-cmmc.com/api/v1/packages/pkg_01J9X3M4V6Q8/evidence?limit=100&offset=0" \
-H "Authorization: Bearer $BEDROCK_CMMC_TOKEN"[
{
"id": "ev_01J9X6…",
"name": "AC-4 Information Flow Policy",
"fileName": "AC-4-policy-v3.pdf",
"mimeType": "application/pdf",
"fileSize": 184223,
"description": "Approved 2026-05-10",
"version": 3,
"isLatestVersion": true,
"atoPackageId": "pkg_01J9X3M4V6Q8",
"evidenceType": "POLICY",
"status": "CURRENT",
"reviewFrequency": "ANNUAL",
"expirationDate": "2027-05-10T00:00:00Z",
"uploadedAt": "2026-05-11T16:40:00Z",
"createdAt": "2026-05-11T16:40:00Z",
"requirementStatuses": ["rs_01J9X4…"]
}
]POST /packages/{id}/evidence
Scope evidence:write. Creates the evidence record and returns a presigned
URL for the file bytes. Uploading is a three-step dance: create the record,
PUT the file straight to storage, then confirm so the record leaves its
pending state. The API never proxies file contents.
| Field | Type | Notes |
|---|---|---|
fileName | string | Required |
mimeType | string | Required, e.g. application/pdf |
fileSize | integer | Required, bytes. Must fit your remaining storage and the 100 MB per-file ceiling |
name | string | Optional display name; defaults to the file name |
description | string | Optional |
evidenceType | string | POLICY, PLAN, PROCEDURE, DIAGRAM, SCREENSHOT, OTHER (default) |
curl -s -X POST https://api.bedrock-cmmc.com/api/v1/packages/pkg_01J9X3M4V6Q8/evidence \
-H "Authorization: Bearer $BEDROCK_CMMC_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"fileName": "dlp-rule-export-2026-08-30.pdf",
"mimeType": "application/pdf",
"fileSize": 92311,
"description": "Purview DLP rule export showing CUI flow rules",
"evidenceType": "SCREENSHOT"
}'{
"evidence": {
"id": "ev_01J9Y0…",
"fileName": "dlp-rule-export-2026-08-30.pdf",
"mimeType": "application/pdf",
"fileSize": 92311,
"s3Key": "org_01HX…/pkg_01J9X3M4V6Q8/ev_01J9Y0…/dlp-rule-export-2026-08-30.pdf",
"uploadStatus": "pending_upload",
"atoPackageId": "pkg_01J9X3M4V6Q8"
},
"uploadUrl": "https://…amazonaws.com/…?X-Amz-Signature=…",
"uploadMethod": "PUT",
"uploadHeaders": { "Content-Type": "application/pdf" },
"uploadExpiresAt": "2026-09-02T14:23:11Z",
"confirmPath": "/api/v1/packages/pkg_01J9X3M4V6Q8/evidence/ev_01J9Y0…/confirm"
}Then upload the bytes with exactly the method and headers returned
(Content-Type must match the mimeType you declared). The URL is valid for
15 minutes (uploadExpiresAt); a record that is never confirmed is reaped
after an hour. Allowed MIME types and the 100 MB ceiling are the portal's.
curl -s -X PUT "$UPLOAD_URL" \
-H "Content-Type: application/pdf" \
--data-binary @dlp-rule-export-2026-08-30.pdfPOST /packages/{id}/evidence/{evidenceId}/confirm
Scope evidence:write. Verifies the object landed in storage and flips the
record out of pending_upload — the same step the web app performs after
its own upload. Until you confirm, the record is invisible in the Evidence
page and cannot be linked. No body.
curl -s -X POST https://api.bedrock-cmmc.com/api/v1/packages/pkg_01J9X3M4V6Q8/evidence/ev_01J9Y0…/confirm \
-H "Authorization: Bearer $BEDROCK_CMMC_TOKEN"{ "id": "ev_01J9Y0…", "uploadStatus": "uploaded" }If the bytes have not arrived yet the call returns 409 UPLOAD_NOT_FOUND;
finish the PUT and try again.
POST /packages/{id}/evidence/{evidenceId}/links
Scope evidence:write. Links an evidence record to exactly one of an
objective or a control. Send objectiveId (the objective's id from the
control detail) or controlId (a requirement code such as 03.01.03, or
the control's id), never both.
curl -s -X POST https://api.bedrock-cmmc.com/api/v1/packages/pkg_01J9X3M4V6Q8/evidence/ev_01J9Y0…/links \
-H "Authorization: Bearer $BEDROCK_CMMC_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "objectiveId": "obj_01HZ7A3D1M" }'{ "evidenceId": "ev_01J9Y0…", "objectiveStatusId": "os_01J9X5…", "linked": true }Linking to a control returns requirementStatusId instead. Linking is
idempotent. An objective that has never been assessed in this package has no
status record to attach to; the call returns 409 CONFLICT asking you to
PATCH its status first.
POA&Ms
GET /packages/{id}/poams
Scope poams:read. POA&Ms with their milestones, linked requirements,
assignees, and comments. Optional filters: status and riskLevel.
curl -s "https://api.bedrock-cmmc.com/api/v1/packages/pkg_01J9X3M4V6Q8/poams?status=OPEN" \
-H "Authorization: Bearer $BEDROCK_CMMC_TOKEN"[
{
"id": "poam_01J9Z1…",
"type": "ASSESSMENT",
"atoPackageId": "pkg_01J9X3M4V6Q8",
"title": "Enforce session lock on shared engineering workstations",
"description": "03.01.10 objectives b and c not met — no lock after inactivity on 6 lab hosts.",
"riskLevel": "MODERATE",
"status": "OPEN",
"remediationPlan": "Push the CIS session-lock GPO to the LAB OU; verify with a screenshot per host.",
"scheduledCompletionDate": "2026-10-15T00:00:00Z",
"daysToRemediate": 43,
"deadline": "2027-03-02T00:00:00Z",
"createdBy": "agent:claude-code (jane)",
"createdAt": "2026-09-02T14:08:51Z",
"updatedAt": "2026-09-02T14:08:51Z",
"milestones": [
{ "id": "ms_01…", "description": "GPO linked to LAB OU", "dueDate": "2026-09-20T00:00:00Z", "completed": false, "sortOrder": 1 }
],
"requirements": [ { "requirementId": "03.01.10", "title": "Session Lock" } ],
"assignees": [],
"comments": []
}
]POST /packages/{id}/poams
Scope poams:write. Creates a POA&M with the same validation the app
applies, plus one the app enforces in its form: every requirement named in
requirementIds must be POA&M-eligible under the DoD assessment
methodology. Requirements worth 5 points, and most worth 3, cannot be
POA&M'd; naming one returns 422 POAM_NOT_ALLOWED and creates nothing.
Check poamAllowed on the SPRS endpoint first.
| Field | Type | Notes |
|---|---|---|
title | string | Required |
description | string | Required — the deficiency, in assessor terms |
type | string | ASSESSMENT, OPERATIONAL (default), or FINDING |
riskLevel | string | Required: CRITICAL, HIGH, MODERATE, LOW |
remediationPlan | string | Required |
scheduledCompletionDate | string | Required, YYYY-MM-DD or RFC 3339 |
requirementIds | array of strings | Requirement codes such as 03.01.10 (or control ids) |
findingId | string | The assessment finding this POA&M answers; required when type is FINDING |
curl -s -X POST https://api.bedrock-cmmc.com/api/v1/packages/pkg_01J9X3M4V6Q8/poams \
-H "Authorization: Bearer $BEDROCK_CMMC_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Enforce session lock on shared engineering workstations",
"description": "03.01.10 objectives b and c not met — no lock after inactivity on 6 lab hosts.",
"type": "ASSESSMENT",
"riskLevel": "MODERATE",
"remediationPlan": "Push the CIS session-lock GPO to the LAB OU; verify with a screenshot per host.",
"scheduledCompletionDate": "2026-10-15",
"requirementIds": ["03.01.10"]
}'Returns 201 with the POA&M. The 180-day POA&M closeout deadline is
computed for you. A blocked request looks like this:
{
"data": null,
"error": {
"message": "a POA&M is not permitted for requirement(s) 03.01.12 under the CMMC Level 2 assessment methodology; they must be MET",
"code": "POAM_NOT_ALLOWED"
}
}PATCH /packages/{id}/poams/{poamId}
Scope poams:write. Partial update; send only what changes. Milestones,
assignees, and comments are managed in the app and are not part of this
surface.
| Field | Type | Notes |
|---|---|---|
status | string | OPEN, IN_PROGRESS, CLOSED, OVERDUE |
title | string | |
description | string | |
riskLevel | string | CRITICAL, HIGH, MODERATE, LOW |
remediationPlan | string | |
scheduledCompletionDate | string | YYYY-MM-DD |
reviewNotes | string | Notes from the latest review of this POA&M |
curl -s -X PATCH https://api.bedrock-cmmc.com/api/v1/packages/pkg_01J9X3M4V6Q8/poams/poam_01J9Z1… \
-H "Authorization: Bearer $BEDROCK_CMMC_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "status": "IN_PROGRESS", "reviewNotes": "GPO linked to LAB OU on 2026-09-18; screenshots pending." }'{ "id": "poam_01J9Z1…", "updated": ["reviewNotes", "status"] }There is no delete. Close a POA&M by setting status to CLOSED.
Assets
GET /packages/{id}/assets
Scope assets:read. The package's asset inventory — hardware, software,
and network — with CUI/FCI handling flags. Paged: limit (default 100, max
1000) and offset.
curl -s "https://api.bedrock-cmmc.com/api/v1/packages/pkg_01J9X3M4V6Q8/assets?limit=100&offset=0" \
-H "Authorization: Bearer $BEDROCK_CMMC_TOKEN"[
{
"id": "asset_01J9…",
"name": "ENG-WS-014",
"assetType": "HARDWARE",
"assetCategory": "CUI_ASSET",
"hostname": "eng-ws-014.acme.local",
"ipAddress": "10.20.4.14",
"operatingSystem": "Windows 11 Enterprise 24H2",
"processesFCI": true,
"processesCUI": true,
"isManaged": true,
"patchLevel": "2026-08 CU",
"lastPatchDate": "2026-08-13T00:00:00Z",
"location": "Bldg 2 / Lab",
"owner": "Engineering",
"atoPackageId": "pkg_01J9X3M4V6Q8",
"createdAt": "2026-03-04T12:00:00Z",
"updatedAt": "2026-08-14T08:00:00Z"
}
]Continuous monitoring
GET /packages/{id}/conmon
Scope conmon:read. One row per requirement with its review frequency, last
and next review dates, and evidence freshness — the table the ConMon page
shows.
[
{
"requirementCode": "03.14.01",
"requirementStatusId": "rs_01J9X4…",
"requirementDbId": "req_01HZ7B…",
"familyCode": "SI",
"title": "Flaw Remediation",
"points": 5,
"frequency": "MONTHLY",
"lastReviewDate": "2026-07-28T00:00:00Z",
"nextReviewDate": "2026-08-28T00:00:00Z",
"daysUntilReview": -5,
"status": "OVERDUE",
"reviewStatus": "OVERDUE",
"evidenceCount": 4
}
]status is one of FRESH, DUE_SOON, OVERDUE, NO_EVIDENCE. Filter
client-side for OVERDUE and DUE_SOON to build a review to-do list.
System Security Plan
GET /packages/{id}/ssp
Scope ssp:read. The SSP's status and every text section — system
description, boundary, environment, personnel, approval metadata. Diagram
files are referenced by name and URL only; the inline diagram blob is
omitted.
{
"id": "ssp_01J9…",
"atoPackageId": "pkg_01J9X3M4V6Q8",
"version": "2.1",
"status": "DRAFT",
"creationMethod": "GENERATED",
"systemName": "ENG-CUI",
"systemAbbreviation": "ENG-CUI",
"systemCategory": "Moderate",
"systemPurpose": "Engineering data handling for DoD contracts",
"systemBoundary": "Azure GCC High tenant plus 42 managed endpoints",
"systemEnvironment": "Hybrid: cloud tenant + on-prem lab VLAN",
"networkDiagramFileName": "eng-cui-network-v4.png",
"networkDiagramUrl": "https://…",
"systemOwner": "R. Patel",
"securityOfficer": "J. Doe",
"authorizingOfficial": "M. Chen",
"preparedByName": "J. Doe",
"preparedByTitle": "ISSO"
}Approving or archiving an SSP is not available through the API.
SPRS
GET /packages/{id}/sprs
Scope sprs:read. The package's Supplier Performance Risk System score
under the DoD Assessment Methodology (110 minus the points of every
requirement not met), the per-family rollup, the requirements that block
certification because they cannot be POA&M'd, the POA&M-eligible gaps, and
the most recent affirmation your Owner recorded.
{
"level": "LEVEL_2",
"catalogRevision": "rev2",
"sprs": {
"score": 87,
"maxScore": 110,
"pointsDeducted": 23,
"metCount": 94,
"notMetCount": 14,
"notApplicableCount": 2,
"asOf": "2026-09-02T14:00:12Z"
},
"families": [
{ "familyCode": "AC", "familyName": "Access Control", "total": 22, "met": 18, "notMet": 4, "notApplicable": 0, "notAssessed": 0, "pointsAtRisk": 8 }
],
"blockers": [
{ "requirementId": "03.01.12", "title": "Remote Access Control", "points": 5, "status": "NON_COMPLIANT", "poamAllowed": false, "objectivesMet": 1, "objectivesTotal": 3 }
],
"poamEligible": [
{ "requirementId": "03.01.10", "title": "Session Lock", "points": 1, "status": "NON_COMPLIANT", "poamAllowed": true, "objectivesMet": 1, "objectivesTotal": 3, "hasOpenPoam": true }
],
"lastAffirmation": {
"id": "aff_01J8…",
"score": 81,
"affirmedAt": "2026-03-14T17:30:00Z",
"expiresAt": "2027-03-14T17:30:00Z",
"affirmedByName": "R. Patel"
}
}For a Level 1 package sprs is null and the response carries
practicesMet out of 17 instead. Revision 3 packages return a message that
SPRS is not yet defined for Rev 3. On a deployment where the posture service
has not shipped yet, the route answers 501 NOT_IMPLEMENTED rather than
guessing.
Me
GET /me
Any scope. Describes the calling key — useful as a connectivity check and so an agent can read its own grants before planning.
curl -s https://api.bedrock-cmmc.com/api/v1/me \
-H "Authorization: Bearer $BEDROCK_CMMC_TOKEN"Shown here with the full envelope:
{
"data": {
"org": { "id": "org_01HX…", "name": "Acme Defense LLC", "orgType": "OSC" },
"key": {
"id": "3f9a1c2e8b7d4650",
"name": "claude-code (jane)",
"scopes": ["packages:read", "controls:read", "controls:write", "evidence:read", "evidence:write", "poams:read", "poams:write"],
"expiresAt": "2026-12-01T14:02:11Z",
"lastUsedAt": "2026-09-02T13:58:40Z"
}
},
"error": null
}orgType is one of OSC, MSP, RP, C3PAO.
OpenAPI document
GET /openapi.json
No authentication. The OpenAPI 3.1 description of everything above, suitable for code generators, request validators, and OpenAPI-to-MCP bridges.
curl -s https://api.bedrock-cmmc.com/api/v1/openapi.json | jq '.paths | keys'