EQTY LabDocs

Review a control of an assessment submission

Record a program reviewer's judgement of one control of an assessment's newest submission: satisfied, not satisfied, or not applicable, registered with the Integrity Service as a governance statement in the assessment's graph, signed by the reviewer and citing the submission and the evidence it names for the control. The reviewer must currently review the assessment's program, be an active organization member, and have neither started the assessment nor made the submission; administering programs does not make a member a reviewer. Whether the evidence is enough is the reviewer's judgement. A submission made before controls carried evidence cannot be reviewed; its starter submits again. A control is found not applicable only where the program permits it, and not satisfied only with a statement. A control may be reviewed again; the newest review counts. A superseded submission is never reviewed, nor a certified one: revoking its certification is how to undo it. A review that leaves the submission passed certifies it at once, signed off by the reviewer, as a project's auditor issues its compliance credential: the response carries the certification, or, when issuing it failed, certificationError, and the review stands either way, so a program administrator or a reviewer whose review it rests on can ask for it again. An issued certification is attached to the gateway on the reviewer's token in the same request, which the agent registry lets the reviewer it names as authorizedBy do, so the right it grants takes effect at once; gateway says how that went, and a failure there undoes nothing. The reviewer acts in person, or through their API key bound to no project with full access or the review_agent_assessments scope. Issuing the certification signs with the platform key on the reviewer's token, which a key does only if its scopes cover every permission of a role that may sign; otherwise the review stands and certificationError says why

posthttps://localhost:10001/api/v1/organizations/{orgId}/assessments/{assessmentId}/submissions/{submission}/controls/{controlCode}/reviews
bash
curl --request POST \
  --url 'https://localhost:10001/api/v1/organizations/{orgId}/assessments/{assessmentId}/submissions/{submission}/controls/{controlCode}/reviews' \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{"outcome":"satisfied","statement":"string"}'
javascript
const response = await fetch("https://localhost:10001/api/v1/organizations/{orgId}/assessments/{assessmentId}/submissions/{submission}/controls/{controlCode}/reviews", {
  method: "POST",
  headers: {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "outcome": "satisfied",
    "statement": "string"
  })
});

const data = await response.json();
typescript
interface ReviewControlResponse {
  certification?: Record<string, unknown>;
  certificationError?: string;
  controlCode?: string;
  documentCid?: string;
  evidenceCid?: string;
  gateway?: Record<string, unknown>;
  outcome?: "satisfied" | "not_satisfied" | "not_applicable";
  reviewedAt?: string;
  reviewedBy?: string;
  reviewedByDid?: string;
  reviewedByUserId?: string;
  statement?: string;
  statementId?: string;
  uuid?: string;
}

const response = await fetch("https://localhost:10001/api/v1/organizations/{orgId}/assessments/{assessmentId}/submissions/{submission}/controls/{controlCode}/reviews", {
  method: "POST",
  headers: {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "outcome": "satisfied",
    "statement": "string"
  })
});
if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);

const data: ReviewControlResponse = await response.json();
python
import requests

response = requests.post(
    "https://localhost:10001/api/v1/organizations/{orgId}/assessments/{assessmentId}/submissions/{submission}/controls/{controlCode}/reviews",
    headers={
        "Authorization": "Bearer <token>",
        "Content-Type": "application/json",
    },
    json={
        "outcome": "satisfied",
        "statement": "string",
    },
)
response.raise_for_status()
print(response.json())
go
package main

import (
	"fmt"
	"io"
	"net/http"
	"strings"
)

func main() {
	body := strings.NewReader(`{
	  "outcome": "satisfied",
	  "statement": "string"
	}`)
	req, err := http.NewRequest(http.MethodPost, "https://localhost:10001/api/v1/organizations/{orgId}/assessments/{assessmentId}/submissions/{submission}/controls/{controlCode}/reviews", body)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer <token>")
	req.Header.Set("Content-Type", "application/json")

	res, err := http.DefaultClient.Do(req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	data, err := io.ReadAll(res.Body)
	if err != nil {
		panic(err)
	}
	fmt.Println(res.Status, string(data))
}
java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Example {
    public static void main(String[] args) throws Exception {
        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://localhost:10001/api/v1/organizations/{orgId}/assessments/{assessmentId}/submissions/{submission}/controls/{controlCode}/reviews"))
            .header("Authorization", "Bearer <token>")
            .header("Content-Type", "application/json")
            .method("POST", HttpRequest.BodyPublishers.ofString("""
                {
                  "outcome": "satisfied",
                  "statement": "string"
                }
                """))
            .build();

        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.statusCode() + " " + response.body());
    }
}
csharp
using System.Net.Http.Headers;
using System.Text;

using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "https://localhost:10001/api/v1/organizations/{orgId}/assessments/{assessmentId}/submissions/{submission}/controls/{controlCode}/reviews");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "<token>");
request.Content = new StringContent("""
    {
      "outcome": "satisfied",
      "statement": "string"
    }
    """, Encoding.UTF8, "application/json");

