Platform & Tenant Onboarding
VDX is the platform layer above the EDK. It shares the EDK onboarding flow, application tenant bootstrap, license activation, secret backend selection, onboarding policy, tenant creation, owner activation, and tenant identity-provider setup, and adds white-labeled portals and dashboards plus the ability to run many service instances per tenant.
This page gives the ordered onboarding sequence and calls out the VDX-specific deltas. For step-by-step detail on each shared stage, see the EDK platform and tenant guides (Onboarding Walkthrough, Application Tenant, License & Policy, Registration Journeys, Owner Activation, and Tenant Federation).
Two REST surfaces drive onboarding. The exact request and response shapes for every operation are documented interactively in the Scalar API references linked below.
| Surface | Base path | Reference | Purpose |
|---|---|---|---|
| Platform Admin | /api/platform-admin/v1 | Platform Admin API | Application tenant bootstrap, license, secret backend, onboarding policy, tenant lifecycle, domains, public endpoints, federation, owner redemption |
| Software / Instance Manager | /api/services/v1 | Software / Instance Manager API | Provision and manage issuer, verifier, and authorization-server instances per tenant |
Every authenticated call carries a bearer JWT. Tenant context is resolved from that token, never from a request parameter. The application tenant is always authenticated through the platform's own hosted identity provider, so operators can always reach the platform administration surface, including before any customer tenant exists.
The Onboarding Sequence
1. Bootstrap the application (platform) tenant + hosted IdP
2. Activate the license
3. Select the secret backend
4. Set the onboarding policy
5. Create a tenant (admin-direct, admin-invite, or self-service signup)
6. Owner activation
7. Tenant configures its own IdP / federation
8. Provision MANY service instances + bind public endpoints ← VDX delta
Steps 1 through 7 are the shared EDK flow. Step 8 is the VDX delta: a tenant runs many service instances per type instead of one.
1. Bootstrap the platform tenant
A VDX deployment starts by bootstrapping the application tenant, the tenant that administers the deployment. It always has a hosted authorization server, so operators can sign in even before any customer tenant exists, and independently of any customer tenant's later external IdP configuration.
The platform tenant is initialized from application.tenant.* configuration on the container deployment, or by calling POST /application/tenant/bootstrap (bootstrapApplicationTenant) with the operator contact details. The call creates the application tenant and its hosted authorization server, or reconciles them if they already exist, and reports whether the platform can register its first real tenant. See the Platform Admin API reference for the request schema.
2. Activate the license
Production tenant onboarding is gated on an active license. Activate it by submitting the signed license token with PUT /application/license (putApplicationLicense), and read the snapshot back with GET /application/license (getApplicationLicense).
Container deployments can also load license material at startup. The Kubernetes overlays mount license files at /var/run/secrets/vdx/license, and the runtime reads the signed license envelope, recipient key, and trust material from that directory. The expected runtime files are:
/var/run/secrets/vdx/license/license.jwe
/var/run/secrets/vdx/license/recipient-private-key.pem
/var/run/secrets/vdx/license/root-ca-bundle.pem
/var/run/secrets/vdx/license/crl-bundle.pem
The development overlay also mounts /var/run/secrets/vdx/license/dev-license.jwe for local license material. The object names in the shipped overlays are vdx-dev-license-material and vdx-dev-license-trust for development, vdx-staging-license-material and vdx-staging-license-trust for staging, and vdx-production-license-material and vdx-production-license-trust for production. Operators may replace those names in their own Helm values, but the mounted filenames must stay aligned with the runtime paths.
The snapshot is what the rest of onboarding reads. It exposes a status (ACTIVE, EXPIRED, INVALID, or MISSING), a features set (selfSignup, subtenants), and limits (maxRootTenants, maxTotalTenants, maxHierarchyDepth, subtenantsAllowed). Self-service signup is only offered when selfSignup is present, subtenant journeys only when subtenants is present, and tenant creation stops at the count limits. The license also bounds per-service instance limits, the ceiling on how many issuer, verifier, and authorization-server instances a tenant may run (see Many Instances per Tenant). Configuration can further restrict a licensed capability, but cannot enable one the license disallows. The schemas are in the Platform Admin API reference.
3. Select the secret backend
Per-tenant secrets (identity-provider client secrets, SMTP passwords, integration secrets) are stored in a single selected backend. Tenant configuration keeps only opaque references such as clientSecretRef; plaintext secrets are write-only on the way in and are never echoed. The backend enum decides where they go:
azure-key-vault: Azure Key Vault. Use it on Azure deployments.hashicorp-vault: HashiCorp Vault. Use it for a Vault-centric or cloud-neutral deployment.kubernetes-secret-mount: secrets that Kubernetes mounts into the pod. Use it when secrets are provisioned as Kubernetes Secrets.config-system-dev-only: the config system itself. Local and development only, never production.
Set or reconfigure the backend with PUT /application/secrets/backend (putSecretBackend) and read the current selection with GET /application/secrets/backend (getSecretBackend). A non-dev backend is required before invite or self-service signup journeys are enabled. See the Platform Admin API reference.
4. Set the onboarding policy
The onboarding policy toggles which tenant-creation routes the platform accepts: root-tenant creation, admin invite, self-service signup, subtenant signup, and whether self-signup requires operator approval. Set it with PUT /application/onboarding (putApplicationOnboarding).
A toggle being on does not mean the route is usable. Read GET /application/onboarding/availability (getApplicationOnboardingAvailability) to see the evaluated result, which combines the policy with the license features and limits, email readiness, and the secret backend selection, and reports a reason for each route that is not enabled. The license is evaluated before policy: a toggle can turn off or constrain a licensed route, but cannot enable a route the license disallows. See the Platform Admin API reference.
5. Create a tenant
A tenant is created with POST /tenants (registerTenant), with a default authorization server and an owner bootstrap. Owner identity is supplied as local contact details; the owner's external IdP is not collected at creation time. The decision that shapes this step is ownerDelivery.mode: none has the operator complete owner activation out of band (works without an email service), and email has the platform email the owner an invitation (needs the email service). (manual is not accepted over REST.) A root tenant leaves parentTenantId null; a subtenant sets it to the parent's id. For the public path, use POST /tenants/signup/request (requestTenantSignup). The request schemas are in the Platform Admin API reference.
There are three ways to drive this step:
| Route | How the owner is reached | Email service required |
|---|---|---|
| Admin-direct | Operator creates the tenant and completes owner onboarding through authenticated admin flows | No |
| Admin-invite | Operator creates the tenant; the platform emails the owner an activation link | Yes |
| Self-service signup | A prospective owner signs up from a public page; the platform emails a verification link, with optional operator approval | Yes |
Admin-direct creation is the only route that works without an email service configured. Self-service signup additionally requires the selfSignup license feature; any subtenant route requires the subtenants feature with subtenantsAllowed = true, within maxHierarchyDepth.
Identity-provider settings (federation provider, OIDC issuer, OAuth client id and secret) are never collected during tenant creation. They belong to post-activation tenant IdP setup.
6. Owner activation
The tenant owner activates through the owner bootstrap path with POST /owner/redeem (redeemOwnerInvitation), a public, unauthenticated endpoint where the owner submits the one-time token plus the new credential they set. For admin-invite and self-service routes the platform delivers the token to the owner by email server-side; the token is delivered out of band and is never returned by any API. See the Platform Admin API reference.
7. Tenant configures its own IdP
Once the owner is active, the tenant chooses how its users sign in: platform-hosted (the platform authorization server acts as the tenant's IdP, the default) or external (federate to the tenant's own OIDC IdP). To federate, register the IdP with POST /federation/idps (createTenantIdp), supplying the issuer, clientId, and claimsMapping (which maps subject, email, and displayName). The clientSecret is write-only in, stored in the selected secret backend, and returned only as a clientSecretRef. Probe it with POST /federation/idps/{idpId}/test, then enable it with POST /federation/idps/{idpId}/enable.
Tenant federation is license-gated on the federation feature. Configuring a tenant IdP never affects application-tenant login, which always stays on the platform's hosted authorization server. The schemas are in the Platform Admin API reference.
Many Instances per Tenant
On the EDK, a tenant runs at most one OID4VCI issuer, one OID4VP verifier, and one OAuth2 authorization server. On VDX, a tenant can run many instances of each service type, bounded only by the license's per-service instance limits. Each instance is configured, deployed, and routed independently.
A VDX tenant provisions as many issuer, verifier, and authorization-server instances as its license allows, for example a separate issuer per credential program, or distinct verifiers for distinct relying-party contexts, all under one tenant.
Instances are provisioned through the Software / Instance Manager API at /api/services/v1. There is a generic surface and three typed surfaces:
| Endpoint | Manages |
|---|---|
POST /api/services/v1/services/instances | Generic cross-type instance creation |
POST /api/services/v1/oid4vci/issuers | OID4VCI issuer instances |
POST /api/services/v1/oid4vp/verifiers | OID4VP verifier instances |
POST /api/services/v1/oauth2/servers | OAuth2 authorization-server instances |
Create a typed issuer instance with POST /oid4vci/issuers. The decisions here are the managementMode (whether the platform manages the instance lifecycle) and which authorization server it binds to. A second issuer for the same tenant is created the same way with a different display name. Each instance has its own id, lifecycle, and configuration, and supports lifecycle actions (deploy, suspend, resume, decommission) and deployment and endpoint sub-resources under /services/instances/{instanceId}. See the Software / Instance Manager API reference for the full request and response shapes.
Independent routing per instance
Because a tenant runs several instances of the same type, each needs a distinct public URL. A public-endpoint binding makes an instance independently routable by a custom host or a path prefix. Bind one with PUT /tenants/{tenantId}/public-endpoints/{serviceType} (upsertTenantPublicEndpoint), where serviceType is OID4VCI_ISSUER, OID4VP_VERIFIER, or OAUTH2_AUTHORIZATION_SERVER. The body sets where the named instanceId is advertised: a verified custom host, or a pathPrefix under the platform subdomain, plus the wellKnownPath. By assigning a distinct host or pathPrefix per instance, issuer A and issuer B of the same tenant are reachable at separate, stable URLs. List a tenant's bindings with GET /tenants/{tenantId}/public-endpoints. The schemas are in the Platform Admin API reference.
Platform Container Deployment
VDX is delivered as a platform container deployment on top of the EDK service set. The platform tenant is initialized the same way on every deployment target, Kubernetes, Docker Compose, cloud, or on-premise, from application.tenant.* configuration or the POST /api/platform-admin/v1/application/tenant/bootstrap call described above. The platform tenant always has a hosted IdP and is the home of the /api/platform-admin/v1 API.
In Kubernetes, each service pod can receive common license material through the shared Helm chart volume hooks. The chart exposes generic extra pod volumes and container volume mounts; the environment overlays use those hooks to attach the license Secret and trust ConfigMap as one read-only directory. This keeps secret contents out of Git and keeps license material provisioning in the operator's secret management flow.
See Operations & Management for the full deployment model across Kubernetes, Docker Compose, cloud native, and on-premise.
Operator Surfaces
VDX adds white-labeled per-tenant portals and dashboards over this flow. Operators drive license activation, secret-backend selection, tenant creation, and instance provisioning through the platform admin surfaces and the management dashboards:
- Admin Console, platform administration, tenant management, policy configuration, and onboarding status, over
/api/platform-admin/v1. - Issuer Management and Verifier Management, provision and operate the per-tenant issuer and verifier instances over
/api/services/v1.
Each tenant's portals and dashboards are branded as the tenant's own product, while the underlying admin APIs and instance manager are shared. See Operations & Management for portals, dashboards, and branding.