Check an operating profile against the agent
Compares a profile with the agent's tool inventory and captured configuration, and reports what it doesn't declare (undeclared), what the agent was seen with that it excludes (excluded), what it declares that wasn't seen (unused), and the app's or model provider's own tools it doesn't declare that the agent was only ever offered, never called (available), with coverage saying what couldn't be checked. Available findings are no gap: a client deciding whether the profile covers the agent's activity reads summary.undeclared, and summary.available counts what it could still pick up. An MCP server's or namespace's tools are never available, only undeclared, called or not. Send a document, the CID of one of the agent's versions, or nothing for its current profile. An invalid document still gets findings for the rules it gets right, beside its first error. Configuration that can't be derived leaves permissions unchecked rather than failing the check, unless state_cid names it. Never writes.
https://<host>/api/v1/agents/{agentId}/profile/checkcurl --request POST \
--url 'https://<host>/api/v1/agents/{agentId}/profile/check' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{"cid":"baga6yaq6eb6jml6fjkop55ltrmueohlwstzxdwxvqcyjtqa7j3j3nei5jituw","document":{}}'const response = await fetch("https://<host>/api/v1/agents/{agentId}/profile/check", {
method: "POST",
headers: {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
},
body: JSON.stringify({
"cid": "baga6yaq6eb6jml6fjkop55ltrmueohlwstzxdwxvqcyjtqa7j3j3nei5jituw",
"document": {}
})
});
const data = await response.json();interface AgentProfileCheckResponse {
agent_id?: string;
cid?: string;
correlation_id?: string;
coverage?: Record<string, unknown>;
errors?: Record<string, unknown>[];
findings?: Record<string, unknown>[];
summary?: Record<string, unknown>;
valid?: boolean;
version?: number;
window?: Record<string, unknown>;
}
const response = await fetch("https://<host>/api/v1/agents/{agentId}/profile/check", {
method: "POST",
headers: {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
},
body: JSON.stringify({
"cid": "baga6yaq6eb6jml6fjkop55ltrmueohlwstzxdwxvqcyjtqa7j3j3nei5jituw",
"document": {}
})
});
if (!response.ok) throw new Error(`${response.status} ${response.statusText}`);
const data: AgentProfileCheckResponse = await response.json();import requests
response = requests.post(
"https://<host>/api/v1/agents/{agentId}/profile/check",
headers={
"Authorization": "Bearer <token>",
"Content-Type": "application/json",
},
json={
"cid": "baga6yaq6eb6jml6fjkop55ltrmueohlwstzxdwxvqcyjtqa7j3j3nei5jituw",
"document": {},
},
)
response.raise_for_status()
print(response.json())package main
import (
"fmt"
"io"
"net/http"
"strings"
)
func main() {
body := strings.NewReader(`{
"cid": "baga6yaq6eb6jml6fjkop55ltrmueohlwstzxdwxvqcyjtqa7j3j3nei5jituw",
"document": {}
}`)
req, err := http.NewRequest(http.MethodPost, "https://<host>/api/v1/agents/{agentId}/profile/check", 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))
}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>/api/v1/agents/{agentId}/profile/check"))
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString("""
{
"cid": "baga6yaq6eb6jml6fjkop55ltrmueohlwstzxdwxvqcyjtqa7j3j3nei5jituw",
"document": {}
}
"""))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode() + " " + response.body());
}
}using System.Net.Http.Headers;
using System.Text;
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, "https://<host>/api/v1/agents/{agentId}/profile/check");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "<token>");
request.Content = new StringContent("""
{
"cid": "baga6yaq6eb6jml6fjkop55ltrmueohlwstzxdwxvqcyjtqa7j3j3nei5jituw",
"document": {}
}
""", Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request);
Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");<?php
$ch = curl_init('https://<host>/api/v1/agents/{agentId}/profile/check');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer <token>',
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => <<<'JSON'
{
"cid": "baga6yaq6eb6jml6fjkop55ltrmueohlwstzxdwxvqcyjtqa7j3j3nei5jituw",
"document": {}
}
JSON,
CURLOPT_RETURNTRANSFER => true,
]);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
echo $status . " " . $response . PHP_EOL;require "net/http"
uri = URI("https://<host>/api/v1/agents/{agentId}/profile/check")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer <token>"
request["Content-Type"] = "application/json"
request.body = <<~'JSON'
{
"cid": "baga6yaq6eb6jml6fjkop55ltrmueohlwstzxdwxvqcyjtqa7j3j3nei5jituw",
"document": {}
}
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}"// 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>/api/v1/agents/{agentId}/profile/check")
.header("Authorization", "Bearer <token>")
.json(&json!({
"cid": "baga6yaq6eb6jml6fjkop55ltrmueohlwstzxdwxvqcyjtqa7j3j3nei5jituw",
"document": {}
}))
.send()
.await?;
println!("{} {}", response.status(), response.text().await?);
Ok(())
}Authorizations
BearerAuth· API key in header "Authorization" — Type "Bearer" followed by a space and a sign-in token: a Guardian Platform JWT, or an OIDC ID token in a standalone deployment.ApiKeyAuth· API key in header "Authorization" — Type "Bearer" followed by a space and a Guardian Platform API key. Project-scoped routes and registration tokens check the key's scopes and project binding. Per-agent routes accept a key bound to no project that has empty scopes or full access, which acts as its holder, or a scope for what the route does: submit_agent_assessments reads an agent (its detail, configurations, state history and the files a state references, definitions, tools and operating profile, as its assessments rest on), view_agent_certifications reads its certifications, and manage_programs or review_agent_assessments attach a certification. A state's raw snapshot and findings, its audit exchanges and plugin events, and saving its operating profile take full access. A scope counts only while the key's holder still holds that permission in the key's organization, which auth-service reports as keyHeldScopes. A key never acts as an admin, so its holder's binding to the agent still decides.
Parameters
Path parameters
| Name | Type | Description |
|---|---|---|
agentIdrequired | string | Agent ID |
Query parameters
| Name | Type | Description |
|---|---|---|
start | string | RFC3339 start time (default: 30 days before end) |
end | string | RFC3339 end time (default: now) |
state_cid | string | State CID whose configuration to check permissions against (default: the agent's newest usable observation) |
Request body
The document, the CID of one of the agent's versions, or nothing for its current profile
cidstringCID is the content ID of a profile to adopt.
documentobjectDocument is an AgentOperatingProfileV1.
{
"cid": "baga6yaq6eb6jml6fjkop55ltrmueohlwstzxdwxvqcyjtqa7j3j3nei5jituw",
"document": {}
}Responses
200OK
agent_idstringcidstringCID is the content ID the profile has, or would get once saved; null while the document is invalid.
correlation_idstringcoverageobjectCoverage is null when the document could not be read at all.
Show properties
configurationobjectConfiguration is the one permissions were read from, or null.
Show properties
app_typestringcidstringobserved_atstringstate_cidstring
configuration_unavailablestringConfigurationUnavailable says why no configuration was read.
hoststringHost is gateway_traffic_only: hosts the agent reached directly, or over stdio, are never seen. no_traffic when the window holds no exchanges.
inventorygithub_com_eqtylab_gateway_control-plane_service_internal_toolinventory.CoverageShow github_com_eqtylab_gateway_control-plane_service_internal_toolinventory.Coverage
calls_not_visibleintegerdeclaredgithub_com_eqtylab_gateway_control-plane_service_internal_toolinventory.DeclaredCoverageShow github_com_eqtylab_gateway_control-plane_service_internal_toolinventory.DeclaredCoverage
claude_code_configstringdefinitionstring
limitsstring[]llm_exchangesintegertools_not_visibleintegertools_readintegertools_unreadableintegerunread_endpointsgithub_com_eqtylab_gateway_control-plane_service_internal_toolinventory.UnreadEndpoint[]Show properties
Array of
github_com_eqtylab_gateway_control-plane_service_internal_toolinventory.UnreadEndpointexchangesintegeroperationstringproviderstring
limitsstring[]mcpstringMCP is configured when the configuration names servers; else via_tool_names.
permissionstringPermission is checked, not_captured (the app's integration derives none) or no_configuration.
toolstringTool is checked when the window read at least one tool list, and not_visible otherwise.
errorsinternal_controlhttp.agentProfileCheckError[]Show properties
Array of
internal_controlhttp.agentProfileCheckErrorcodestringmessagestringpathstring
findingsgithub_com_eqtylab_gateway_control-plane_service_internal_profilecheck.Finding[]Findings are present whenever the document could be read, valid or not, so an owner sees them while the rest is unfinished.
Show properties
Array of
github_com_eqtylab_gateway_control-plane_service_internal_profilecheck.Findingitemgithub_com_eqtylab_gateway_control-plane_service_internal_profilecheck.ItemShow github_com_eqtylab_gateway_control-plane_service_internal_profilecheck.Item
callsintegerdescriptionstringhoststringkindstringlast_seen_atstringmatch_keystringnamestringnamespacestringpermissionstringrun_bystringserverstringsourcesstring[]suggested_accessagentprofile.Access[]SuggestedAccess is what the draft guesses the item needs, from every verb in a tool's name, or its description's first word when the name has none; empty when it has no guess. The check leaves it out.
traffic_kindstring
kindstringrulegithub_com_eqtylab_gateway_control-plane_service_internal_profilecheck.RuleRefShow github_com_eqtylab_gateway_control-plane_service_internal_profilecheck.RuleRef
matchstringpathstringstatementstringStatement is the exclusion's statement, for a top-level exclusion.
summarygithub_com_eqtylab_gateway_control-plane_service_internal_profilecheck.SummaryShow github_com_eqtylab_gateway_control-plane_service_internal_profilecheck.Summary
availableintegerexcludedintegerundeclaredintegerunusedinteger
validbooleanversionintegerVersion is the agent's version with that CID; null when it holds none.
windowgithub_com_eqtylab_gateway_control-plane_service_internal_toolinventory.WindowShow github_com_eqtylab_gateway_control-plane_service_internal_toolinventory.Window
endstringstartstring
{
"agent_id": "string",
"cid": "string",
"correlation_id": "string",
"coverage": {
"configuration": {
"app_type": "string",
"cid": "string",
"observed_at": "string",
"state_cid": "string"
},
"configuration_unavailable": "string",
"host": "string",
"inventory": {
"calls_not_visible": 0,
"declared": {
"claude_code_config": "string",
"definition": "string"
},
"limits": [
"string"
],
"llm_exchanges": 0,
"tools_not_visible": 0,
"tools_read": 0,
"tools_unreadable": 0,
"unread_endpoints": [
{
"exchanges": 0,
"operation": "string",
"provider": "string"
}
]
},
"limits": [
"string"
],
"mcp": "string",
"permission": "string",
"tool": "string"
},
"errors": [
{
"code": "profile_invalid",
"message": "string",
"path": "context.mode"
}
],
"findings": [
{
"item": {
"calls": 0,
"description": "string",
"host": "string",
"kind": "string",
"last_seen_at": "string",
"match_key": "string",
"name": "string",
"namespace": "string",
"permission": "string",
"run_by": "string",
"server": "string",
"sources": [
"string"
],
"suggested_access": [
"read"
],
"traffic_kind": "string"
},
"kind": "string",
"rule": {
"match": "string",
"path": "string",
"statement": "string"
}
}
],
"summary": {
"available": 0,
"excluded": 0,
"undeclared": 0,
"unused": 0
},
"valid": true,
"version": 0,
"window": {
"end": "string",
"start": "string"
}
}400Bad Request
codestringcorrelation_idstringmessagestring
{
"code": "string",
"correlation_id": "string",
"message": "string"
}401Unauthorized
codestringcorrelation_idstringmessagestring
{
"code": "string",
"correlation_id": "string",
"message": "string"
}403Forbidden
codestringcorrelation_idstringmessagestring
{
"code": "string",
"correlation_id": "string",
"message": "string"
}404agent_not_found, profile_not_found, or the state named by state_cid
codestringcorrelation_idstringmessagestring
{
"code": "string",
"correlation_id": "string",
"message": "string"
}409The state named by state_cid can't be used
codestringcorrelation_idstringmessagestring
{
"code": "string",
"correlation_id": "string",
"message": "string"
}413Request Entity Too Large
codestringcorrelation_idstringmessagestring
{
"code": "string",
"correlation_id": "string",
"message": "string"
}422The state named by state_cid can't be read
codestringcorrelation_idstringmessagestring
{
"code": "string",
"correlation_id": "string",
"message": "string"
}500Internal Server Error
codestringcorrelation_idstringmessagestring
{
"code": "string",
"correlation_id": "string",
"message": "string"
}503Service Unavailable
codestringcorrelation_idstringmessagestring
{
"code": "string",
"correlation_id": "string",
"message": "string"
}