using var response = await client.SendAsync(request);
Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");
php
<?php

$ch = curl_init('https://localhost:10001/api/v1/organizations/{orgId}/assessments/{assessmentId}/submissions/{submission}/controls/{controlCode}/reviews');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer <token>',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => <<<'JSON'
    {
      "outcome": "satisfied",
      "statement": "string"
    }
    JSON,
    CURLOPT_RETURNTRANSFER => true,
]);

$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
echo $status . " " . $response . PHP_EOL;
ruby
require "net/http"

uri = URI("https://localhost:10001/api/v1/organizations/{orgId}/assessments/{assessmentId}/submissions/{submission}/controls/{controlCode}/reviews")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer <token>"
request["Content-Type"] = "application/json"
request.body = <<~'JSON'
  {
    "outcome": "satisfied",
    "statement": "string"
  }
JSON

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
  http.request(request)
end
puts "#{response.code} #{response.body}"
rust
// Cargo.toml dependencies: reqwest (features "json"), serde_json, tokio (features "full")
use serde_json::json;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let response = reqwest::Client::new()
        .post("https://localhost:10001/api/v1/organizations/{orgId}/assessments/{assessmentId}/submissions/{submission}/controls/{controlCode}/reviews")
        .header("Authorization", "Bearer <token>")
        .json(&json!({
            "outcome": "satisfied",
            "statement": "string"
        }))
        .send()
        .await?;

    println!("{} {}", response.status(), response.text().await?);
    Ok(())
}

Authorizations

  • BearerAuth · API key in header "Authorization" — Type "Bearer" followed by a space and JWT token.

Parameters

Path parameters

NameTypeDescription
orgId
required
string

Organization ID or UUID (urn:uuid optional)

assessmentId
required
string

Assessment UUID (urn:uuid optional)

submission
required
integer

Submission number, from 1

controlCode
required
string

The program control, e.g. AGT-1

Request bodyrequired

The review

application/json
  • outcomestring (enum)required

    One ofsatisfiednot_satisfiednot_applicable

  • statementstring

    Required to find the control not satisfied

Examplejson
{
  "outcome": "satisfied",
  "statement": "string"
}

Responses

201Review, and the certification it issued when it passed the submission, with what attaching it to the gateway did

