DID Web Registry Setup Guide

This guide walks through a complete end-to-end setup: tenant, supporting services, DID Registry, custom domain via a host alias, and creating a resolvable did:web.

It assumes you use the Enterprise API Gateway — an optional component that exposes public resolution endpoints while keeping the main Enterprise API private. Custom domains typically point to the Gateway rather than the internal API.

Replace placeholder values throughout this guide:

  • waltid — your organization ID
  • did-registry-tenant — your tenant name
  • enterprise.test.waltid.cloud — your Enterprise API domain
  • registry.test.waltid.cloud — your custom resolution domain

Architecture

Loading diagram...

Step 1: Create the tenant

Create a tenant and pre-configure which DID Registry service it will use. The registry service ID (did-web-registry) must match the service you create in Step 5.

curl -w "
HTTP %{http_code}
" -X 'POST' \
  'https://waltid.enterprise.test.waltid.cloud/v1/did-registry-tenant/resource-api/tenants/create' \
  -H 'accept: */*' \
  -H 'Authorization: Bearer {yourBearerToken}' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "DID Registry Example Tenant",
    "configuration": {
       "didRegistry": "waltid.did-registry-tenant.did-web-registry"
    }
  }'

Expected: 201

Step 2: Create the DID Store

The DID Store persists DID documents. Both the DID Service and DID Registry depend on it.

curl -w "
HTTP %{http_code}
" -X 'POST' \
  'https://waltid.enterprise.test.waltid.cloud/v1/did-registry-tenant.did-store/resource-api/services/create' \
  -H 'accept: */*' \
  -H 'Authorization: Bearer {yourBearerToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "type": "did-store"
}'

Expected: 201

Step 3: Create the DID Service

The DID Service creates did:web identifiers and stores documents in the DID Store.

curl -w "
HTTP %{http_code}
" -X 'POST' \
  'https://waltid.enterprise.test.waltid.cloud/v1/did-registry-tenant.did-service/resource-api/services/create' \
  -H 'accept: */*' \
  -H 'Authorization: Bearer {yourBearerToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "type": "did"
}'

Expected: 201

Wire the DID Service to the DID Store:

curl -w "
HTTP %{http_code}
" -X 'POST' \
  'https://waltid.enterprise.test.waltid.cloud/v1/did-registry-tenant.did-service/did-service-api/dids/dependencies/add' \
  -H 'accept: */*' \
  -H 'Authorization: Bearer {yourBearerToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "dependency": "waltid.did-registry-tenant.did-store"
}'

Expected: 201

Step 4: Create the DID Registry Service

curl -w "
HTTP %{http_code}
" -X 'POST' \
  'https://waltid.enterprise.test.waltid.cloud/v1/did-registry-tenant.did-web-registry/resource-api/services/create' \
  -H 'accept: */*' \
  -H 'Authorization: Bearer {yourBearerToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "type": "did-registry"
}'

Expected: 201

Wire the DID Registry to the DID Store:

curl -w "
HTTP %{http_code}
" -X 'POST' \
  'https://waltid.enterprise.test.waltid.cloud/v1/did-registry-tenant.did-web-registry/did-registry-service-api/dependencies/add' \
  -H 'accept: */*' \
  -H 'Authorization: Bearer {yourBearerToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "dependency": "waltid.did-registry-tenant.did-store"
}'

Expected: 201

Step 5: Create a host alias

Map a custom domain to the tenant so DIDs can use registry.test.waltid.cloud instead of the default Enterprise domain.

curl -w "
HTTP %{http_code}
" -X 'POST' \
  'https://waltid.enterprise.test.waltid.cloud/v1/waltid.registry-host-alias/host-alias-api/host-aliases/create' \
  -H 'accept: */*' \
  -H 'Authorization: Bearer {yourBearerToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "domain": "registry.test.waltid.cloud"
}'

Expected: 201

Step 6: Create a KMS and key

DIDs need a signing key from KMS.

# Create KMS
curl -w "
HTTP %{http_code}
" -X 'POST' \
  'https://waltid.enterprise.test.waltid.cloud/v1/did-registry-tenant.kms/resource-api/services/create' \
  -H 'accept: */*' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer {yourBearerToken}' \
  -d '{
  "type": "kms"
}'

# Generate a key — save the returned keyId for the next step
curl -X 'POST' \
  'https://waltid.enterprise.test.waltid.cloud/v1/did-registry-tenant.kms/kms-service-api/keys/generate' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer {yourBearerToken}' \
  -d '{
  "backend": "jwk",
  "keyType": "secp256r1"
}'

Wire the DID Service to the KMS — without this, DID creation in the next step fails with 400 No dependency for KMS with resource path '...':

curl -w "
HTTP %{http_code}
" -X 'POST' \
  'https://waltid.enterprise.test.waltid.cloud/v1/did-registry-tenant.did-service/did-service-api/dids/dependencies/add' \
  -H 'accept: */*' \
  -H 'Authorization: Bearer {yourBearerToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "dependency": "waltid.did-registry-tenant.kms"
}'

Expected: 201

Step 7: Create a did:web

Create the DID using the custom domain and a tenant-scoped path. Replace {keyId} with the key ID from Step 6.

curl -w "
HTTP %{http_code}
" -X 'POST' \
  'https://waltid.enterprise.test.waltid.cloud/v1/did-registry-tenant.did-service/did-service-api/dids/create/web' \
  -H 'Authorization: Bearer {yourBearerToken}' \
  -H 'accept: application/json' \
  -H 'Content-Type: application/json' \
  -d '{
  "type": "single_key_reference",
  "keyReference": "{keyId}",
  "domain": "registry.test.waltid.cloud",
  "path": "did-registry-tenant/max"
}'

Expected: 201 with a response containing "did": "did:web:registry.test.waltid.cloud:did-registry-tenant:max".

Step 8: Verify resolution

The created DID is routed as follows:

RouteURLNotes
Public resolution (use this)https://registry.test.waltid.cloud/did-registry-tenant/max/did.jsonStandard did:web HTTPS resolution
Gateway (colon-encoded)https://registry.test.waltid.cloud/did-registry-tenant:max:did.jsonNon-standard; do not use
Internal APIhttps://waltid.enterprise.test.waltid.cloud/v1/waltid.did-registry-tenant.did-web-registry/did-registry-service-api/registry/did-registry-tenant:max/did.jsonInternal only — {id} is the colon-separated path suffix only, without the domain

Verify the public URL:

curl https://registry.test.waltid.cloud/did-registry-tenant/max/did.json

A 200 response with the DID document JSON confirms the full setup is working.

Troubleshooting

SymptomCheck
404 on resolution URLDID Store dependency on both DID Service and DID Registry; domain and path match the URL
Wrong domain in DIDHost alias created; domain parameter uses the custom domain, not the Enterprise API domain
Tenant config mismatchdidRegistry in tenant config matches the full registry service ID
Gateway not routingDNS points to Gateway; Gateway configured to forward did.json requests

See also External DID Web Hosting for DNS and reverse-proxy configuration.

Last updated on August 18, 2026