EQTY LabDocs

Issue agent certification

Issues the AgentCertificationV1 an assessment submission earned, as a ComponentAttestation signed with the platform key whose subject is the agent's registry UUID. The certification belongs to the agent, not to any of its DIDs, which are added, rotated and revoked independently. Whoever asks is recorded as authorizedBy. That is a program administrator (organization-level manage_programs, from the UI or an integration's API key) or a reviewer who made one of the reviews the certification rests on, never the assessment's starter or the submission's author: auditors certify, as an audit owner issues a project compliance credential, and the review is the sign-off. Governance asks on the caller's behalf, as the reviewer whose verdict completes a passing review or when an administrator or reviewer asks again, and vouches for the request with its own token. Only governance knows who started the assessment and who reviews the program, by the user rather than a DID they may replace, so anyone asking Integrity directly is refused, a program administrator too. A reviewer is known only once the reviews named are read, so anyone else in the organization is refused after them. The platform signature goes through Auth Service on the caller's own token, so the caller's role must be one that may sign (audit owner, organization owner or project owner), and an API key needs scopes that let its owner's role sign; when Auth Service refuses, the request fails with 500. The request names the submission and the review it rests on for each control. Program reviewer assignments live in governance, which chooses each control's newest review by a current reviewer; Integrity checks what it can know: the submission is its assessment's newest, by when Integrity registered it; each review is a registered review of it, one per control, its control's newest by when Integrity registered it, finding the control satisfied, or not applicable where the program permits that, and not made by the submission's author. That is checked again under the lock the certification is recorded under, which a review of the submission is claimed under too, so a verdict registered while it was signed is never passed over; once it is recorded, the submission takes no more reviews. The certification's evidence is the statements of the submission, its declarations and those reviews, each signed by whoever made it, and its term runs from when Integrity registered the last review. The submission identifies the issuance. A repeated request, including one retried after a timeout, returns the credential the first one issued, unless the submission's author asks, and a revoked certification is never replaced from the same submission.

posthttps://<host>/gov/v1/certification/agent
bash
curl --request POST \
  --url 'https://<host>/gov/v1/certification/agent' \
  --header 'Authorization: Bearer <token>' \
  --header 'X-Governance-Authorization: <X-Governance-Authorization>' \
  --header 'Content-Type: application/json' \
  --data '{"reviews":["urn:cid:review_one","urn:cid:review_two"],"submission":"urn:cid:baga6yaq6ecyxdwey6hdjb2nf7kptcgz3sspjb765rzbcpvhm5srp7ofoczsto"}'
javascript
const response = await fetch("https://<host>/gov/v1/certification/agent", {
  method: "POST",
  headers: {
    "Authorization": "Bearer <token>",
    "X-Governance-Authorization": "<X-Governance-Authorization>",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "reviews": [
      "urn:cid:review_one",
      "urn:cid:review_two"
    ],
    "submission": "urn:cid:baga6yaq6ecyxdwey6hdjb2nf7kptcgz3sspjb765rzbcpvhm5srp7ofoczsto"
  })
});

const data = await response.json();
typescript
interface AgentCertificationIssueResponse {
  credential: Record<string, unknown>;
  credentialId: string;
  sourceCid: string;
}

const response = await fetch("https://<host>/gov/v1/certification/agent", {
  method: "POST",
  headers: {
    "Authorization": "Bearer <token>",
    "X-Governance-Authorization": "<X-Governance-Authorization>",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "reviews": [
      "urn:cid:review_one",
      "urn:cid:review_two"
    ],
    "submission": "urn:cid:baga6yaq6ecyxdwey6hdjb2nf7kptcgz3sspjb765rzbcpvhm5srp7ofoczsto"
  })
});
if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);

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

response = requests.post(
    "https://<host>/gov/v1/certification/agent",
    headers={
        "Authorization": "Bearer <token>",
        "X-Governance-Authorization": "<X-Governance-Authorization>",
        "Content-Type": "application/json",
    },
    json={
        "reviews": [
            "urn:cid:review_one",
            "urn:cid:review_two",
        ],
        "submission": "urn:cid:baga6yaq6ecyxdwey6hdjb2nf7kptcgz3sspjb765rzbcpvhm5srp7ofoczsto",
    },
)
response.raise_for_status()
print(response.json())
go
package main

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

