Save an agent's operating profile
Saves an AgentOperatingProfileV1 on the agent as an immutable version and makes it the agent's current profile. Send the document, or the CID of a profile an agent you can read already holds to adopt it. Saving a profile the agent already holds adds no version: it returns that version, now current, with 200. Registry admins and users bound to the agent as operator may save; a viewer binding reads the profile but can't save it (403). An API key must have full access and no project binding. Each change is recorded in the agent's status events (save_profile, set_current_profile).
https://<host>/api/v1/agents/{agentId}/profilecurl --request PUT \
--url 'https://<host>/api/v1/agents/{agentId}/profile' \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{"cid":"baga6yaq6eb6jml6fjkop55ltrmueohlwstzxdwxvqcyjtqa7j3j3nei5jituw","document":{}}'const response = await fetch("https://<host>/api/v1/agents/{agentId}/profile", {
method: "PUT",
headers: {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
},
body: JSON.stringify({
"cid": "baga6yaq6eb6jml6fjkop55ltrmueohlwstzxdwxvqcyjtqa7j3j3nei5jituw",
"document": {}
})
});
const data = await response.json();interface AgentProfileResponse {
agent_id?: string;
cid?: string;
correlation_id?: string;
current?: boolean;
current_set_by?: string;
current_since?: string;
document?: Record<string, unknown>;
saved_at?: string;
saved_by?: string;
version?: number;
}
const response = await fetch("https://<host>/api/v1/agents/{agentId}/profile", {
method: "PUT",
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: AgentProfileResponse = await response.json();import requests
response = requests.put(
"https://<host>/api/v1/agents/{agentId}/profile",
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.MethodPut, "https://<host>/api/v1/agents/{agentId}/profile", 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"))
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.method("PUT", 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.Put, "https://<host>/api/v1/agents/{agentId}/profile");
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');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'PUT',
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")
request = Net::HTTP::Put.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()
.put("https://<host>/api/v1/agents/{agentId}/profile")
.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 |
Request bodyrequired
The profile document, or the CID of a profile to adopt
cidstringCID is the content ID of a profile to adopt.
documentobjectDocument is an AgentOperatingProfileV1.
{
"cid": "baga6yaq6eb6jml6fjkop55ltrmueohlwstzxdwxvqcyjtqa7j3j3nei5jituw",
"document": {}
}Responses
200The agent already held the profile
agent_idstringcidstringcorrelation_idstringcurrentbooleancurrent_set_bystringcurrent_sincestringCurrentSince and CurrentSetBy say when and by whom the version was made current; present only on the current version.
documentobjectDocument is the canonical AgentOperatingProfileV1 that CID is computed over.
saved_atstringsaved_bystringversionintegerVersion numbers the agent's distinct profiles in the order it first saved them.
{
"agent_id": "string",
"cid": "baga6yaq6eb6jml6fjkop55ltrmueohlwstzxdwxvqcyjtqa7j3j3nei5jituw",
"correlation_id": "string",
"current": true,
"current_set_by": "string",
"current_since": "string",
"document": {},
"saved_at": "string",
"saved_by": "string",
"version": 3
}201A new version
agent_idstringcidstringcorrelation_idstringcurrentbooleancurrent_set_bystringcurrent_sincestringCurrentSince and CurrentSetBy say when and by whom the version was made current; present only on the current version.
documentobjectDocument is the canonical AgentOperatingProfileV1 that CID is computed over.
saved_atstringsaved_bystringversionintegerVersion numbers the agent's distinct profiles in the order it first saved them.
{
"agent_id": "string",
"cid": "baga6yaq6eb6jml6fjkop55ltrmueohlwstzxdwxvqcyjtqa7j3j3nei5jituw",
"correlation_id": "string",
"current": true,
"current_set_by": "string",
"current_since": "string",
"document": {},
"saved_at": "string",
"saved_by": "string",
"version": 3
}400The document was refused, or the body is malformed (invalid_request, without path)
codestringCode is profile_invalid, profile_contains_secret, profile_too_large or profile_type_unsupported.
correlation_idstringmessagestringpathstringPath is where in the document the error is, as
systems[0].match[1], orprofilefor the document as a whole.
{
"code": "profile_invalid",
"correlation_id": "string",
"message": "string",
"path": "systems[0].match[1]"
}401Unauthorized
codestringcorrelation_idstringmessagestring
{
"code": "string",
"correlation_id": "string",
"message": "string"
}403A viewer binding, or an API key narrowed by scopes or a project
codestringcorrelation_idstringmessagestring
{
"code": "string",
"correlation_id": "string",
"message": "string"
}404agent_not_found, or profile_not_found when no agent you can read holds the CID
codestringcorrelation_idstringmessagestring
{
"code": "string",
"correlation_id": "string",
"message": "string"
}409agent_deregistered
codestringcorrelation_idstringmessagestring
{
"code": "string",
"correlation_id": "string",
"message": "string"
}413Request Entity Too Large
codestringcorrelation_idstringmessagestring
{
"code": "string",
"correlation_id": "string",
"message": "string"
}500Internal Server Error
codestringcorrelation_idstringmessagestring
{
"code": "string",
"correlation_id": "string",
"message": "string"
}