Skip to content
↑↓ to move · Enter to openEsc to close
Guide

How to verify an AI agent, step by step

Updated

An AI agent that calls your business, sends your API a request or offers to transact on someone's behalf will usually say who it is. Saying is not verifying. A name, a voice, a logo and a confident description are all things any agent can produce, so a verification procedure has to rest on something the agent cannot fake: a signature made by a key the agent's operator holds, over a statement you can check byte for byte.

This guide is a procedure you can follow as a developer or a security reviewer. It uses the open AgenID protocol, version 1.1.1, and the reference registry at www.agenid.com. Every step can be run with a standard Ed25519 library and an RFC 8785 canonicalizer, and none of it requires an account, an API key or trust in AgenID's database.

The procedure has ten steps. The first eight establish identity: which agent this is, which operator key signed its declaration, and whether that signature is intact and current. Steps nine and ten are about interpretation: what the resulting verification level means, and why the question of what the agent is allowed to do is a separate decision you still have to make.

Honest limits are stated as they come up and collected near the end. The short version: today the reference registry can confirm that an operator signed a declaration and registered it, and nothing more.

What does it mean to verify an AI agent?

Verifying an AI agent means checking, for yourself, that a specific operator key signed a specific set of statements about a specific agent at a specific time. It does not mean confirming those statements are true. An operator can sign a manifest that describes its agent inaccurately, and the signature will still be valid. What the check removes is the ability to lie about who is operating the agent without being caught.

In AgenID an agent has a permanent identifier of the form agenid:<ULID>, described in the AgenID identifier entry. Behind that identifier sits an agent manifest written by the agent operator: the agent's name and description, the operator's name, domain and contact, the agent's purpose and channels, and three disclosure attestations. The manifest is never signed directly. A ManifestProof signs a small payload that binds the manifest by its SHA-256 manifest digest.

The steps below rebuild that chain from public material, following section 7.1 of the specification and failing fast on the cheapest checks.

  • Identity: which agent is this, and which operator key stands behind it.
  • Integrity: has the declaration been altered since it was signed.
  • Currency: was the key active when it signed, and is the proof inside its validity window now.
  • Assurance: which claims, if any, an independent authority has checked.
  • Permission: what the agent may do, which the protocol leaves to you.

Steps 1 to 3: get the identifier, resolve the envelope, check the schema

Step 1, get the agenid. An agent that participates in AgenID presents its identifier directly, or links to its Verification Card, or shows a badge that links there. Check the grammar before doing anything else: agenid: followed by exactly 26 Crockford base32 characters, matching ^agenid:[0-9A-HJKMNP-TV-Z]{26}$. The format is explained in the ULID entry. A malformed identifier is a stop, not a lookup. The only example identifier used on this site is the one from the specification, agenid:01J8Z3K3F2QZ9X6V7R4T8N2W5Y.

Step 2, resolve the envelope. The same URL serves two representations. A browser gets the HTML card; a machine asks for JSON and gets the canonical resolution envelope: curl -H "Accept: application/json" https://www.agenid.com/a/<agenid>. You can also paste an identifier into /verify. The envelope carries the manifest, the proof, the operator key document, a proof_check result, every assertion with its own check, the highest currently valid level, and key-discovery pointers naming both places the operator key can be fetched.

Read the status code. For JSON, 404 agent_not_found means there is no record, and an unregistered identifier is not evidence of anything beyond that. A 503 registry_unavailable means the status is unknown, not disproven. The HTML card returns 200 even for an unknown identifier, so never infer existence from it.

Step 3, check the schema. Validate the manifest, the proof and the key document against the normative JSON Schemas published with the specification. The schemas are strict: unknown members are rejected, and reserved manifest keys such as permissions and authorizations are rejected by a v1.1.1 validator. Erratum E1 adds a number-domain rule, rejecting non-finite numbers and integer tokens larger than 2^53 minus 1, because two implementations can disagree about what such a number serialized to. No v1.1.1 field is a number, so a conformant document never reaches that edge.

Step 4: how do you recompute the manifest digest?

Before any signature math, confirm that the manifest in front of you is the manifest the operator signed. Canonicalize the manifest with RFC 8785 JSON Canonicalization Scheme, hash the resulting UTF-8 bytes with SHA-256, and compare the lowercase hex result with proof.manifest_digest.value. The envelope's top-level manifest_digest should match as well.

