Trust domains
A trust domain is a named, tenant-owned set of public cryptographic evidence: root and intermediate CAs, DIDs, JWKs, OpenID Federation entities, issuer URIs, DNS names. On its own a domain does nothing. It becomes enforcement only when something attaches it for a specific usage.
For the complete credential-format, status, KMS, and verification sequence, start with Credentials, status, and trust. For the ISO mdoc-specific VICAL and CWT boundary, use mDoc VICAL and CWT status.
That separation is the whole model. A domain says what evidence exists. An attachment says which resource uses that evidence, and for what question. The same domain can be the issuer allow-list for one verifier and the wallet-provider allow-list for an issuer, without either resource knowing about the other.
Trust domains are not credential designs and not status lists. Designs describe what a credential contains. Status lists say whether a credential was revoked. Trust domains answer who was allowed to sign it.
Audience: tenant admin, or platform operator acting in a tenant context.
Prerequisites: Onboard a tenant. Anchors reference identifiers you already manage under Keys and DID.
Reference: Domains, Policy.
API: Trust domain, base path /api/trust-domain/v1.
The three things you configure
Every trust decision in the product is assembled from three records. Getting a presentation to succeed means all three are right, and a failure is almost always one of them being absent.
Domains and anchors. The evidence. An anchor carries one identifier (a certificate, a DID, a federation entity) plus, separately, the admission classes it is allowed to answer for. An X.509 root admitted as CREDENTIAL_ISSUER cannot silently become a TLS server root because someone attached the domain somewhere else.
Attachments. The binding from a consuming resource to an ordered list of domains, for one usage. The key is the triple (consumerKind, consumerId, usage). There is exactly one attachment per triple.
Eligibility grants. A tenant-level cap on which domains a given consumer kind is permitted to select for a usage. This is governance, not selection. It exists so that delegating verifier editing to a team does not also delegate the choice of which trust roots that team may point at.
Usages
A usage is the question being asked. Each one resolves independently, so a verifier can be strict about issuers and say nothing at all about wallet providers.
| Usage | Question | Anchor admission class required |
|---|---|---|
CREDENTIAL_ISSUER_TRUST | May this issuer have issued the credential being presented? | CREDENTIAL_ISSUER |
WALLET_PROVIDER_ESTABLISHMENT | Is this wallet from a wallet provider we accept? | WALLET_PROVIDER |
VERIFIER_TRUST | Is this relying party one we release attributes to? | VERIFIER |
AUTHORIZATION_SERVER_TRUST | Did this token, ID token, JARM response or logout come from an authorization server we admit? | AUTHORIZATION_SERVER_SIGNER |
TLS_SERVER | Should this connector complete a TLS handshake with this host? | TLS_SERVER_CA |
CATALOG_AUTHORIZATION | Is this credential type authorized for this issuer by a catalog? | none, catalog snapshots carry the authority |
Two admission classes exist for artifacts rather than for a usage of their own. CATALOG_SIGNER
verifies a catalog snapshot, and MDOC_VICAL_SIGNER verifies an ISO 18013-5 VICAL. Both admit the
signature over a list; the entries inside the list are then admitted separately, normally as
CREDENTIAL_ISSUER.
The admission class column is the part people miss. An anchor participates in a decision only when it holds the admission class that the usage requires. Adding a certificate to a domain is not enough.
Consumer kinds and the resolution cascade
An attachment is looked up against a fixed chain that starts at the exact resource and ends at the tenant. Resolution takes the first level that has an attachment row for that usage. It does not merge levels, and it does not continue past a match.
| Consumer kind | Lookup order |
|---|---|
TENANT | tenant |
OID4VCI_ISSUER | issuer, tenant |
OID4VCI_ISSUANCE_TEMPLATE | template, issuer, tenant |
OID4VCI_CREDENTIAL_CONFIGURATION | configuration, template, issuer, tenant |
OID4VP_VERIFIER | verifier, tenant |
OID4VP_DCQL_QUERY | query, verifier, tenant |
OID4VP_VERIFIER_DCQL_BINDING | binding, query, verifier, tenant |
OID4VP_REQUEST_TEMPLATE | template, binding, query, verifier, tenant |
CONNECTOR_HTTP_ENDPOINT | endpoint, tenant |
AS | authorization server, tenant |
Two consequences worth designing around:
An attachment at a specific level replaces the tenant default rather than adding to it. If you attach one narrow domain to a DCQL query, the query stops seeing the tenant's domains entirely. Add the tenant's domain to that attachment's list if you meant to extend.
An attachment with an empty domain list under FAIL_CLOSED is a valid, meaningful configuration: it trusts nothing at that level and stops the cascade there. That is how you disable inherited trust for one query without touching the tenant.
Policy modes
Identity usages carry an IdentityAdmissionPolicy with one of two modes.
FAIL_CLOSED means only the domains listed on the attachment admit an identity. This is the default and the only mode a new tenant starts with.
UNRESTRICTED means identity admission is not limited to named domains. It is always explicit. It is never inferred from an absent attachment or an empty list. It does not weaken anything else: signatures, revocation status, holder binding, hostname verification and egress policy all still run.
TLS_SERVER has no mode. It carries a TlsServerPolicy with expectedDnsNames and optional requiredExtendedKeyUsages, and it is always fail closed. There is no unrestricted TLS.
Walking one decision through
A verifier verifier-age-check runs a DCQL query query-over18 and receives a presentation. To decide whether the issuer is acceptable, the runtime does this:
- Build the consumer chain for
OID4VP_DCQL_QUERY:query-over18, thenverifier-age-check, then the tenant. - Look for an attachment on
(OID4VP_DCQL_QUERY, query-over18, CREDENTIAL_ISSUER_TRUST). Not found. - Look for
(OID4VP_VERIFIER, verifier-age-check, CREDENTIAL_ISSUER_TRUST). Found, listingtrust-domain-nl-pidat order 0 andtrust-domain-eu-walletat order 1. Stop here. - The winner is not the tenant, so check the eligibility grant for
(OID4VP_VERIFIER, CREDENTIAL_ISSUER_TRUST). Both domains are in it, so the selection stands. A domain outside the grant is a configuration error, not a silent drop. - For each domain in order, require the domain to be
ACTIVEand inside its validity window, then collect anchors that are active, inside their own validity window, and hold theCREDENTIAL_ISSUERadmission class. - Collect any active ETSI trust source snapshots on those domains and add their derived entries.
- Evaluate the credential's issuer identity against that assembled evidence.
Steps 3, 4, 5 and 6 can each produce an empty result, and each empty result fails the presentation for a different reason. The decision response tells you which one.
Domains inventory
The Domains area lists trust domains by display name, lifecycle status (Draft, Active, Disabled), anchor count, validity window and last update.
Navigation: Resources > Trust domains > Domains
Deep link: #resource=trustDomains&rarea=domains
An empty list is expected on a new tenant. Nothing is trusted until an operator opts in.
- Admin Console
- REST API