func main() {
	body := strings.NewReader(`{
	  "reviews": [
	    "urn:cid:review_one",
	    "urn:cid:review_two"
	  ],
	  "submission": "urn:cid:baga6yaq6ecyxdwey6hdjb2nf7kptcgz3sspjb765rzbcpvhm5srp7ofoczsto"
	}`)
	req, err := http.NewRequest(http.MethodPost, "https://<host>/gov/v1/certification/agent", body)
	if err != nil {
		panic(err)
	}
	req.Header.Set("Authorization", "Bearer <token>")
	req.Header.Set("X-Governance-Authorization", "<X-Governance-Authorization>")
	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://<host>/gov/v1/certification/agent"))
            .header("Authorization", "Bearer <token>")
            .header("X-Governance-Authorization", "<X-Governance-Authorization>")
            .header("Content-Type", "application/json")
            .method("POST", HttpRequest.BodyPublishers.ofString("""
                {
                  "reviews": [
                    "urn:cid:review_one",
                    "urn:cid:review_two"
                  ],
                  "submission": "urn:cid:baga6yaq6ecyxdwey6hdjb2nf7kptcgz3sspjb765rzbcpvhm5srp7ofoczsto"
                }
                """))
            .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://<host>/gov/v1/certification/agent");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "<token>");
request.Headers.Add("X-Governance-Authorization", "<X-Governance-Authorization>");
request.Content = new StringContent("""
    {
      "reviews": [
        "urn:cid:review_one",
        "urn:cid:review_two"
      ],
      "submission": "urn:cid:baga6yaq6ecyxdwey6hdjb2nf7kptcgz3sspjb765rzbcpvhm5srp7ofoczsto"
    }
    """, 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://<host>/gov/v1/certification/agent');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer <token>',
        'X-Governance-Authorization: <X-Governance-Authorization>',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => <<<'JSON'
    {
      "reviews": [
        "urn:cid:review_one",
        "urn:cid:review_two"
      ],
      "submission": "urn:cid:baga6yaq6ecyxdwey6hdjb2nf7kptcgz3sspjb765rzbcpvhm5srp7ofoczsto"
    }
    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://<host>/gov/v1/certification/agent")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer <token>"
request["X-Governance-Authorization"] = "<X-Governance-Authorization>"
request["Content-Type"] = "application/json"
request.body = <<~'JSON'
  {
    "reviews": [
      "urn:cid:review_one",
      "urn:cid:review_two"
    ],
    "submission": "urn:cid:baga6yaq6ecyxdwey6hdjb2nf7kptcgz3sspjb765rzbcpvhm5srp7ofoczsto"
  }
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://<host>/gov/v1/certification/agent")
        .header("Authorization", "Bearer <token>")
        .header("X-Governance-Authorization", "<X-Governance-Authorization>")
        .json(&json!({
            "reviews": [
                "urn:cid:review_one",
                "urn:cid:review_two"
            ],
            "submission": "urn:cid:baga6yaq6ecyxdwey6hdjb2nf7kptcgz3sspjb765rzbcpvhm5srp7ofoczsto"
        }))
        .send()
        .await?;

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

Authorizations

  • BearerAuth · HTTP bearer (JWT)

Parameters

Header parameters

NameTypeDescription
X-Governance-Authorization
required
string

Governance's own bearer token, Bearer <token>, which it adds when it forwards a request, having checked the caller neither started the assessment nor made the submission, and, unless they administer programs, that they review the program

Request bodyrequired

application/json
  • reviewsstring[]required

    The urn:cid of the review the certification rests on for each control of the program, as /gov/v1/agent/review registered it: one per control. Governance chooses them, since it knows who reviews the program; Integrity checks each is a passing review of this submission

  • submissionstringrequired

    The urn:cid of the assessment submission to certify, as /gov/v1/agent/submission registered it. It identifies the issuance, so repeating a request returns the credential it issued

Examplejson
{
  "reviews": [
    "urn:cid:review_one",
    "urn:cid:review_two"
  ],
  "submission": "urn:cid:baga6yaq6ecyxdwey6hdjb2nf7kptcgz3sspjb765rzbcpvhm5srp7ofoczsto"
}

Responses

200The agent certification the submission earned, signed by the platform; repeating the request returns the same credential to anyone but the submission's author

application/json
  • credentialobjectrequired
  • credentialIdstringrequired
  • sourceCidstringrequired
Examplejson
{
  "credential": {},
  "credentialId": "urn:uuid:0f8e2d4c-7b6a-4c59-8e3d-2a1b0c9d8e7f",
  "sourceCid": "bafybeibwzifp5oc5mirxgcq"
}

400The submission is not a urn:cid

application/json
  • callstackobject
    Show properties

    object | null

  • errorstringrequired
Examplejson
{
  "callstack": {},
  "error": "string"
}

401Missing or invalid bearer or X-Governance-Authorization token

application/json
  • callstackobject
    Show properties

    object | null

  • errorstringrequired
Examplejson
{
  "callstack": {},
  "error": "string"
}

403The request carries no X-Governance-Authorization header whose token holds organization-level certify_agents; or the caller made the submission, or holds no organization-level manage_programs and made none of the reviews the certification rests on

application/json
  • callstackobject
    Show properties

    object | null

  • errorstringrequired
Examplejson
{
  "callstack": {},
  "error": "string"
}

404No assessment submission of the caller's organization is registered under the CID

application/json
  • callstackobject
    Show properties

    object | null

  • errorstringrequired
Examplejson
{
  "callstack": {},
  "error": "string"
}

409The certification this submission earned has been revoked, so certifying the agent again takes a new submission; or the one recorded rests on reviews since superseded, so it is not returned; or a review registered after one named is still being recorded

application/json
  • callstackobject
    Show properties

    object | null

  • errorstringrequired
Examplejson
{
  "callstack": {},
  "error": "string"
}

422The submission cannot certify: a later submission superseded it; the reviews named are not one passing review of it per program control, each its control's newest; a review finds a control not applicable where the program does not permit that, or one was made by the submission's author; a document behind it is missing or not what its CID names; or its term would already have run out

application/json
  • callstackobject
    Show properties

    object | null

  • errorstringrequired
Examplejson
{
  "callstack": {},
  "error": "string"
}

500Internal error, including Auth Service refusing to sign on the caller's token

application/json
  • callstackobject
    Show properties

    object | null

  • errorstringrequired
Examplejson
{
  "callstack": {},
  "error": "string"
}

503Auth Service or blob storage unavailable

application/json
  • callstackobject
    Show properties

    object | null

  • errorstringrequired
Examplejson
{
  "callstack": {},
  "error": "string"
}