curl --request POST \
--url https://api.humanos.tech/agents/keys/{did} \
--header 'Content-Type: application/json' \
--data '
{
"cnf": {
"jwk": {
"kty": "EC",
"crv": "P-256",
"x": "…",
"y": "…"
}
},
"evidence": {
"type": "device-self",
"proof": "eyJhbGciOiJFUzI1NiIsInR5cCI6InZpYS1yZWcrand0In0.…"
},
"regNonce": "k3Jd0Q7bXm2sVQ8hZ1pYt4Rc"
}
'import requests
url = "https://api.humanos.tech/agents/keys/{did}"
payload = {
"cnf": { "jwk": {
"kty": "EC",
"crv": "P-256",
"x": "…",
"y": "…"
} },
"evidence": {
"type": "device-self",
"proof": "eyJhbGciOiJFUzI1NiIsInR5cCI6InZpYS1yZWcrand0In0.…"
},
"regNonce": "k3Jd0Q7bXm2sVQ8hZ1pYt4Rc"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
cnf: {jwk: {kty: 'EC', crv: 'P-256', x: '…', y: '…'}},
evidence: {
type: 'device-self',
proof: 'eyJhbGciOiJFUzI1NiIsInR5cCI6InZpYS1yZWcrand0In0.…'
},
regNonce: 'k3Jd0Q7bXm2sVQ8hZ1pYt4Rc'
})
};
fetch('https://api.humanos.tech/agents/keys/{did}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.humanos.tech/agents/keys/{did}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'cnf' => [
'jwk' => [
'kty' => 'EC',
'crv' => 'P-256',
'x' => '…',
'y' => '…'
]
],
'evidence' => [
'type' => 'device-self',
'proof' => 'eyJhbGciOiJFUzI1NiIsInR5cCI6InZpYS1yZWcrand0In0.…'
],
'regNonce' => 'k3Jd0Q7bXm2sVQ8hZ1pYt4Rc'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.humanos.tech/agents/keys/{did}"
payload := strings.NewReader("{\n \"cnf\": {\n \"jwk\": {\n \"kty\": \"EC\",\n \"crv\": \"P-256\",\n \"x\": \"…\",\n \"y\": \"…\"\n }\n },\n \"evidence\": {\n \"type\": \"device-self\",\n \"proof\": \"eyJhbGciOiJFUzI1NiIsInR5cCI6InZpYS1yZWcrand0In0.…\"\n },\n \"regNonce\": \"k3Jd0Q7bXm2sVQ8hZ1pYt4Rc\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.humanos.tech/agents/keys/{did}")
.header("Content-Type", "application/json")
.body("{\n \"cnf\": {\n \"jwk\": {\n \"kty\": \"EC\",\n \"crv\": \"P-256\",\n \"x\": \"…\",\n \"y\": \"…\"\n }\n },\n \"evidence\": {\n \"type\": \"device-self\",\n \"proof\": \"eyJhbGciOiJFUzI1NiIsInR5cCI6InZpYS1yZWcrand0In0.…\"\n },\n \"regNonce\": \"k3Jd0Q7bXm2sVQ8hZ1pYt4Rc\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.humanos.tech/agents/keys/{did}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"cnf\": {\n \"jwk\": {\n \"kty\": \"EC\",\n \"crv\": \"P-256\",\n \"x\": \"…\",\n \"y\": \"…\"\n }\n },\n \"evidence\": {\n \"type\": \"device-self\",\n \"proof\": \"eyJhbGciOiJFUzI1NiIsInR5cCI6InZpYS1yZWcrand0In0.…\"\n },\n \"regNonce\": \"k3Jd0Q7bXm2sVQ8hZ1pYt4Rc\"\n}"
response = http.request(request)
puts response.read_body{
"keyId": "did:web:humanos.tech:agent:2f8b6c41-9d37-4e5a-8b02-6c1d9e7a4f35#key-2",
"granted": {
"assurance": "hardware",
"binding": "attested"
},
"degradedFrom": "hardware"
}{
"statusCode": 400,
"message": "pop_invalid: registration evidence refused (v0.3 §16.5)",
"error": "Bad Request"
}{
"statusCode": 404,
"message": "actor not found",
"error": "Not Found"
}Add Key
Add a key to a live agent, verified with fresh v0.3 §16.5 evidence
The key is held to exactly the standard registration was, so a rung never means “whatever was true at signup”
curl --request POST \
--url https://api.humanos.tech/agents/keys/{did} \
--header 'Content-Type: application/json' \
--data '
{
"cnf": {
"jwk": {
"kty": "EC",
"crv": "P-256",
"x": "…",
"y": "…"
}
},
"evidence": {
"type": "device-self",
"proof": "eyJhbGciOiJFUzI1NiIsInR5cCI6InZpYS1yZWcrand0In0.…"
},
"regNonce": "k3Jd0Q7bXm2sVQ8hZ1pYt4Rc"
}
'import requests
url = "https://api.humanos.tech/agents/keys/{did}"
payload = {
"cnf": { "jwk": {
"kty": "EC",
"crv": "P-256",
"x": "…",
"y": "…"
} },
"evidence": {
"type": "device-self",
"proof": "eyJhbGciOiJFUzI1NiIsInR5cCI6InZpYS1yZWcrand0In0.…"
},
"regNonce": "k3Jd0Q7bXm2sVQ8hZ1pYt4Rc"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
cnf: {jwk: {kty: 'EC', crv: 'P-256', x: '…', y: '…'}},
evidence: {
type: 'device-self',
proof: 'eyJhbGciOiJFUzI1NiIsInR5cCI6InZpYS1yZWcrand0In0.…'
},
regNonce: 'k3Jd0Q7bXm2sVQ8hZ1pYt4Rc'
})
};
fetch('https://api.humanos.tech/agents/keys/{did}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.humanos.tech/agents/keys/{did}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'cnf' => [
'jwk' => [
'kty' => 'EC',
'crv' => 'P-256',
'x' => '…',
'y' => '…'
]
],
'evidence' => [
'type' => 'device-self',
'proof' => 'eyJhbGciOiJFUzI1NiIsInR5cCI6InZpYS1yZWcrand0In0.…'
],
'regNonce' => 'k3Jd0Q7bXm2sVQ8hZ1pYt4Rc'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.humanos.tech/agents/keys/{did}"
payload := strings.NewReader("{\n \"cnf\": {\n \"jwk\": {\n \"kty\": \"EC\",\n \"crv\": \"P-256\",\n \"x\": \"…\",\n \"y\": \"…\"\n }\n },\n \"evidence\": {\n \"type\": \"device-self\",\n \"proof\": \"eyJhbGciOiJFUzI1NiIsInR5cCI6InZpYS1yZWcrand0In0.…\"\n },\n \"regNonce\": \"k3Jd0Q7bXm2sVQ8hZ1pYt4Rc\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.humanos.tech/agents/keys/{did}")
.header("Content-Type", "application/json")
.body("{\n \"cnf\": {\n \"jwk\": {\n \"kty\": \"EC\",\n \"crv\": \"P-256\",\n \"x\": \"…\",\n \"y\": \"…\"\n }\n },\n \"evidence\": {\n \"type\": \"device-self\",\n \"proof\": \"eyJhbGciOiJFUzI1NiIsInR5cCI6InZpYS1yZWcrand0In0.…\"\n },\n \"regNonce\": \"k3Jd0Q7bXm2sVQ8hZ1pYt4Rc\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.humanos.tech/agents/keys/{did}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"cnf\": {\n \"jwk\": {\n \"kty\": \"EC\",\n \"crv\": \"P-256\",\n \"x\": \"…\",\n \"y\": \"…\"\n }\n },\n \"evidence\": {\n \"type\": \"device-self\",\n \"proof\": \"eyJhbGciOiJFUzI1NiIsInR5cCI6InZpYS1yZWcrand0In0.…\"\n },\n \"regNonce\": \"k3Jd0Q7bXm2sVQ8hZ1pYt4Rc\"\n}"
response = http.request(request)
puts response.read_body{
"keyId": "did:web:humanos.tech:agent:2f8b6c41-9d37-4e5a-8b02-6c1d9e7a4f35#key-2",
"granted": {
"assurance": "hardware",
"binding": "attested"
},
"degradedFrom": "hardware"
}{
"statusCode": 400,
"message": "pop_invalid: registration evidence refused (v0.3 §16.5)",
"error": "Bad Request"
}{
"statusCode": 404,
"message": "actor not found",
"error": "Not Found"
}Headers
Pin request, response, and webhook shapes to a specific dated API version (YYYY-MM-DD). Omit to use the version pinned to your API key (set when the key is created; new keys default to the latest version). New integrations should target the latest version.
^\d{4}-\d{2}-\d{2}$"2026-10-16"
Path Parameters
The agent's DID, URL-encoded.
"did:web:humanos.tech:agent:2f8b6c41-9d37-4e5a-8b02-6c1d9e7a4f35"
Body
The agent's public key, as a P-256 JWK. Carried BESIDE the evidence, never inside it: evidence proves where a key lives and does not transport it — for an attestation the key is derived from the signed document and cross-checked against this one.
{
"jwk": {
"kty": "EC",
"crv": "P-256",
"x": "…",
"y": "…"
}
}
Confinement evidence (v0.3 §16.5). Every key you hold proves it is held: proof is a via-reg+jwt signed by the key over { cnf: { jkt }, aud, nonce, iat } — aud and nonce from POST /agents/register-challenge. device-self (+ proof) grants pop/self; custody (+ proof) pop/custodial; attestation-eat and webauthn chained to a pinned root grant device or hardware and need no proof. Evidence that cannot be checked degrades — an unanchored quote to pop/self, then with proof. Evidence that is wrong is refused with its code; none is refused.
{
"type": "device-self",
"proof": "eyJhbGciOiJFUzI1NiIsInR5cCI6InZpYS1yZWcrand0In0.…"
}
The single-use nonce from POST /agents/register-challenge.
"k3Jd0Q7bXm2sVQ8hZ1pYt4Rc"
Response
Returns the new key and the rung it earned.
The key the change concerns, what a mandate pins (D7).
"did:web:humanos.tech:agent:2f8b6c41-9d37-4e5a-8b02-6c1d9e7a4f35#key-2"
The rung the evidence earned after verification.
Show child attributes
Show child attributes
Present only when the evidence could not be confirmed and a lower rung was granted: what was claimed.
device, hardware "hardware"