Tenant-level DIDs
Tenant-level DIDs are scoped to a specific tenant within the organization. Each tenant gets its own namespace in the DID path, which provides isolation between tenants — ideal for multi-tenant SaaS deployments where each customer should have separate DIDs.
When to use tenant-level DIDs
| Use tenant-level when… | Example |
|---|---|
| Each tenant needs its own DID namespace | did:web:domain.com:tenant-a:alice |
| You want strict isolation between tenants | Tenant A cannot resolve Tenant B's DIDs by path |
| DIDs represent users or entities within a tenant | Per-user holder DIDs in a wallet tenant |
For organization-wide identities, use Organization-level DIDs instead.
DID format and resolution
| Value | |
|---|---|
| DID pattern | did:web:{domain}:{tenant}:{user} |
| Example DID | did:web:waltid.enterprise-sandbox.waltid.dev:users:bob |
| Resolution URL | https://waltid.enterprise-sandbox.waltid.dev/users/bob/did.json |
When creating a DID via the DID Service, include the tenant name in the path:
{
"domain": "waltid.enterprise-sandbox.waltid.dev",
"path": "users/bob"
}
See Create a did:web for the full API.
The short resolution URL works by looking up the tenant's configured didRegistry: the first path segment ({tenant}) is matched against a tenant ID within the organization, and the request is resolved against that tenant's registry — see Resolution → Tenant-relative resolution for the underlying mechanism.
Setup overview
Tenant-level DIDs require three steps:
- Create the DID Registry service inside the tenant
- Configure the tenant to reference that registry via
didRegistry - Create DIDs with tenant-scoped paths
The didRegistry value is stored and read back by the tenant config endpoints exactly as given — no expansion happens there. At resolution time, though, it's tried both as given and expanded relative to the tenant, so either a full resource path ({orgID}.{tenantID}.{serviceID}, e.g. waltid.users.did-registry-main) or a short, tenant-relative service ID (e.g. did-registry-main) resolves correctly. The full path is still recommended, since it's what you'll see echoed back from config-view calls and is unambiguous at a glance.
Loading diagram...
Step 1: Create the DID Registry Service
Create the registry inside the target tenant. Choose a service ID (e.g. did-registry-main) — you will reference the full path in the tenant configuration.
Endpoint: /v1/{target}/resource-api/services/create | API Reference
Example Request
curl -X 'POST' \
'https://{orgID}.enterprise-sandbox.waltid.dev/v1/{target}/resource-api/services/create' \
-H 'accept: */*' \
-H 'Authorization: Bearer {yourToken}' \
-H 'Content-Type: application/json' \
-d '{
"type": "did-registry"
}'
Path Parameters
orgID— Organization base URL or host alias.target— resourceIdentifier — Organization + tenant + service ID:{organizationID}.{tenantID}.{serviceID}, e.g.waltid.users.did-registry-main
Body Parameters
type— serviceType — Must bedid-registry
Response Codes
201— Service created successfully.
Then add the DID Store as a dependency — see Setup → Step 3b.
Step 2: Configure the tenant
Link the tenant to the DID Registry service. You can set this when creating a new tenant or update an existing one.
Option A: Create a new tenant with registry configured
Endpoint: /v1/{target}/resource-api/tenants/create | API Reference
Example Request
curl -X 'POST' \
'https://{orgID}.enterprise-sandbox.waltid.dev/v1/{target}/resource-api/tenants/create' \
-H 'accept: */*' \
-H 'Authorization: Bearer {yourToken}' \
-H 'Content-Type: application/json' \
-d '{
"configuration": {
"didRegistry": "waltid.users.did-registry-main"
}
}'
Path Parameters
orgID— Organization base URL.target— resourceIdentifier — Organization + tenant ID:{organizationID}.{tenantID}, e.g.waltid.users
Body Parameters
configuration.didRegistry— String — Full resource path of the DID Registry service (matching Step 1). This is stored and read back exactly as given; a short ID like"did-registry-main"also resolves correctly (expanded relative to the tenant at resolution time), but the full path is recommended for clarity.
Response Codes
201— Tenant created successfully.
Option B: Update an existing tenant
Use the tenant config update endpoint to set didRegistry on an existing tenant. See External DID Web Hosting → Step 2 for the API.
Step 3: Verify the configuration
Confirm the tenant points to the correct registry:
Endpoint: /v1/{target}/tenant-api/tenants/config/view | API Reference
curl -X GET \
'https://{orgID}.enterprise-sandbox.waltid.dev/v1/{target}/tenant-api/tenants/config/view' \
-H 'accept: application/json' \
-H 'Authorization: Bearer {yourToken}'
Path Parameters
orgID— Organization base URL.target/{tenant}— Full tenant resource identifier, e.g.waltid.users
Expected response:
{
"didRegistry": "waltid.users.did-registry-main"
}
The response echoes back exactly what was stored in Step 2. Supplying the full resource path there is recommended for clarity — though a short, tenant-relative service ID also works at resolution time (see Resolution → Tenant-relative resolution).
Step 4: Create and verify a DID
Create a did:web with a tenant-scoped path (see Create a did:web), then verify resolution:
# Create the DID (via DID Service — path should be "{tenant}/user")
# Then verify:
curl https://waltid.enterprise-sandbox.waltid.dev/users/bob/did.json
Next steps
- Setup — Base registry setup and DID Store dependency
- Create a did:web — Generate tenant-scoped DIDs
- External DID Web Hosting — Publish DIDs on a custom domain