- Overview
- Request
- Response
List trust domains
Endpoint: GET /api/trust-domain/v1/domains
Captured response: 200 OK
This captured endpoint is shown from the E2E run; it is not mapped to one of the generated EDK REST API reference pages.
Connect an environment to rewrite this call to real service bases and run it.
Create a trust domain
New domains start as Draft so you can add anchors and admissions before anything resolves against them. Attaching a draft domain is allowed; resolving against one fails, because resolution requires ACTIVE.
- Admin Console
- REST API
New trust domain takes a name and description, then returns to the list.

- Overview
- Request
- Response
Create trust domain
Endpoint: POST /api/trust-domain/v1/domains
Captured response: 201 Created
This captured endpoint is shown from the E2E run; it is not mapped to one of the generated EDK REST API reference pages.
Connect an environment to rewrite this call to real service bases and run it.
Domain overview and lifecycle
Overview holds display name, description and an optional validity window. Leave validity empty for a domain that is always in range.
- Activate moves Draft to Active so resolution can use it.
- Disable stops an Active domain from resolving without deleting its anchors or the attachments that point at it. Resolution against a disabled domain fails closed.
- Delete removes the domain, its anchors and its admissions. Check the consumers list first: deleting a domain that attachments still reference leaves those attachments unresolvable.
Every mutation is optimistically concurrent. version increments on write, the current strong ETag comes back on GET, and a mutating request must carry it in If-Match. A missing header returns 428, a stale one returns 412.
- Admin Console
- REST API

