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 IDdid-registry-tenant— your tenant nameenterprise.test.waltid.cloud— your Enterprise API domainregistry.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:
| Route | URL | Notes |
|---|---|---|
| Public resolution (use this) | https://registry.test.waltid.cloud/did-registry-tenant/max/did.json | Standard did:web HTTPS resolution |
| Gateway (colon-encoded) | https://registry.test.waltid.cloud/did-registry-tenant:max:did.json | Non-standard; do not use |
| Internal API | https://waltid.enterprise.test.waltid.cloud/v1/waltid.did-registry-tenant.did-web-registry/did-registry-service-api/registry/did-registry-tenant:max/did.json | Internal 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
| Symptom | Check |
|---|---|
404 on resolution URL | DID Store dependency on both DID Service and DID Registry; domain and path match the URL |
| Wrong domain in DID | Host alias created; domain parameter uses the custom domain, not the Enterprise API domain |
| Tenant config mismatch | didRegistry in tenant config matches the full registry service ID |
| Gateway not routing | DNS points to Gateway; Gateway configured to forward did.json requests |
See also External DID Web Hosting for DNS and reverse-proxy configuration.
