EQTY LabDocs

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.

posthttps://<host>/api/v1/agents/{agentId}/profile/check
bash
curl --request POST \
  --url 'https://<host>/api/v1/agents/{agentId}/profile/check' \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{"cid":"baga6yaq6eb6jml6fjkop55ltrmueohlwstzxdwxvqcyjtqa7j3j3nei5jituw","document":{}}'
javascript
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();
typescript
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();
python
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())
go
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))
}
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>/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());
    }
}
csharp
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
<?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;
ruby
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}"
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>/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

NameTypeDescription
agentId
required
string

Agent ID

Query parameters

NameTypeDescription
startstring

RFC3339 start time (default: 30 days before end)

endstring

RFC3339 end time (default: now)

state_cidstring

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

application/json
  • cidstring

    CID is the content ID of a profile to adopt.

  • documentobject

    Document is an AgentOperatingProfileV1.

Examplejson
{
  "cid": "baga6yaq6eb6jml6fjkop55ltrmueohlwstzxdwxvqcyjtqa7j3j3nei5jituw",
  "document": {}
}

Responses

200OK

application/json
  • agent_idstring
  • cidstring

    CID is the content ID the profile has, or would get once saved; null while the document is invalid.

  • correlation_idstring
  • coverageobject

    Coverage is null when the document could not be read at all.

    Show properties
    • configurationobject

      Configuration is the one permissions were read from, or null.

      Show properties
      • app_typestring
      • cidstring
      • observed_atstring
      • state_cidstring
    • configuration_unavailablestring

      ConfigurationUnavailable says why no configuration was read.

    • hoststring

      Host 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.Coverage
      Show github_com_eqtylab_gateway_control-plane_service_internal_toolinventory.Coverage
      • calls_not_visibleinteger
      • declaredgithub_com_eqtylab_gateway_control-plane_service_internal_toolinventory.DeclaredCoverage
        Show github_com_eqtylab_gateway_control-plane_service_internal_toolinventory.DeclaredCoverage
        • claude_code_configstring
        • definitionstring
      • limitsstring[]
      • llm_exchangesinteger
      • tools_not_visibleinteger
      • tools_readinteger
      • tools_unreadableinteger
      • unread_endpointsgithub_com_eqtylab_gateway_control-plane_service_internal_toolinventory.UnreadEndpoint[]
        Show properties

        Array of github_com_eqtylab_gateway_control-plane_service_internal_toolinventory.UnreadEndpoint

        • exchangesinteger
        • operationstring
        • providerstring
    • limitsstring[]
    • mcpstring

      MCP is configured when the configuration names servers; else via_tool_names.

    • permissionstring

      Permission is checked, not_captured (the app's integration derives none) or no_configuration.

    • toolstring

      Tool is checked when the window read at least one tool list, and not_visible otherwise.

  • errorsinternal_controlhttp.agentProfileCheckError[]
    Show properties

    Array of internal_controlhttp.agentProfileCheckError

    • codestring
    • messagestring
    • pathstring
  • 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.Finding

    • itemgithub_com_eqtylab_gateway_control-plane_service_internal_profilecheck.Item
      Show github_com_eqtylab_gateway_control-plane_service_internal_profilecheck.Item
      • callsinteger
      • descriptionstring
      • hoststring
      • kindstring
      • last_seen_atstring
      • match_keystring
      • namestring
      • namespacestring
      • permissionstring
      • run_bystring
      • serverstring
      • sourcesstring[]
      • 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
    • kindstring
    • rulegithub_com_eqtylab_gateway_control-plane_service_internal_profilecheck.RuleRef
      Show github_com_eqtylab_gateway_control-plane_service_internal_profilecheck.RuleRef
      • matchstring
      • pathstring
      • statementstring

        Statement is the exclusion's statement, for a top-level exclusion.

  • summarygithub_com_eqtylab_gateway_control-plane_service_internal_profilecheck.Summary
    Show github_com_eqtylab_gateway_control-plane_service_internal_profilecheck.Summary
    • availableinteger
    • excludedinteger
    • undeclaredinteger
    • unusedinteger
  • validboolean
  • versioninteger

    Version is the agent's version with that CID; null when it holds none.

  • windowgithub_com_eqtylab_gateway_control-plane_service_internal_toolinventory.Window
    Show github_com_eqtylab_gateway_control-plane_service_internal_toolinventory.Window
    • endstring
    • startstring
Examplejson
{
  "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

application/json
  • codestring
  • correlation_idstring
  • messagestring
Examplejson
{
  "code": "string",
  "correlation_id": "string",
  "message": "string"
}

401Unauthorized

application/json
  • codestring
  • correlation_idstring
  • messagestring
Examplejson
{
  "code": "string",
  "correlation_id": "string",
  "message": "string"
}

403Forbidden

application/json
  • codestring
  • correlation_idstring
  • messagestring
Examplejson
{
  "code": "string",
  "correlation_id": "string",
  "message": "string"
}

404agent_not_found, profile_not_found, or the state named by state_cid

application/json
  • codestring
  • correlation_idstring
  • messagestring
Examplejson
{
  "code": "string",
  "correlation_id": "string",
  "message": "string"
}

409The state named by state_cid can't be used

application/json
  • codestring
  • correlation_idstring
  • messagestring
Examplejson
{
  "code": "string",
  "correlation_id": "string",
  "message": "string"
}

413Request Entity Too Large

application/json
  • codestring
  • correlation_idstring
  • messagestring
Examplejson
{
  "code": "string",
  "correlation_id": "string",
  "message": "string"
}

422The state named by state_cid can't be read

application/json
  • codestring
  • correlation_idstring
  • messagestring
Examplejson
{
  "code": "string",
  "correlation_id": "string",
  "message": "string"
}

500Internal Server Error

application/json
  • codestring
  • correlation_idstring
  • messagestring
Examplejson
{
  "code": "string",
  "correlation_id": "string",
  "message": "string"
}

503Service Unavailable

application/json
  • codestring
  • correlation_idstring
  • messagestring
Examplejson
{
  "code": "string",
  "correlation_id": "string",
  "message": "string"
}