> For the complete documentation index, see [llms.txt](https://docs.toolkit.equs.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.toolkit.equs.ai/standards/crypto/dedicated-did.md).

# Dedicated DID

Give each of your users, and each of their agents, a did:web identity of their own, with keys held in a custodial wallet.

A **dedicated DID** is a `did:web` of a Holder's own. Each of your users, and each of their AI agents, can have one, backed by keys held for them in a custodial wallet on the platform. The DID document publishes the Holder's public keys, including a **key-agreement key** you can encrypt data to. Only the Holder's wallet can decrypt that data, and you never handle the Holder's private keys.

{% hint style="info" %}
This uses the **Web Wallet**, which is off by default and available on request.
{% endhint %}

**Before you start** you need:

* The [Web Wallet](/standards/wallets/web-wallet.md) enabled for your Tenant.
* Your identity provider connected, so that each Holder is one of your users. See [Bring Your Own IdP](/standards/onboarding/bring-your-own-idp.md).

### Who creates it 👥

It depends on whether the Holder uses the Web Wallet themselves:

* **Holders who use the Web Wallet** set up their own dedicated DID there, signed in through your identity provider. There's nothing for you to call. This is what [Decrypt Payload (Embed UI)](/standards/crypto/decrypt-payload-embed-ui.md) relies on.
* **Holders who won't use a wallet** need you to onboard them. That's typically the case when you decrypt data for them from your back end, with [Decrypt Payload (API)](/standards/crypto/decrypt-payload-api.md).
* **Agents** are always onboarded by you, since an agent never signs in to set up a wallet of its own.

### Onboard a user 🙋

Onboard a user when they won't set up a wallet themselves. Use the user ID your identity provider reports for them: the same `sub` they sign in with. The platform creates their wallet, a key pair and a `did:web` with a key-agreement key, and registers the DID so it can be resolved.

The call does the cryptographic setup synchronously, so expect it to take longer than a typical create call.

{% hint style="info" icon="rectangle-api" %}
This call is on the Identity Manager API.
{% endhint %}

## Create a holder user

> Creates a Keycloak user account for a holder, tagged with the tenant's slug and the external identity provider user id, links a federated identity for that user under the tenant's identity provider, and registers the user in the Web Custodial Wallet.

```json
{"openapi":"3.0.0","info":{"title":"EqusDTK Identity Manager Documentation","version":"0.1.2"},"tags":[{"name":"Holders","description":""}],"security":[{"sp":[]},{"tenant":[]},{"sp":[]}],"components":{"securitySchemes":{"sp":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Service Provider. Token of the Service Provider client in the `agents` realm, with no `tenantId` claim. Authorize one bearer scheme at a time."},"tenant":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Tenant. Token of a Tenant machine client or of a Tenant portal user, carrying the `tenantId` claim. Authorize one bearer scheme at a time."}},"schemas":{"CreateUserRequest":{"type":"object","properties":{"extUserId":{"type":"string","description":"External IdP user unique ID"}},"required":["extUserId"]},"CreateUserResponse":{"type":"object","properties":{"id":{"type":"string","description":"Internal Idp user unique ID"}},"required":["id"]}}},"paths":{"/api/v1/holders/users":{"post":{"description":"Creates a Keycloak user account for a holder, tagged with the tenant's slug and the external identity provider user id, links a federated identity for that user under the tenant's identity provider, and registers the user in the Web Custodial Wallet.","operationId":"HoldersController_createUser_api/v1","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserRequest"}}}},"responses":{"201":{"description":"When the user is created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserResponse"}}}},"400":{"description":"- When the tenant slug and subject identifier together exceed the username length limit.\n- When the request names no tenant: the token carries no tenantId claim and the Service Provider sent no x-tenant-id header."},"401":{"description":"When the operator-portal-ui token is missing or invalid, or when it carries no tenantId claim and belongs to a client other than the Service Provider."},"409":{"description":"When a holder with this subject identifier already exists for this tenant."}},"summary":"Create a holder user","tags":["Holders"]}}}}
```

### Onboard an agent 🤖

An agent gets a dedicated DID of its own, separate from the user it acts for. Onboard the user first: an agent is always created for an existing user. The platform provisions the agent's wallet, key pair and `did:web` the same way, and returns an `agentId`.

{% hint style="info" icon="rectangle-api" %}
This call is on the Identity Manager API.
{% endhint %}

## Create a holder agent

> Creates a placeholder, disabled Keycloak user account for a holder agent, tagged with the tenant's slug, and registers the agent in the Web Custodial Wallet under the given userId and name.

```json
{"openapi":"3.0.0","info":{"title":"EqusDTK Identity Manager Documentation","version":"0.1.2"},"tags":[{"name":"Holders","description":""}],"security":[{"sp":[]},{"tenant":[]},{"sp":[]}],"components":{"securitySchemes":{"sp":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Service Provider. Token of the Service Provider client in the `agents` realm, with no `tenantId` claim. Authorize one bearer scheme at a time."},"tenant":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Tenant. Token of a Tenant machine client or of a Tenant portal user, carrying the `tenantId` claim. Authorize one bearer scheme at a time."}},"schemas":{"CreateAgentRequest":{"type":"object","properties":{"userId":{"type":"string","description":"User unique ID this agent is associated with"},"name":{"type":"string","description":"The agent's own name"}},"required":["userId","name"]},"CreateAgentResponse":{"type":"object","properties":{"id":{"type":"string","description":"Agent unique ID"}},"required":["id"]}}},"paths":{"/api/v1/holders/agents":{"post":{"description":"Creates a placeholder, disabled Keycloak user account for a holder agent, tagged with the tenant's slug, and registers the agent in the Web Custodial Wallet under the given userId and name.","operationId":"HoldersController_createAgent_api/v1","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAgentRequest"}}}},"responses":{"201":{"description":"When the agent is created.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateAgentResponse"}}}},"400":{"description":"When the request names no tenant: the token carries no tenantId claim and the Service Provider sent no x-tenant-id header."},"401":{"description":"When the operator-portal-ui token is missing or invalid, or when it carries no tenantId claim and belongs to a client other than the Service Provider."},"409":{"description":"When the user already has an agent with the given name."}},"summary":"Create a holder agent","tags":["Holders"]}}}}
```

### Look up a Holder's DID 🔍

When all you have is a Holder's ID, use it to fetch their `did:web` and DID document: the `userId` for a user, or the `agentId` for an agent. The document's `keyAgreement` entry holds the public key to encrypt to.

{% hint style="info" icon="rectangle-api" %}
This call is on the Web Wallet API.
{% endhint %}

## Fetch a holder did:web (Service Provider / Tenant)

> Fetches the holder did:web and doc for Service Provider or Tenant with holderId provided

```json
{"openapi":"3.0.0","info":{"title":"EqusDTK Web Custodial Wallet Documentation","version":"0.1.2"},"tags":[{"name":"Holder DID","description":""}],"security":[{"sp":[]},{"tenant":[]},{"sp":[]}],"components":{"securitySchemes":{"sp":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Service Provider. Token of the Service Provider client in the `agents` realm, with no `tenantId` claim. Authorize one bearer scheme at a time."},"tenant":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Tenant. Token of a Tenant machine client or of a Tenant portal user, carrying the `tenantId` claim. Authorize one bearer scheme at a time."}}},"paths":{"/api/v1/did/web/{holderId}":{"get":{"description":"Fetches the holder did:web and doc for Service Provider or Tenant with holderId provided","operationId":"DidController_getForHolder_api/v1","parameters":[{"name":"holderId","required":true,"in":"path","description":"Holder unique ID","schema":{"type":"string"}}],"responses":{"200":{"description":"did:web fetched successfully"},"404":{"description":"did:web was not found"}},"summary":"Fetch a holder did:web (Service Provider / Tenant)","tags":["Holder DID"]}}}}
```

### Encrypt data to a Holder 🔐

To keep data so that only a Holder's wallet can open it, encrypt it (or a symmetric key that protects it) to the Holder's key-agreement public key, as a compact JWE. Store it alongside the data it protects.

When you need the data back, the Holder's wallet decrypts it and returns the result re-encrypted to a key only you hold:

* [Decrypt Payload (API)](/standards/crypto/decrypt-payload-api.md): from your back end, without the Holder present.
* [Decrypt Payload (Embed UI)](/standards/crypto/decrypt-payload-embed-ui.md): with the Holder present, approving in their Web Wallet.

{% hint style="warning" %}
**Decryption always uses the Holder's current key-agreement key.** If the key is rotated, data encrypted to the old key can no longer be decrypted.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.toolkit.equs.ai/standards/crypto/dedicated-did.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