Get:
- Overview
- Request
- Response
Get trust domain
Endpoint: GET /api/trust-domain/v1/domains/trust-domain-eu-wallet
Captured response: 200 OK
This captured endpoint is shown from the E2E run; it is not mapped to one of the generated EDK REST API reference pages.
Connect an environment to rewrite this call to real service bases and run it.
Activate by writing status: "ACTIVE":
- Overview
- Request
- Response
Activate trust domain
Endpoint: PUT /api/trust-domain/v1/domains/trust-domain-partner-dids
Captured response: 200 OK
This captured endpoint is shown from the E2E run; it is not mapped to one of the generated EDK REST API reference pages.
Connect an environment to rewrite this call to real service bases and run it.
Anchors
An anchor is one piece of public evidence inside a domain. It has an evidence mechanism describing what kind of material it is (X509, DID, JWK, OIDFED_ENTITY, ISSUER_URI, DNS_NAME) and an origin telling you where it came from: IMPORTED for material an operator supplied, TENANT_PUBLIC for a public resource this tenant itself produced.
An anchor never carries a private key. It references a public identifier. Signing selectors live on the issuer, catalog or LoTE settings that need them, and trust evaluation never resolves a private key. If a design asks you to put a KMS key reference on an anchor, the design is wrong.
X.509 anchors may carry an x509Policy constraining permitted DNS names, permitted URI origins, required extended key usages and required certificate policy OIDs. Those constraints are applied during path validation, not at admission time.
Entries derived from an ETSI trust source snapshot are not anchors. They stay inside their immutable source snapshot and are added to the evidence set at resolution time. They are never copied into the anchor table, so refreshing a source cannot leave stale rows behind.
- Admin Console
- REST API
The Anchors tab lists mechanism, masked display value, admission classes and status. Add opens the anchor wizard.

Each list entry pairs the stored anchor with a resolved, enforcement-safe identifier summary. The summary is what the console renders and what the QR and PEM export actions use; it carries a masked display value rather than the full identifier.
- Overview
- Request
- Response
List trust anchors
Endpoint: GET /api/trust-domain/v1/domains/trust-domain-eu-wallet/anchors
Captured response: 200 OK
This captured endpoint is shown from the E2E run; it is not mapped to one of the generated EDK REST API reference pages.
Connect an environment to rewrite this call to real service bases and run it.
Admissions
Admission is a separate sub-resource because it is a separate decision. Adding an anchor puts evidence in the domain. Granting an admission class says which question that evidence is allowed to answer.
GET /api/trust-domain/v1/domains/{domainId}/anchors/{anchorId}/admissions
PUT /api/trust-domain/v1/domains/{domainId}/anchors/{anchorId}/admissions/{admissionClass}
DELETE /api/trust-domain/v1/domains/{domainId}/anchors/{anchorId}/admissions/{admissionClass}
PUT takes the domain id and the anchor's current version as expectedAnchorVersion, so an admission grant cannot race an anchor edit.
An admission may carry constraints. A requiredIssuerUriOrigin constraint pins the anchor to issuers under one origin. Constrained paths are evaluated against asserted request material, which is not part of the public evaluation contract, so an anchor with that constraint is deliberately excluded from the generic resolve response and stays fail closed there.
- Overview
- Request
- Response
List the admission classes granted to an anchor
Endpoint: GET /api/trust-domain/v1/domains/trust-domain-eu-wallet/anchors/anchor-nl-pid-root/admissions
Captured response: 200 OK
This captured endpoint is shown from the E2E run; it is not mapped to one of the generated EDK REST API reference pages.
Connect an environment to rewrite this call to real service bases and run it.
Attachments
An attachment is addressed by its triple, so there is no separate create step and no attachment id to track in your client.
GET /api/trust-domain/v1/attachments
GET /api/trust-domain/v1/attachments/{consumerKind}/{consumerId}/{usage}
PUT /api/trust-domain/v1/attachments/{consumerKind}/{consumerId}/{usage}
DELETE /api/trust-domain/v1/attachments/{consumerKind}/{consumerId}/{usage}
PUT replaces the whole aggregate: the policy and the complete ordered domain list. There is no partial update and no per-domain endpoint, which keeps ordering unambiguous. Use If-Match with the attachment ETag. On a first write, If-Match: * is the correct header.
DELETE removes the attachment and therefore re-opens the cascade: the consumer falls through to the next level. Deleting is not the same as writing an empty FAIL_CLOSED list, which stops the cascade and trusts nothing.
The tenant's own configuration is just an attachment on (TENANT, <tenantId>, <usage>). There is no special tenant policy record. A brand-new tenant has no tenant attachment at all, which is why it fails closed on every usage until an operator writes one.
- Admin Console
- REST API
Attachments are edited where the resource lives, not in a central list. A verifier's Trust section, an issuer's Trust section and the tenant Policy area all render the same editor against a different triple.

