On the internet, an identity usually rests on an identifier and a secret, for example an email address and a password. This model was designed for people. Programs that reach services use even more limited mechanisms, generally a plain API key, including when they are autonomous agents acting on behalf of someone.
This article presents what makes up a digital identity, what changes when that identity belongs to a program, then how to give an autonomous agent an identity a service can verify.
What makes up a digital identity
A digital identity rests on three elements:
- an identifier, which designates someone uniquely for a service, such as an email address or a username;
- a proof, which lets the service check that whoever shows up controls that identifier, such as a password, a code received by email or a cryptographic key;
- attributes, which describe the person, such as their name, date of birth or role.
In a classic service, these three elements are stored together in the database. An account looks like this:
{
"email": "alice@example.com",
"password_hash": "$argon2id$v=19$m=65536,t=3,p=4$...",
"name": "Alice Martin",
"role": "accountant",
"email_verified": true
}
The email field acts as the identifier. The password_hash field holds the
fingerprint of the password, which serves as proof at sign-in. The name and role
fields are attributes, and email_verified records that the service sent a code to
that address and that the person copied it back.
These attributes carry very different weight depending on where they come from. The name was typed in by the person, and the service never checked it. The role was granted by the company. For a trustworthy attribute, the service has to rely on a third party that states it and signs it: an employer for a role, a university for a degree, the state for a civil identity. That is the principle of the European digital identity wallet, used in the emailless account architecture presented on this blog.
The identity of non-humans
Programs need an identity too. A website, a backup script or an integration between two pieces of software must prove who they are before reaching a service. These are known as non-human identities.
The most common example is the HTTPS certificate. When a browser connects to a site,
the server presents a certificate that binds its domain name to a public key. This
blog’s certificate can be printed with openssl:
openssl s_client -connect blog.kaizensphere.dev:443 \
-servername blog.kaizensphere.dev </dev/null 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates
At the time of writing, the command prints:
subject=CN = blog.kaizensphere.dev
issuer=C = US, O = Let's Encrypt, CN = YR2
notBefore=Aug 27 02:03:17 2026 GMT
notAfter=Nov 25 02:03:16 2026 GMT
The subject line holds the identifier, here the domain name. The issuer line
names the certification authority that signed the certificate, and the notBefore
and notAfter lines bound its validity. The elements of an identity are all there:
an identifier, a proof through the private key that only the server holds, and an
attestation signed by a third party the browser trusts.
To reach an API, many programs use a much simpler solution: an API key. A script that imports invoices into an accounting system gets a configuration of this kind:
{
"name": "invoice-import",
"api_key": "7f3a9c1e...",
"scopes": ["*"],
"expires_at": null,
"owner": null
}
Here the key serves as both identifier and proof. It grants access to everything
("scopes": ["*"]), it never expires ("expires_at": null) and it is tied to no one
responsible ("owner": null). Anyone, or any program, that gets hold of this key can
act in place of the script.
These limits come from three differences with the identity of a person:
- a program acts on behalf of someone, so the service needs to know who answers for its actions;
- a program may be created for one task then deleted a few minutes later, and its rights should last just as long;
- programs are numerous, and the World Economic Forum noted in October 2025 that AI agents multiply this kind of identity, often with broad and persistent access.
The case of autonomous agents
These three differences become sharper once the program chooses what it does itself. An autonomous agent receives a goal, decides on the steps and chains the calls: read invoices, check a balance, prepare a payment. Its behaviour depends on the situation, and one single key covers every one of these actions.
Take an agent in charge of processing a company’s invoices. With an API key, the accounting service it calls only knows that the request comes from someone holding the key. It has no way of telling which agent is acting, on behalf of which team, or how far that agent may go.
The service therefore needs three answers: which agent is addressing it, on whose behalf it acts, and what it is allowed to do. These checks go by the name Know Your Agent, by analogy with the Know Your Customer of banks, and the Decentralized Identity Foundation devotes a specification to them.
The principle of verifiable credentials
A verifiable credential (VC) is a document signed by an issuer, stating attributes about a subject. Its format is defined by the VC 2.0 data model of the W3C. The mechanism involves three roles:
- the issuer, who signs the credential;
- the holder, who keeps it and presents it;
- the verifier, who checks the signature before accepting the attributes.
Any organisation can issue a credential: a university for a degree, an employer for a role, a state for a civil identity. It works much like the HTTPS certificate, where the browser accepts a signature because it trusts the authority behind it. Here the verifier decides which issuers it accepts, and for which attributes.
A credential always designates its subject by an identifier. Before describing the agent’s rights, it therefore needs one, along with the proof that goes with it.
Giving an autonomous agent an identifier
That identifier has to stay stable and rest on a key that only the agent controls. The W3C defines Decentralized Identifiers (DID) for this, and their specification has been a recommendation since 2022. A DID points to a document that publishes the public keys of its holder.
For the invoice-processing agent, the identifier could be:
did:web:example.com:agents:invoices
The did:web method means the document is published on a website, here at
https://example.com/agents/invoices/did.json. The identifier therefore depends on
the company’s domain name, which the company keeps control of. KYA-OS accepts this
method and did:key, which derives the identifier from the public key itself.
The document holds the following:
{
"@context": [
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/suites/jws-2020/v1"
],
"id": "did:web:example.com:agents:invoices",
"controller": "did:web:example.com",
"verificationMethod": [
{
"id": "did:web:example.com:agents:invoices#key-1",
"type": "JsonWebKey2020",
"controller": "did:web:example.com:agents:invoices",
"publicKeyJwk": { "kty": "EC", "crv": "P-256", "x": "...", "y": "..." }
}
],
"authentication": ["did:web:example.com:agents:invoices#key-1"]
}
The id field repeats the agent’s identifier. The controller field names the
company, which can modify this document. The verificationMethod block holds the
agent’s public key, and authentication states that this key proves its identity.
In practice, when the agent signs a request with its private key, the service fetches this document, reads the public key and checks the signature. The identifier and the proof are now separate, unlike with the API key.
Describing the agent’s rights in a mandate
The agent’s rights are described in a credential issued by the company. KYA-OS gives it a name, the delegation credential, and two possible shapes. The one used here builds on ZCAP-LD, a format for delegated capabilities, and on the VC 2.0 model:
{
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://w3id.org/security/zcap/v1",
"https://kya-os.org/ns/delegation/v1"
],
"type": ["VerifiableCredential", "DelegationCredential"],
"issuer": "did:web:example.com",
"validUntil": "2026-09-22T18:00:00Z",
"credentialSubject": {
"id": "urn:zcap:del_2026_09_22_001",
"invoker": "did:web:example.com:agents:invoices",
"invocationTarget": "did:web:accounting.example.com",
"parentCapability": "did:web:accounting.example.com",
"allowedAction": ["invoices.read", "payments.propose"],
"caveats": [
{ "type": "MaxAmount", "limit": "500.00", "currency": "EUR" }
]
},
"credentialStatus": {
"type": "BitstringStatusListEntry",
"statusPurpose": "revocation",
"statusListIndex": "94567",
"statusListCredential": "https://example.com/status/delegations"
}
}
The issuer field names the company that delegates, and validUntil the end of
validity, here the same evening.
The credentialSubject block carries the delegation itself. The invoker field
names the agent that may use it, invocationTarget the resource it applies to, here
the accounting API, and allowedAction the list of permitted actions, whose names
remain specific to each service. The caveats array adds reservations, including a
cap of 500 euros per payment.
The credentialStatus block allows revoking the mandate before it expires. It points
to a status list published by the
company. If the agent is compromised, the company updates that list and the mandate
is refused at the very next check.
The company’s signature, added at issuance, is left out here.
A mandate describes a permission, and not a quality of the agent. The specification enforces that separation: a verifier rejects a delegation credential whose subject would carry any other attribute, so that the authorization decision stays clear of identity claims.
A difference with the HTTPS certificate shows up here. A certificate is presented by the server that is its subject, whereas a mandate is kept by the agent and presented when it needs it. For a person, the same credential sits in a wallet on their phone.
A holder can also reveal only part of a credential. Someone proving they are of age keeps their date of birth to themselves. This is called selective disclosure, and the SD-JWT format, chosen among others by the European wallet, already allows it. An agent mandate lends itself less to this, since the service checks precisely which actions are allowed and which resource is targeted.
Tracing a program back to its responsible party
When an agent sends a request, for example a payment proposal, it presents its mandate and proves it holds the key of its DID. The service then runs the following checks:
1. Resolve the agent's DID and fetch its public key
2. Check that the request is signed with that key
3. Resolve the issuer's DID and check the signature on the mandate
4. Check that the invoker field names this agent
5. Check that invocationTarget names the resource being called
6. Check the validUntil date
7. Look up the status list to confirm the mandate is still active
8. Compare the requested action with allowedAction and the caveats
If any of these steps fails, the request is refused.
These checks establish that the mandate was indeed issued by did:web:example.com.
KYA-OS calls that entity the responsible party, at the root of the delegation chain,
and holds it accountable for the actions taken below it. What remains is knowing who
stands behind that identifier. The service can rely on a list of issuers it knows, or
ask the company for a credential about its own identity, issued by a third party.
The company therefore needs a wallet to keep a credential about itself, issued by a third party the service recognises, for example an official register. That wallet rests on the same building blocks described above, and it can be put in place with today’s tools. The European Union is preparing a standardised version for companies, presented in the June 2026 EUDI Wallet news, with verified identification data and digital management of representation mandates. The service then links every action of the agent to an organisation whose identity is attested.
Limits
- A mandate states what the agent is allowed to do, and not what it will actually do. The service therefore has to check every action, like the decision engine presented in the article on ODRL.
- Delegation between agents remains an open subject. When an agent hands a task to another agent, the final service must be able to walk back the whole chain of mandates. The Decentralized Identity Foundation devotes a report on delegated authority to it.
Conclusion
A digital identity rests on three elements: an identifier, a proof and attributes. A user account gathers them in a database. An HTTPS certificate spreads them across a domain name, a private key and the signature of an authority. An API key merges them into a single secret, which is enough for a script and becomes awkward as soon as an autonomous agent picks its own actions.
The building blocks for doing otherwise exist and are standardised. A DID gives the agent a stable identifier and a key it alone holds. A mandate describes what it may do, on which resource, until when and under which reservations. A status list allows revoking it before its term. The service checks all of this by computation, without asking the issuer again at every request, and traces the chain back to the organisation that answers for the agent.
The work that remains has less to do with cryptography than with organisation. A DID document has to be published for each agent, mandates issued and signed, the status list kept up to date, the trusted issuers chosen, and the company tied to an attested identity. The specifications are still young, KYA-OS in particular, and delegation from one agent to another still calls for shared rules. A service that already receives requests from agents can start with the simplest form: a short-lived mandate, a named resource and a counted set of actions.