Canonicalize from a parsed data structure, never by editing serialized text. RFC 8785 sorts object members by UTF-16 code units, emits non-ASCII characters as raw UTF-8, escapes only quotation marks, backslashes and control characters, and serializes numbers in one defined form, so member order in the file you received is irrelevant.

A mismatch fails with manifest_digest_mismatch, and you stop there. This is the check that catches tampering in transit, at rest or in a copied envelope: change one character of the manifest and the digest no longer matches.

Steps 5 and 6: how do you verify the Ed25519 ManifestProof?

Step 5, verify the signature. Take the proof object, remove the signature member and nothing else, canonicalize what remains with RFC 8785, and verify the signature over those exact bytes with the operator's public key. The $schema member stays in the signing input, so swapping the schema URI breaks the signature by design. The signature is base64url without padding and decodes to 64 bytes; the public key is the key document's public_key_b64u, 32 bytes.

Use pure Ed25519 as defined in RFC 8032. Do not pre-hash the payload with SHA-256 or SHA-512 and do not use the Ed25519ph variant. The only SHA-256 in the protocol is the manifest digest value carried inside the payload. A pre-hashed signature does not reproduce a spec signature, and some key services offer both modes on the same key, so this is a real way to get a silent wrong answer. A failed check is signature_invalid.

Step 6, check the key's role and controller. Signature validity is necessary, not sufficient. Confirm that the proof's key_id matches the key document's key_id, that the key document's role is operator, and that its controller equals the agent's identifier. An authority-role key must never be accepted for a ManifestProof, even when the math validates; that is key_role_mismatch. A key controlling a different agent is key_controller_mismatch. See operator key and authority key.

Step 7: how do you fetch the operator key from two sources?

The envelope carries a copy of the operator's key document, but a copy handed to you by the registry is exactly what a dishonest or compromised registry would substitute. Two-path key discovery closes that gap by fetching the key from two places that are controlled by different parties and requiring them to agree.

The registry path takes the key's wire form, the bare key ULID without the agenid:key: prefix: curl https://www.agenid.com/v1/keys/<key-ulid>. The envelope's operator_key.discovery.registry_path gives it to you. The operator path is a well-known URI on the operator's own domain, the operator_domain named in the manifest: curl https://<operator_domain>/.well-known/agenid/keys.json. That document lists every key the operator controls, and it must be served over HTTPS with a valid certificate for that domain.

Compare the registry copy, the operator copy and the copy in the envelope. They must describe the same key: same key_id, same public_key_b64u, same role and controller. Disagreement is a hard failure, not a warning; it is the exact case two-path discovery exists to catch.

Be precise about the one-source case. The operator publishes the .well-known copy, and AgenID cannot make it do so. If the operator's domain serves no key document, you have one source, not two, and the key-substitution defense is not in force for that agent. A verifier that treats a missing operator copy as agreement has disabled the check. Record it as a single-source result.

Step 8: how do you check key status and validity windows?

A key document carries status (active, retired or revoked) and the timestamps retired_at and revoked_at. Evaluate the key against the instant the proof claims to have been signed, its created_at, not against the current time. A proof created before a key was retired stays valid; a proof created after revoked_at fails regardless of its signature. The failure is key_not_active_at_signing_time. The key rotation and revocation entry covers the model.

Retired and revoked keys stay resolvable forever. Historical signatures must remain checkable. A 200 from the key endpoint is therefore not a statement that the key is usable today. Read the fields.

Then check the proof's own window: created_at must be at or before now, and now must be at or before expires_at. Proofs expire, so operators re-sign. Verification applies no clock leeway. Pass the current time in explicitly so the result is reproducible. Outcomes are not_yet_valid and expired.

One limit belongs here. The key model can express retirement and revocation, but the reference deployment has no write path that retires or revokes a key, and no agent revocation or suspension flow. Agent status values exist in the schema, and nothing writes them today. Every registered agent shows ACTIVE. Treat that field as unpopulated, not as an assurance.

Step 9: what does the verification level tell you?

If steps 1 to 8 pass, you have established a DECLARED claim bound to an operator key. The ManifestProof can never produce more than DECLARED. The verification level comes from a different object, a VerificationAssertion signed by an authority key, and each assertion is checked the same way: digest binding against the current manifest, signature, role authority, controller equal to the named authority, validity window, and trust in the authority itself through a pinned root authority key.