List every attachment in the tenant:
- Overview
- Request
- Response
List trust attachments
Endpoint: GET /api/trust-domain/v1/attachments
Captured response: 200 OK
This captured endpoint is shown from the E2E run; it is not mapped to one of the generated EDK REST API reference pages.
Connect an environment to rewrite this call to real service bases and run it.
Read one:
- Overview
- Request
- Response
Get the trust attachment for one consumer and usage
Endpoint: GET /api/trust-domain/v1/attachments/OID4VP_VERIFIER/verifier-age-check/CREDENTIAL_ISSUER_TRUST
Captured response: 200 OK
This captured endpoint is shown from the E2E run; it is not mapped to one of the generated EDK REST API reference pages.
Connect an environment to rewrite this call to real service bases and run it.
Replace the selection:
- Overview
- Request
- Response
Replace the domain selection on an attachment
Endpoint: PUT /api/trust-domain/v1/attachments/OID4VP_VERIFIER/verifier-age-check/CREDENTIAL_ISSUER_TRUST
Captured response: 200 OK
This captured endpoint is shown from the E2E run; it is not mapped to one of the generated EDK REST API reference pages.
Connect an environment to rewrite this call to real service bases and run it.
Eligibility grants
A grant caps which domains a consumer kind may select for a usage. It is checked against the winning attachment, and only when that attachment is not the tenant's own. The tenant is the authority, so the tenant is not capped by itself.
GET /api/trust-domain/v1/eligibility/{consumerKind}/{usage}
PUT /api/trust-domain/v1/eligibility/{consumerKind}/{usage}
DELETE /api/trust-domain/v1/eligibility/{consumerKind}/{usage}
When no grant exists for a consumer kind and usage, a non-tenant attachment that selects any domain is rejected. Write the grant before delegating resource editing, not after. The failure is a configuration error with a clear message, not a silent narrowing of the domain list.
- Overview
- Request
- Response
List the eligibility grant for a consumer kind and usage
Endpoint: GET /api/trust-domain/v1/eligibility/OID4VP_VERIFIER/CREDENTIAL_ISSUER_TRUST
Captured response: 200 OK
This captured endpoint is shown from the E2E run; it is not mapped to one of the generated EDK REST API reference pages.
Connect an environment to rewrite this call to real service bases and run it.
Who uses this domain
Before you disable or delete a domain, ask it what depends on it.
- Overview
- Request
- Response
List everything that currently selects a domain
Endpoint: GET /api/trust-domain/v1/domains/trust-domain-eu-wallet/consumers
Captured response: 200 OK
This captured endpoint is shown from the E2E run; it is not mapped to one of the generated EDK REST API reference pages.
Connect an environment to rewrite this call to real service bases and run it.
The response lists every attachment that currently selects the domain, across every consumer kind and usage. There is no reverse pointer stored on the domain itself; this is a query over attachments.
Catalogs
A catalog authorizes credential types for an issuer, and it runs after issuer trust has already succeeded. Issuer trust says the signer is admissible. The catalog says this admissible signer is authorized for this credential type.
Catalogs are plural and domain-owned. There is no central registry. A non-qualified use case may keep a tenant-local, unpublished catalog. A qualified use case requires catalog authorization; a non-qualified one makes it opt in through the CATALOG_AUTHORIZATION usage.
GET /api/trust-domain/v1/domains/{domainId}/catalogs
PUT /api/trust-domain/v1/domains/{domainId}/catalogs/{catalogId}
GET /api/trust-domain/v1/domains/{domainId}/catalogs/{catalogId}/snapshots
POST /api/trust-domain/v1/domains/{domainId}/catalogs/{catalogId}/candidates/validate
POST /api/trust-domain/v1/domains/{domainId}/catalogs/{catalogId}/snapshots/{snapshotId}/activate
Catalogs follow the same candidate, validate, activate discipline as trust sources. Editing a catalog produces a candidate snapshot. Only an explicitly activated snapshot is authoritative, and runtime reads the active snapshot rather than live catalog state.
ETSI trust sources
ETSI trust sources are managed per trust domain. TS 119 612 EU and custom LoTL sources authorize QEAA providers only. TS 119 602 LoTE resources authorize exactly PID Provider, Wallet Provider, PuB-EAA Provider, Access CA and Registration Certificate Provider roles. There is no cross-profile or cross-territory fallback. Revocation-only services do not establish issuer trust.
The EU LoTL is a product-owned singleton. Its URL, XML format, scheme identity and signer or pivot bootstrap are immutable. The Admin Console can change only its enabled flag. Custom sources take an HTTPS URL, format, scheme identity, signer anchor references and a bounded egress policy. A changed configuration creates a candidate revision instead of replacing the active one.
The platform validates and refreshes sources. Runtime services consume the local immutable snapshot and never fetch external LoTL or national Trusted List URLs during a request. A refresh failure preserves the previous snapshot only until its signed NextUpdate. After that the source is EXPIRED and supplies no usable trust.
In the Admin Console, open a domain and use QEAA trusted lists for the fixed EU singleton or a custom TS 119 612 source. The fixed EU flow never asks for a URL. The bulk action applies that same product-owned source to each selected domain and reports a separate outcome per domain. The source view shows its revision, active snapshot, derived QEAA entries, signed freshness, refresh history and safe diagnostics.
The REST lifecycle is:
GET /api/trust-domain/v1/domains/{domainId}/trust-sources
PUT /api/trust-domain/v1/domains/{domainId}/trust-sources/eu
GET /api/trust-domain/v1/domains/{domainId}/trust-sources/{sourceId}/revisions
POST /api/trust-domain/v1/domains/{domainId}/trust-sources/{sourceId}/revisions/{revision}/validate
POST /api/trust-domain/v1/domains/{domainId}/trust-sources/{sourceId}/revisions/{revision}/activate
POST /api/trust-domain/v1/domains/{domainId}/trust-sources/{sourceId}/refresh
GET /api/trust-domain/v1/domains/{domainId}/trust-sources/{sourceId}/snapshot
GET /api/trust-domain/v1/domains/{domainId}/trust-sources/{sourceId}/derived-entries
GET /api/trust-domain/v1/domains/{domainId}/trust-sources/{sourceId}/refresh-attempts
GET /api/trust-domain/v1/domains/{domainId}/trust-sources/{sourceId}/diagnostics
List responses use the items envelope. Source management and activation are separate permissions. The EU, custom, validate, activate and disable mutations require the current strong ETag in If-Match.
Custom LoTL request
Use the externally hosted test LoTL URL and pinned signer references supplied by the accepted deployment configuration. Do not enter a private key, a secret or an unapproved URL.
PUT /api/trust-domain/v1/domains/trust-domain-eu-wallet/trust-sources/webuild-test-lotl
If-Match: "source:webuild-test-lotl:1"
Content-Type: application/json
{
"url": "https://<approved-we-build-host>/<path-to-test-lotl>",
"format": "application/xml",
"schemeIdentity": "<accepted-we-build-scheme-identity>",
"signerAnchorIds": ["<accepted-we-build-signer-anchor-id>"],
"egressPolicy": {
"allowedHosts": ["<approved-we-build-host>"],
"maxArtifactBytes": 10485760
},
"enabled": true
}
The source is not trusted after this request alone. Validate the candidate, then activate the exact validated revision. The immutable snapshot carries its sequence, digest, signed issue time, signed NextUpdate and normalized QEAA entries.
External TS 119 602 LoTE sources
Use the domain's Provider lists tab to consume an existing external LoTE. Supply an HTTPS URL, one of the five supported profiles, pinned signer-anchor references and a bounded allowed-host policy. The source follows the same candidate, validate, activate discipline as a custom LoTL.
GET /api/trust-domain/v1/domains/{domainId}/lote-sources
PUT /api/trust-domain/v1/domains/{domainId}/lote-sources/{sourceId}
DELETE /api/trust-domain/v1/domains/{domainId}/lote-sources/{sourceId}
GET /api/trust-domain/v1/domains/{domainId}/lote-sources/{sourceId}/revisions
POST /api/trust-domain/v1/domains/{domainId}/lote-sources/{sourceId}/revisions/{revision}/validate
POST /api/trust-domain/v1/domains/{domainId}/lote-sources/{sourceId}/revisions/{revision}/activate
POST /api/trust-domain/v1/domains/{domainId}/lote-sources/{sourceId}/refresh
GET /api/trust-domain/v1/domains/{domainId}/lote-sources/{sourceId}/diagnostics
Diagnostics expose operational state, attempts and active snapshot metadata such as provider count and signed freshness. They do not expose artifact bytes, normalized providers, source configuration, signer material or anchors. A failed refresh preserves the last-known-good snapshot only through its signed nextUpdate; an expired snapshot supplies no trust.
ETSI trust lists and mdoc VICAL are different inputs
Do not use the terms interchangeably in an integration design:
| Artifact | Encoding and purpose | Evaluation question |
|---|---|---|
| ETSI TS 119 602 trust list | Signed JSON/JAdES list of trusted entities and roles | Can the tenant validate, activate, refresh, publish and consume the correct trusted-entity snapshot for verification? |
| ISO 18013-5 VICAL | mdoc-specific signed CBOR/COSE trust artifact for document signers | Does the deployed verifier have an explicit VICAL producer, consumer, parser, cache and trust-anchor path? |
The existence of TS 119 602 LoTE routes, an mso_mdoc credential configuration or a DSC certificate does not prove VICAL support. The two are configured separately and evaluated separately: trust sources on the domain, VICAL on an anchor. A tenant can have a fully working ETSI trust list and no VICAL, or the reverse. See the AAMVA Mobile DL Implementation Guidelines for the mdoc trust vocabulary and ETSI TS 119 602 for the trusted-entity-list model.
mdoc VICAL
A VICAL is configured on the anchor that represents the VICAL provider, not as a trust source:
GET /api/trust-domain/v1/domains/{domainId}/anchors/{anchorId}/mdoc-vical
PUT /api/trust-domain/v1/domains/{domainId}/anchors/{anchorId}/mdoc-vical
DELETE /api/trust-domain/v1/domains/{domainId}/anchors/{anchorId}/mdoc-vical
There is no candidate, validate, activate cycle here. A VICAL has no revision model of its own, so
the safety comes from the anchors it names instead. The body carries an absolute HTTPS URL, the
signerAnchorIds that verify the artifact signature, the issuerAnchorIds it is allowed to carry,
and optional requiredCertificateProfiles.
Every referenced anchor must already exist in the same domain, be ACTIVE, and hold the admission
class for its own job: MDOC_VICAL_SIGNER for the signers, CREDENTIAL_ISSUER for the issuing
authorities. At least one signer is required, because an unverified VICAL would admit every
authority it happens to contain. That is the whole reason the signer gets a separate admission
class: a certificate that is merely present in a domain must not be able to vouch for a whole list.
Reading the VICAL of an anchor that has none returns an unconfigured VICAL rather than 404, so a
client can render the empty form without a special case. Removing a VICAL leaves the anchor and its
admission in place; only the source goes away.
Hosted TS 119 602 LoTE resources
Hosted LoTE authoring is a publication capability, not a trust-establishment control. An LoTE resource has one editable draft and immutable published versions. The supported profiles are PID_PROVIDER, WALLET_PROVIDER, PUB_EAA_PROVIDER, ACCESS_CA and REGISTRATION_CERTIFICATE_PROVIDER. QEAA is never an LoTE profile. The registration certificate profile uses the WRPRC provider type and issuance service, not the EU registrars or Register service type.
Draft and provider changes use the draft ETag. Provider identity is (country, providerId), so the same provider identifier can exist in more than one country. Validation requires the profile-specific issuance service type, the exact NOTIFIED service status, country-qualified identity, certificate data and signed validity rules.
Publication signs the exact JSON bytes with a non-exportable KMS key reference, independently verifies the JAdES artifact and publishes atomically through the EDK_HOSTED, AWS_S3 or AZURE_BLOB adapters. Use opaque secret references for external publication. The sequence increments only after publication succeeds. Published versions retain exact payload bytes, signed artifact bytes, publication receipt, signer reference and nextUpdate, and cannot be edited.
GET /api/trust-domain/v1/domains/{domainId}/lotes/{loteId}/v1/providers
POST /api/trust-domain/v1/domains/{domainId}/lotes/{loteId}/v1/{country}/providers
GET /api/trust-domain/v1/domains/{domainId}/lotes/{loteId}/v1/{country}/providers/{providerId}
PUT /api/trust-domain/v1/domains/{domainId}/lotes/{loteId}/v1/{country}/providers/{providerId}
DELETE /api/trust-domain/v1/domains/{domainId}/lotes/{loteId}/v1/{country}/providers/{providerId}
The provider list response uses { "providers": [...] }. Provider mutations require If-Match. Runtime checks consume the domain's active published LoTE snapshot and fail closed for an absent, ambiguous, mismatched, withdrawn or expired publication.
Connector TLS trust
Outbound connector endpoints select TLS trust the same way everything else does: an attachment on (CONNECTOR_HTTP_ENDPOINT, <endpointId>, TLS_SERVER), cascading to the tenant.
Public WebPKI is explicit tenant policy backed by a pinned product bundle. It is not an ambient fallback to the JVM trust store. If you want an endpoint to trust the public web, say so with a domain that carries the public root material. An endpoint with no TLS_SERVER attachment anywhere in its chain does not complete a handshake.
Client keys stay in KMS and are unrelated to this. Trust resolution returns public CA material and digests only.
The internal resolution surface
Runtime services do not read domains, anchors and attachments directly. They call a small set of evaluation routes, which is why a misconfiguration surfaces as one clear decision rather than as scattered lookup failures.
POST /api/trust-domain/v1/internal/resolve/attachment
POST /api/trust-domain/v1/internal/resolve/authentication-material
POST /api/trust-domain/v1/internal/resolve/tls-server-material
POST /api/trust-domain/v1/internal/evaluate/credential-issuer
POST /api/trust-domain/v1/internal/evaluate/wallet-provider
POST /api/trust-domain/v1/internal/evaluate/verifier
POST /api/trust-domain/v1/internal/evaluate/catalog-authorization
These are east-west routes between platform services and are not part of the customer-facing contract. They are documented here because reading a decision response is the fastest way to understand why a presentation failed. The response carries the domain revisions that participated, the evidence mechanisms that were available and diagnostics such as trust.anchor.token-unavailable. It does not carry raw identifier material.
Use resolve/attachment from automation when you want to preview which level of the cascade would win for a consumer before you change it.
Recommended order of work
- Create draft domains, one per issuer ecosystem you care about.
- Add anchors, then grant each anchor the admission classes it should answer for.
- Activate the domains that are ready.
- Write the tenant attachment for each usage you enforce. Until you do, that usage fails closed.
- Write eligibility grants for every consumer kind you intend to delegate.
- Attach domains on specific verifiers, issuers, queries, authorization servers and connector endpoints where they need to differ from the tenant.
- Preview with
resolve/attachmentbefore cutting production traffic over.
Coming from the earlier binding model
If you are updating an integration written against the previous release, the mapping is:
| Previously | Now |
|---|---|
/bindings with purpose and target type | An attachment on the matching (consumerKind, consumerId, usage) |
/oid4vp/trust-domains and the per-verifier, per-query and per-template selection routes | The same attachments, addressed by consumer kind |
Tenant allowed list | An eligibility grant per consumer kind and usage |
Tenant defaults | The TENANT attachment for that usage |
issuerTrustMode: TRUST_DOMAINS and UNRESTRICTED | policy.mode: FAIL_CLOSED and UNRESTRICTED |
Anchor trustRole and purposes | Admission classes on the anchor sub-resource |
There is no compatibility route, no dual read and no fallback. The database migration translates every unambiguous record and blocks on ambiguity rather than guessing, so an upgrade that reports unresolved authority ambiguity is telling you a real decision was never expressible in the old model.