NXD trust list - add and manage entries as an OAuth2 client
When to use
Use this skill when the task is one of these:
- Register a certificate in an NXD trust list (for example a Wallet-Relying Party Access Certificate, WRPAC, so wallets trust a verifier).
- Update or remove a trust-list entry from automation, CI, or an agent.
- Wire a backend (for example the iGrant.io OWS) to the trust list so a certificate upload can be followed by a trust-list registration.
Prerequisites
- A client id and client secret for the trust-list backend. A
backoffice admin creates them on the OAuth2 Clients page of the
dashboard (
/backoffice). The secret is shown exactly once. - The base URL of the deployment, for example
https://trustlist.nxd.foundation.
A client holds one or more scopes, and the scopes decide what a write does:
| Scope | PUT (upsert) | DELETE | Notes |
|---|---|---|---|
read |
refused (403) | refused (403) | list participants and trust lists only |
write |
applies directly, lists re-sign | queued for approval | for high-trust automation |
write:review |
queued for approval | queued for approval | every action waits for a super admin |
When a client holds both write and write:review, write wins: upserts
apply directly. Deletes queue for approval whatever the scopes.
A queued action answers 202 with
{"status": "queued for approval", "approvalId": "<id>"} and appears on the
dashboard's Approvals page. Only a super admin resolves it; the client
cannot poll the approval, so treat a 202 as "submitted, a human decides".
Every client action is recorded in the activity log as client:<name>.
1. Get a token
POST {base}/auth/token, body application/x-www-form-urlencoded:
grant_type=client_credentials&client_id=<id>&client_secret=<secret>
HTTP Basic (Authorization: Basic base64(id:secret)) also works. The answer:
{ "access_token": "<jwt>", "token_type": "Bearer", "expires_in": 3600 }
Send it as Authorization: Bearer <jwt>. Disabling the client or changing its
scopes takes effect on the next request, not at token expiry. Errors follow
RFC 6749: {"error": "invalid_client", "error_description": "..."}.
2. Add or update an entry
PUT {base}/admin/participants with a participant record. Common rules:
participant_id:^[a-z0-9][a-z0-9-]{1,63}$. The upsert is idempotent on this id; add?mode=createto refuse (409) an id that is already taken.name,address(street, locality, country as 2-letter code;postal_codeoptional), at least oneelectronic_addressesentry, aninformation_uri, and at least oneservicesentry are required.trade_nameis optional.- One participant may carry several services across several lists.
service_typeis optional; when omitted, the list's first (issuance) type from the tables below is used.statusisgranted(default) orwithdrawn.status_starting_timeandservice_supply_pointsare optional.- Answers:
200 {"status": "participant saved and lists re-signed"}for a direct write, or the202approval answer. An expired certificate is accepted but flagged as a warning in the dashboard; a syntactically invalid record is a400.
Primary (per-type) lists
These lists require digital_ids.x509_certificates: base64 DER (the same
encoding as a JWS x5c element), leaf certificate. Every other digital-id
kind is discarded on these lists.
list key |
Documents | Allowed service_type URIs (first is the default) |
|---|---|---|
qeaa |
NXD-TL-QEAA.xml (TS 119 612 TSL) |
http://uri.etsi.org/TrstSvc/Svctype/EAA/Q |
eaa |
NXD-TL-EAA.xml, nxd-eaa-providers-lote.json |
http://uri.etsi.org/19602/SvcType/PubEAA/Issuance, http://uri.etsi.org/19602/SvcType/PubEAA/Revocation |
pub-eaa |
NXD-TL-PubEAA.xml, nxd-pub-eaa-providers-lote.json |
http://uri.etsi.org/19602/SvcType/PubEAA/Issuance, http://uri.etsi.org/19602/SvcType/PubEAA/Revocation |
pid |
NXD-TL-PID.xml, nxd-pid-providers-lote.json |
http://uri.etsi.org/19602/SvcType/PID/Issuance, http://uri.etsi.org/19602/SvcType/PID/Revocation |
wallet |
NXD-TL-WalletProviders.xml, nxd-wallet-providers-lote.json |
http://uri.etsi.org/19602/SvcType/WalletSolution/Issuance, http://uri.etsi.org/19602/SvcType/WalletSolution/Revocation |
wrpac |
NXD-TL-WRPAC.xml, nxd-wrpac-providers-lote.json |
http://uri.etsi.org/19602/SvcType/WRPAC/Issuance, http://uri.etsi.org/19602/SvcType/WRPAC/Revocation |
wrprc |
NXD-TL-WRPRC.xml, nxd-wrprc-providers-lote.json |
http://uri.etsi.org/19602/SvcType/WRPRC/Issuance, http://uri.etsi.org/19602/SvcType/WRPRC/Revocation |
registrars |
NXD-TL-Registrars.xml, nxd-registrars-and-registers-lote.json |
http://uri.etsi.org/19602/SvcType/Register |
Exact request bodies, one per list. Replace the organisation fields and the
certificate; keep the list and service_type values exactly as shown.
Where a list has a Revocation type, use it instead of Issuance when the
service publishes validity status rather than issuing.
qeaa - Qualified EAA provider (TSL):
{
"participant_id": "acme-qeaa",
"name": "Acme Trust Services AB",
"address": { "street": "Main Street 1", "locality": "Stockholm", "postal_code": "111 11", "country": "SE" },
"electronic_addresses": ["mailto:ops@acme.example"],
"information_uri": "https://acme.example",
"services": [
{
"list": "qeaa",
"name": "Acme QEAA Issuance",
"service_type": "http://uri.etsi.org/TrstSvc/Svctype/EAA/Q",
"digital_ids": { "x509_certificates": ["<base64 DER leaf certificate>"] }
}
]
}
eaa - EAA provider:
{
"participant_id": "acme-eaa",
"name": "Acme Attestations AB",
"address": { "street": "Main Street 1", "locality": "Stockholm", "postal_code": "111 11", "country": "SE" },
"electronic_addresses": ["mailto:ops@acme.example"],
"information_uri": "https://acme.example",
"services": [
{
"list": "eaa",
"name": "Acme EAA Issuance",
"service_type": "http://uri.etsi.org/19602/SvcType/PubEAA/Issuance",
"digital_ids": { "x509_certificates": ["<base64 DER leaf certificate>"] }
}
]
}
pub-eaa - Pub-EAA provider:
{
"participant_id": "acme-pub-eaa",
"name": "Acme Public Attestations AB",
"address": { "street": "Main Street 1", "locality": "Stockholm", "postal_code": "111 11", "country": "SE" },
"electronic_addresses": ["mailto:ops@acme.example"],
"information_uri": "https://acme.example",
"services": [
{
"list": "pub-eaa",
"name": "Acme Pub-EAA Issuance",
"service_type": "http://uri.etsi.org/19602/SvcType/PubEAA/Issuance",
"digital_ids": { "x509_certificates": ["<base64 DER leaf certificate>"] }
}
]
}
pid - PID provider:
{
"participant_id": "acme-pid",
"name": "Acme Identity AB",
"address": { "street": "Main Street 1", "locality": "Stockholm", "postal_code": "111 11", "country": "SE" },
"electronic_addresses": ["mailto:ops@acme.example"],
"information_uri": "https://acme.example",
"services": [
{
"list": "pid",
"name": "Acme PID Issuance",
"service_type": "http://uri.etsi.org/19602/SvcType/PID/Issuance",
"digital_ids": { "x509_certificates": ["<base64 DER leaf certificate>"] }
}
]
}
wallet - Wallet provider:
{
"participant_id": "acme-wallet",
"name": "Acme Wallet AB",
"address": { "street": "Main Street 1", "locality": "Stockholm", "postal_code": "111 11", "country": "SE" },
"electronic_addresses": ["mailto:ops@acme.example"],
"information_uri": "https://acme.example",
"services": [
{
"list": "wallet",
"name": "Acme Wallet Solution",
"service_type": "http://uri.etsi.org/19602/SvcType/WalletSolution/Issuance",
"digital_ids": { "x509_certificates": ["<base64 DER leaf certificate>"] }
}
]
}
wrpac - Wallet-Relying Party Access Certificate provider (verifiers):
{
"participant_id": "acme-shop",
"name": "Acme Shop AB",
"address": { "street": "Main Street 1", "locality": "Stockholm", "postal_code": "111 11", "country": "SE" },
"electronic_addresses": ["mailto:ops@acme.example"],
"information_uri": "https://shop.acme.example",
"services": [
{
"list": "wrpac",
"name": "Acme Shop (Relying Party)",
"service_type": "http://uri.etsi.org/19602/SvcType/WRPAC/Issuance",
"digital_ids": { "x509_certificates": ["<base64 DER leaf certificate>"] }
}
]
}
wrprc - Wallet-Relying Party Registration Certificate provider:
{
"participant_id": "acme-wrprc",
"name": "Acme Registration Services AB",
"address": { "street": "Main Street 1", "locality": "Stockholm", "postal_code": "111 11", "country": "SE" },
"electronic_addresses": ["mailto:ops@acme.example"],
"information_uri": "https://acme.example",
"services": [
{
"list": "wrprc",
"name": "Acme WRPRC Issuance",
"service_type": "http://uri.etsi.org/19602/SvcType/WRPRC/Issuance",
"digital_ids": { "x509_certificates": ["<base64 DER leaf certificate>"] }
}
]
}
registrars - Registrars and registers:
{
"participant_id": "acme-registry",
"name": "Acme Business Registry",
"address": { "street": "Main Street 1", "locality": "Stockholm", "postal_code": "111 11", "country": "SE" },
"electronic_addresses": ["mailto:ops@acme.example"],
"information_uri": "https://registry.acme.example",
"services": [
{
"list": "registrars",
"name": "Acme Register",
"service_type": "http://uri.etsi.org/19602/SvcType/Register",
"digital_ids": { "x509_certificates": ["<base64 DER leaf certificate>"] }
}
]
}
Secondary (legacy) lists
The legacy lists accept any subset of six digital-id kinds; at least one must
be non-empty. service_type is free-form here and usually omitted.
list key |
Document | Digital-id kinds |
|---|---|---|
nxd-tl |
legacy/NXD-TL.xml |
x509_certificates, x509_skis, x509_subject_names, dids, kids, jwks_uris |
ewc-tl |
legacy/EWC-TL.xml |
same six kinds |
nxd-tl - legacy NXD trust list:
{
"participant_id": "acme-legacy",
"name": "Acme Legacy Services AB",
"address": { "street": "Main Street 1", "locality": "Stockholm", "postal_code": "111 11", "country": "SE" },
"electronic_addresses": ["mailto:ops@acme.example"],
"information_uri": "https://acme.example",
"services": [
{
"list": "nxd-tl",
"name": "Acme Issuer",
"digital_ids": {
"x509_certificates": ["<base64 DER leaf certificate>"],
"dids": ["did:web:acme.example#key-1"],
"kids": ["<kid>"],
"jwks_uris": ["https://acme.example/.well-known/jwks.json"]
}
}
]
}
ewc-tl - legacy EWC trust list:
{
"participant_id": "acme-ewc",
"name": "Acme EWC Pilot AB",
"address": { "street": "Main Street 1", "locality": "Stockholm", "postal_code": "111 11", "country": "SE" },
"electronic_addresses": ["mailto:ops@acme.example"],
"information_uri": "https://acme.example",
"services": [
{
"list": "ewc-tl",
"name": "Acme EWC Issuer",
"digital_ids": { "dids": ["did:web:acme.example#key-1"] }
}
]
}
3. Remove an entry
DELETE {base}/admin/participants/{participant_id}. For a service client this
always answers 202 and queues a participant.delete approval, whatever
the scopes - the same rule human admins live under.
4. Read back
GET {base}/admin/participants- every entry, with per-service certificate expiry warnings (needs any scope).GET {base}/admin/trust-lists- the signed documents and their sequence numbers; a successful write bumps the sequence.POST {base}/trust-list/lookupwith{"x5c": ["<base64 DER>"]}- public, no auth; the definitive check that a certificate is granted by a list.
Pitfalls
- The secret is shown once at creation or rotation; there is no read-back.
- A
401on a token that worked a moment ago usually means the client was disabled or deleted - the backend re-reads the client row on every request. - Do not retry a
202- resubmitting creates nothing new (a pending approval for the same participant and action is refused with400) but the queue only drains when a super admin acts. - The approvals queue re-validates the payload at approval time, so a record that was valid when queued can still be rejected if the rules tightened.
- On the per-type lists every digital-id kind except
x509_certificatesis silently discarded; only the legacy lists keep DIDs, KIDs, JWKS URIs, SKIs, and subject names.
Cross-references
igrantio-api-key-management- create the CSR and upload the certificate chain in OWS that this skill then registers in the trust list.igrantio-api-trust-anchor- the OWS-side trust lists that consume what this skill publishes.