Do not take the envelope's verification.level on faith. Recompute it from the assertions that passed your own checks. An assertion bound to an older manifest version is reported as assertion_not_applicable_to_current_manifest; it is not invalid, but it does not speak for the manifest in front of you.

The reference deployment at agenid.com issues nothing above L1_REGISTERED today. L2_DOMAIN_VERIFIED and every level above it require a VerificationAssertion signed by a root authority key, and that key has not been created yet. L5 is reserved and is not issuable at all. On the reference registry today every assertion list is empty and the level you compute should be L1_REGISTERED for a registered agent.

  • DECLARED: an operator signed a manifest. Nobody checked anything.
  • L1_REGISTERED — that signed manifest is in this registry. Still a self-declaration, which is why it renders amber, never as verified.
  • L2_DOMAIN_VERIFIED — an authority checked control of a domain. Defined by the protocol, not issuable today.
  • L3_ORGANIZATION_VERIFIED and L4_DEPLOYMENT_VERIFIED — defined, not issuable today; L4's sampling method is still being specified.
  • No level means compliant, audited, certified or safe. Verification is not compliance.

Step 10: how do you decide what the agent is allowed to do?

Which agent is acting is not the same question as what it is allowed to do. AgenID answers the first. Protocol v1.1.1 defines no signed authorization object, so the permission decision stays with the relying party and the operator. A v1.2 authorization layer exists only as a draft in development.

Keep three claim states apart, as the declared, verified and authorized entry sets out. DECLARED is what the operator says. VERIFIED is what an authority checked, for one claim, in one scope, for one window. AUTHORIZED is what the agent may do, and v1.1.1 has no signed object for it. None of them is ever inferred from another, and a higher verification level does not grant any permission.

In practice, the relying party makes the authorization decision under its own policy. A dental office may accept an L1 agent for booking a cleaning and require a human for anything touching records; see the dentist appointment and financial transaction scenarios. What AgenID contributes is a stable answer to which agent and which operator you are dealing with, so that your policy and your logs attach to a persistent identity instead of a claim.

What does each check prove, and what are the honest limits?

The table below is the fastest way to read a verification result correctly. Each check establishes one narrow fact, and the right-hand column is as important as the middle one.

  • L1 ceiling: The reference deployment at agenid.com issues nothing above L1_REGISTERED today. L2_DOMAIN_VERIFIED and every level above it require a VerificationAssertion signed by a root authority key, and that key has not been created yet. L5 is reserved and is not issuable at all.
  • One-source case: if the operator's domain serves no .well-known/agenid/keys.json, two-path discovery gives you one source and the substitution defense does not apply.
  • No revocation flow: agent statuses are stored but never written, and there is no deployed path to retire or revoke a key.
  • Operator attestations such as discloses_to_user and human_escalation are self-declared and unverifiable by anyone, including AgenID.
  • Object, not request: a valid proof shows who signed a declaration, not that the live request in front of you came from that agent. See agent-to-agent authentication.
Check, what it proves, what it does not prove
CheckWhat it provesWhat it does not prove
Identifier grammarThe string is a well-formed agenidThat any agent is registered under it
Resolution (200 envelope)The registry holds a record for this identifierAnything about the agent's behavior, or that the registry is honest
Strict schema validationThe objects have exactly the defined shapeThat the declared content is true
Manifest digest recomputedThe manifest is byte-for-byte what the proof bindsWho wrote it or whether it is accurate
Ed25519 ManifestProofThe holder of this operator key signed this bindingThat the key holder is the organization named in the manifest
Role and controllerAn operator key for this agent signed it, not an authority key or another agent's keyThat the operator is legitimate
Two-path key agreementThe registry did not substitute the key, if the operator publishes a copyAnything, when only one source exists
Key status and windowThe key was active at signing and the proof is currentThat the key has not been stolen
Recomputed levelWhich authority-checked claims currently applyPermission to act, compliance, or safety

How do you verify without trusting AgenID?

Every step above can be run without the registry being honest, and after step 2 without it being online. The envelope contains everything a verifier needs; your own code recomputes the digest, verifies the signature, checks role, controller and windows, and recomputes the level. The registry cannot forge an operator's signature, so a registry that lied about a manifest or a proof would fail your checks. The key comparison in step 7 is what stops it from swapping the key itself.