application/json
  • certificationobject

    The certification the submission holds, when this review left it passed and it was issued

    Show properties
    • agentNamestringread-only

      The agent's display name in the agent registry when its assessment last read it; a label only, never part of the credential

    • agentUrnstringread-only

      The credential's subject: the agent's registry ID

    • assessmentUuidstringread-only

      urn:uuid form

    • canRevokebooleanread-only

      Whether the caller may revoke it: it is not revoked, and they administer programs or are the reviewer who authorized it and still review its program; false for an API key that does not act as its holder

    • configurationCidstringread-only

      The configuration it certifies, pinned by the submission

    • credentialobjectread-only

      The signed credential, as issued

    • credentialIdstringread-only

      The credential's urn:uuid, which identifies the certification

    • expiresAtstring<date-time>read-only

      The credential's validUntil; absent when the program sets no term

    • issuedAtstring<date-time>read-only
    • issuedBystringread-only

      The program administrator who requested it

    • issuedByDidstringread-only

      Verifiable actor, the credential's authorizedBy; issuedBy is the label

    • issuedByUserIdstringread-only
    • profileCidstringread-only

      The operating profile it certifies the agent against, pinned by the submission; absent when none was

    • programManifestCidstringread-only

      The program version it certifies against

    • programSlugstringread-only

      The program the version belongs to

    • programUuidstringread-only

      urn:uuid form

    • provenanceobjectread-only

      Who certified what: the statements the credential's evidence names, read back from the submission it certifies. Only for a caller who may read its assessment: absent for one who only sees the agent, and for a key scoped to view_agent_certifications

      Show properties
      • declarationscore.CertificationDeclaration[]

        The declarations its evidence names, in the evidence's order

        Show properties

        Array of core.CertificationDeclaration

        • controlsstring[]

          The control codes it covers, in the program's order

        • documentCidstring

          urn:cid of the declaration document

        • statementIdstring

          urn:cid of the governance statement it registered as

        • submittedAtstring<date-time>
        • submittedBystring
        • submittedByDidstring
        • submittedByUserIdstring
        • uuidstring

          urn:uuid form

      • reviewscore.ControlReview[]

        The review of each control it rests on, in the evidence's order, which is the program's

        Show properties

        Array of core.ControlReview

        • controlCodestring
        • documentCidstring

          urn:cid of the review document the Integrity Service stored

        • evidenceCidstring

          urn:cid of the control's evidence the review judged; absent on reviews made before controls carried evidence

        • outcomeobject

          One ofsatisfiednot_satisfiednot_applicable

          Show properties

          object (enum) · one of satisfied, not_satisfied, not_applicable

        • reviewedAtstring<date-time>
        • reviewedBystring
        • reviewedByDidstring
        • reviewedByUserIdstring
        • statementstring
        • statementIdstring

          urn:cid of the governance statement it registered as

        • uuidstringread-only

          urn:uuid form

      • submissionobject

        The submission it certifies

        Show properties
        • documentCidstring

          urn:cid of the submission document

        • numberinteger
        • statementIdstring

          urn:cid of the governance statement it registered as

        • submittedAtstring<date-time>
        • submittedBystring
        • submittedByDidstring
        • submittedByUserIdstring
        • uuidstring

          urn:uuid form

      • unmatchedstring[]

        Statements its evidence names that are none of these, by urn:cid; never expected, and never hidden

    • revokedbooleanread-only

      Set via the revoke endpoint

    • revokedAtstring<date-time>read-only

      Set via the revoke endpoint

    • revokedBystringread-only

      Set via the revoke endpoint

    • revokedByDidstringread-only

      Verifiable actor; revokedBy is the label

    • revokedByUserIdstringread-only

      Set via the revoke endpoint

    • revokedReasonstringread-only

      Set via the revoke endpoint

    • sourceCidstringread-only

      Where the Integrity Service stored the signed credential

    • statusobjectread-only

      Computed on read: revoked, else expired once expiresAt passes, else active

      One ofactiveexpiredrevoked

      Show properties

      object (enum) — Computed on read: revoked, else expired once expiresAt passes, else active · one of active, expired, revoked

    • submissionNumberintegerread-only

      The submission it certifies

  • certificationErrorstring

    Why it was not, when this review left the submission passed but issuing its certification failed; the review stands, and the certification can be asked for again

  • controlCodestring
  • documentCidstring

    urn:cid of the review document the Integrity Service stored

  • evidenceCidstring

    urn:cid of the control's evidence the review judged; absent on reviews made before controls carried evidence

  • gatewayobject

    What issuing the certification did to the gateway's copy, set with certification: the certification is attached as it is issued

    Show properties
    • configurationStatusstring (enum)

      How the agent's keys stand against the configuration it certifies, each by its newest request: current (every key seen lately runs it), partly_drifted (some do; the gateway refuses the rest), drifted (none do) or idle (no key seen lately). Set when attached or refreshed and valid: a certification the gateway won't honor has no keys standing against it

      One ofcurrentpartly_drifteddriftedidle

    • currentboolean

      Whether the gateway enforces the agent's program from it. Set when attached or refreshed

    • errorstring

      When failed: why, and what to do

    • errorCodestring

      When failed: the gateway's error code, or control_plane_unavailable when it could not be reached

    • operatingProfileStatusstring (enum)

      Whether the agent still holds the operating profile it certifies. Set when attached or refreshed

      One ofmatchmismatchunknown

    • outcomestring (enum)

      attached or refreshed: the gateway holds it, as validationStatus says; not_attached: it never held this revoked certification; failed: it could not be updated, as error says

      One ofattachedrefreshednot_attachedfailed

    • validationStatusstring (enum)

      The gateway's verification of it; it honors only a valid one. Set when attached or refreshed

      One ofvalidexpirednot_yet_validrevokedinvaliduntrusted_issuer

  • outcomeobject

    One ofsatisfiednot_satisfiednot_applicable

    Show properties

    object (enum) · one of satisfied, not_satisfied, not_applicable

  • reviewedAtstring<date-time>
  • reviewedBystring
  • reviewedByDidstring
  • reviewedByUserIdstring
  • statementstring
  • statementIdstring

    urn:cid of the governance statement it registered as

  • uuidstringread-only

    urn:uuid form

Examplejson
{
  "certification": {},
  "certificationError": "string",
  "controlCode": "string",
  "documentCid": "string",
  "evidenceCid": "string",
  "gateway": {
    "configurationStatus": "current",
    "current": true,
    "error": "string",
    "errorCode": "string",
    "operatingProfileStatus": "match",
    "outcome": "attached",
    "validationStatus": "valid"
  },
  "outcome": "satisfied",
  "reviewedAt": "2025-01-01T00:00:00Z",
  "reviewedBy": "string",
  "reviewedByDid": "string",
  "reviewedByUserId": "string",
  "statement": "string",
  "statementId": "string",
  "uuid": "string"
}

400Bad request - Invalid request, or the outcome breaks the program's rule

application/json
  • errorstring
Examplejson
{
  "error": "Invalid request body"
}

401Unauthorized - Authentication required

application/json
  • errorstring
Examplejson
{
  "error": "Invalid request body"
}

403Forbidden - Not a current reviewer, not an active member, the assessment's starter or the submission's maker, an API key with neither full access nor review_agent_assessments, or a project-bound key

application/json
  • errorstring
Examplejson
{
  "error": "Invalid request body"
}

404Assessment, submission or control not found, or not readable by the caller

application/json
  • errorstring
Examplejson
{
  "error": "Invalid request body"
}

409The submission was superseded, or certified

application/json
  • errorstring
Examplejson
{
  "error": "Invalid request body"
}

500Internal server error

application/json
  • errorstring
Examplejson
{
  "error": "Invalid request body"
}