Skip to content
Foxx Cyberfoxxcyber/docs

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 URLhttps://api.bedrock-cmmc.com/api/v1
AuthenticationAuthorization: Bearer bcmmc_<id>_<secret> on every request
Content typeJSON in and out (Content-Type: application/json on requests with a body)
Rate limit300 requests per minute per key
DiscoveryGET /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 .data

Identifiers

  • {id} in a path is always the package id — the id field from GET /packages.
  • {controlId} accepts the human-readable requirement code (03.01.03), which is what agents naturally have, or the control's id from GET /packages/{id}/controls. Codes resolve against the package's own catalog revision, so 03.01.03 means the right row for a Rev 2 and a Rev 3 package alike.
  • {objectiveId} is requirement.objectives[].id from the control detail response.
  • {evidenceId} and {poamId} are the id fields 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" } }
HTTPcodeWhen
400BAD_REQUESTThe 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.
401UNAUTHORIZEDMissing or malformed Authorization header, unknown key id, or wrong secret.
401KEY_REVOKEDThe key was revoked. Mint a new one.
401KEY_EXPIREDThe key's expiry has passed. Rotate or mint a new one.
401AGENT_ISSO_DISABLEDAn Owner has switched Agent ISSO off for the organization. Keys resume working when it is switched on again.
403FORBIDDEN_SCOPEThe key lacks the scope this endpoint requires. The message names the scope.
403ORG_CANCELLEDThe organization's subscription was cancelled; the API is closed until it is restored.
404NOT_FOUNDThe package, control, objective, evidence, or POA&M is not reachable by this key.
405METHOD_NOT_ALLOWEDWrong verb for this path. The Allow header lists the verbs the path supports.
409CONFLICTThe 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.
409UPLOAD_NOT_FOUNDPOST /evidence/{evidenceId}/confirm was called before the file bytes reached storage.
409STORAGE_NOT_PROVISIONEDThe organization has no file storage yet; contact support.
409AGENT_KEY_LIMITReturned 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.
413STORAGE_QUOTA_EXCEEDEDPOST /evidence would exceed the organization's storage.
422POAM_NOT_ALLOWEDPOST /poams named a requirement whose SPRS value forbids a POA&M (all 5-point and 3-point requirements, and a few 1-point ones).
423ORG_SUSPENDEDThe organization's subscription is suspended; reads work, writes are refused until billing is resolved.
429RATE_LIMITEDMore than 300 requests in a minute on this key. Back off and retry.
500INTERNAL_ERRORSomething failed on our side. Retry once, then contact support with the request time.
501NOT_IMPLEMENTEDThe 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

MethodPathScopePurpose
GET/packagespackages:readList packages
GET/packages/{id}packages:readPackage profile
GET/packages/{id}/controlscontrols:readControls with status and points; filter by family, status
GET/packages/{id}/controls/{controlId}controls:readControl detail with objectives, evidence, ESP mappings
PATCH/packages/{id}/objectives/{objectiveId}controls:writeSet objective status and narrative fields
PATCH/packages/{id}/controls/{controlId}/notescontrols:writeEdit control notes
GET/packages/{id}/evidenceevidence:readList evidence
POST/packages/{id}/evidenceevidence:writeCreate evidence record and get an upload URL
POST/packages/{id}/evidence/{evidenceId}/confirmevidence:writeConfirm the upload landed
POST/packages/{id}/evidence/{evidenceId}/linksevidence:writeLink evidence to an objective or control
GET/packages/{id}/poamspoams:readList POA&Ms
POST/packages/{id}/poamspoams:writeCreate a POA&M
PATCH/packages/{id}/poams/{poamId}poams:writeUpdate a POA&M
GET/packages/{id}/assetsassets:readList assets
GET/packages/{id}/conmonconmon:readConMon freshness per control
GET/packages/{id}/sspssp:readSystem Security Plan
GET/packages/{id}/sprssprs:readSPRS score and posture
GET/meanyDescribe 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.

QueryValues
familyA family code: AC AT AU CA CM IA IR MA MP PE PS RA SC SI
statusCOMPLIANT, 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.

FieldTypeNotes
statusstringMET, NOT_MET, NOT_APPLICABLE, NOT_ASSESSED
assessmentNotesstringExamine / interview / test notes — what you looked at and concluded
evidenceDescriptionstringProse description of the evidence when no file is attached
implementationStatementstringHow the objective is implemented (feeds the SSP)
responsibilityDescriptionstringWho is responsible — customer, provider, shared
policyReferencestringPolicy document that governs this objective
procedureReferencestringProcedure document that operationalizes it
inheritedStatusstringInheritance 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.

FieldType
implementationNotesstring
assessmentNotesstring
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.

FieldTypeNotes
fileNamestringRequired
mimeTypestringRequired, e.g. application/pdf
fileSizeintegerRequired, bytes. Must fit your remaining storage and the 100 MB per-file ceiling
namestringOptional display name; defaults to the file name
descriptionstringOptional
evidenceTypestringPOLICY, 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.pdf

POST /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.

FieldTypeNotes
titlestringRequired
descriptionstringRequired — the deficiency, in assessor terms
typestringASSESSMENT, OPERATIONAL (default), or FINDING
riskLevelstringRequired: CRITICAL, HIGH, MODERATE, LOW
remediationPlanstringRequired
scheduledCompletionDatestringRequired, YYYY-MM-DD or RFC 3339
requirementIdsarray of stringsRequirement codes such as 03.01.10 (or control ids)
findingIdstringThe 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.

FieldTypeNotes
statusstringOPEN, IN_PROGRESS, CLOSED, OVERDUE
titlestring
descriptionstring
riskLevelstringCRITICAL, HIGH, MODERATE, LOW
remediationPlanstring
scheduledCompletionDatestringYYYY-MM-DD
reviewNotesstringNotes 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'

Last updated September 2, 2026