That is why the registry holds no operator private keys. Keys are generated on the operator's machine or in the browser at /issue and are never sent to AgenID. The trust overview sets out what the system trusts and what it deliberately does not: the registry's stored level, the transport, the client's clock and the operator's honesty about behavior.

Store the envelope you verified alongside your decision. It is self-contained evidence that anyone, including an auditor who trusts neither you nor AgenID, can re-check later. If your recomputation ever disagrees with what the registry reported, the registry is wrong.

How does the independent conformance suite help?

If you write your own verifier, test it against the independent conformance suite at github.com/AgenID-protocol/conformance. It is MIT-licensed, written in Python, and deliberately does not import AgenID's own code: it uses general-purpose libraries for RFC 8785, Ed25519 and JSON Schema.

The suite reproduces the specification's deterministic test vectors: manifest canonicalization and digest, ManifestProof and VerificationAssertion signatures, role-substitution rejections, key-order independence, adversarial Unicode, number and sort-order cases, and the Erratum E1 rejections. Passing it shows your canonicalizer and Ed25519 call produce the exact bytes the protocol defines.

Nothing is published to npm, so there is no AgenID package to install for verification. That is fine: the procedure needs only an RFC 8785 implementation, SHA-256 and pure Ed25519, all of which exist in every mainstream language. The normative text lives in the public specification repository.

Frequently asked questions

What is the quickest way to verify an AI agent?

Paste its identifier into /verify or open its Verification Card at https://www.agenid.com/a/<agenid>. For a machine check, request the same URL with Accept: application/json, recompute the manifest digest, verify the Ed25519 ManifestProof and compare the operator key from both discovery paths.

Do I need an account or API key to verify an agent?

No. Resolution and key discovery are public, unauthenticated reads, and the verification itself runs in your own code. You need an RFC 8785 canonicalizer, SHA-256 and a pure Ed25519 implementation.

What if the operator does not publish a .well-known key file?

Then you have one source for the operator key instead of two, and two-path discovery cannot detect a substituted key for that agent. Treat it as a single-source result. Do not treat a missing copy as agreement.

Can the AgenID registry fake a verification result?

It can report anything, but it cannot forge an operator's Ed25519 signature, and every assertion travels with its own signature. A verifier who recomputes the digest, the signatures and the level detects a registry that lied. Two-path key discovery covers key substitution where the operator publishes its copy.

Which verification levels can an agent have today?

The reference deployment at agenid.com issues nothing above L1_REGISTERED today. L2_DOMAIN_VERIFIED and every level above it require a VerificationAssertion signed by a root authority key, and that key has not been created yet. L5 is reserved and is not issuable at all.

Does a valid verification mean the agent is allowed to act?

Which agent is acting is not the same question as what it is allowed to do. AgenID answers the first. Protocol v1.1.1 defines no signed authorization object, so the permission decision stays with the relying party and the operator. A v1.2 authorization layer exists only as a draft in development.

Can I check whether an agent has been revoked?

Not through an agent revocation flow, because none is deployed. Agent status values exist in the schema but nothing writes them. Key documents do carry status, retired_at and revoked_at, and you should evaluate them against the proof's signing time.

How do I test my own verifier implementation?

Run it against the vectors in the independent conformance suite at AgenID-protocol/conformance, which reproduces the specification's signatures, digests and adversarial canonicalization cases without depending on AgenID's code.

What agenid.com issues today

The reference deployment at agenid.com issues DECLARED and L1_REGISTERED only. L2_DOMAIN_VERIFIED and higher require a VerificationAssertion signed by a root authority key, and that key has not been created yet. L5 is reserved by the specification and is not issuable at all.

Identity is not permission

Which agent is acting and who declared it is one question. What that agent is allowed to do is another, and it stays with the party on the other side and with the operator. Protocol v1.1.1 defines no signed authorization object.

Sources

  1. AgenID protocol specification v1.1.1 (AgenID-protocol/spec)
  2. AgenID independent conformance suite (AgenID-protocol/conformance)
  3. RFC 8785: JSON Canonicalization Scheme (JCS)
  4. RFC 8032: Edwards-Curve Digital Signature Algorithm (EdDSA)
  5. RFC 8615: Well-Known Uniform Resource Identifiers

Give your agent an identity

Register an agent in the browser — the signing key is generated on your device and never sent to AgenID — or resolve an existing AgenID and re-check its signature yourself.