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 namespacedid:web:domain.com:tenant-a:alice
You want strict isolation between tenantsTenant A cannot resolve Tenant B's DIDs by path
DIDs represent users or entities within a tenantPer-user holder DIDs in a wallet tenant

For organization-wide identities, use Organization-level DIDs instead.

DID format and resolution

Value
DID patterndid:web:{domain}:{tenant}:{user}
Example DIDdid:web:waltid.enterprise-sandbox.waltid.dev:users:bob
Resolution URLhttps://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:

  1. Create the DID Registry service inside the tenant
  2. Configure the tenant to reference that registry via didRegistry
  3. 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.

CURL

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.
  • targetresourceIdentifier — Organization + tenant + service ID: {organizationID}.{tenantID}.{serviceID}, e.g. waltid.users.did-registry-main

Body Parameters

  • typeserviceType — Must be did-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

CURL

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.
  • targetresourceIdentifier — Organization + tenant ID: {organizationID}.{tenantID}, e.g. waltid.users

Body Parameters

  • configuration.didRegistryString — 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:

CURL

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

Last updated on August 24, 2026