Issue Certificates

Use the X.509 Certificate Service to issue self-signed roots, CA-signed intermediates and leaves, ISO IACA roots, ISO Document Signer certificates, and ETSI Provider / Wallet Relying Party certificates. Every certificate is issued through one request shape (UpsertGeneratedCertificateRequest) sent to either:

  • POST /v1/{target}/x509-service-api/certificates — issue a new certificate.
  • PUT /v1/{target}/x509-service-api/certificates — issue or replace the certificate at a known ID.

Service reference: Swagger API Reference

Shared Path Parameters

  • orgID: When performing operations within an organization, use the organization's Base URL or another valid host alias. For example, if your organization is named test, your default Base URL is test.enterprise-sandbox.waltid.dev on the sandbox environment.
  • target: The X.509 Certificate service path, optionally suffixed with a certificate ID:
    • {organizationID}.{tenantID}.{x509ServiceID} — for POST, the service generates a random certificate ID.
    • {organizationID}.{tenantID}.{x509ServiceID}.{certificateID} — issue the certificate under a specific ID. Required for PUT.

Certificate ID and storage

The certificate ID is taken from the request path, not the body. When one or more X.509 Store services are attached, the issued certificate is written into them under that ID (one shared child ID across all attached stores), and the response persistResult maps each full stored path to its outcome. When no store is attached the certificate is generated and returned but not stored, and persistResult is empty.

Request fields

All issuance requests use the same body. validFrom and validTo are always required; everything else depends on the certificate you are issuing.

Store persistence (optional, override the service default — see Store persistence)

  • createStrategy: String - CREATE_NONE | CREATE_ONE | CREATE_ALL. Applies to POST.
  • upsertStrategy: String - INSERT_ONE_UPDATE_ALL | UPSERT_ALL. Applies to PUT.
  • successStrategy: String - ANY_OK | ALL_OK.
  • storeRef: String - Target reference of a single X.509 store to write into, e.g. test.tenant1.x509-store-1. When omitted, the store selection follows the strategy.

Certificate shape

  • certificateProfile: String (optional) - Applies a predefined profile and validates the issued certificate against it. See Certificate profiles for the full list of names and requirements.
  • signatureAlgorithm: String (optional) - Signature algorithm used to sign the certificate. Defaults to an algorithm derived from the issuer key (SHA-256/384/512 with ECDSA or RSA PKCS#1 depending on the key).
  • validFrom: String (required) - Validity start (notBefore), ISO-8601 instant, e.g. 2026-09-01T00:00:00Z.
  • validTo: String (required) - Validity end (notAfter), ISO-8601 instant.

Subject public key and subject DN (supply exactly one key source)

  • subjectKeyRef: String - KMS key path for the subject key, e.g. test.tenant1.kms1.leaf-key.
  • subjectKeyJwk: Object - Subject public/private key as a JWK.
  • csrPem: String - PKCS#10 CSR PEM. The subject DN and subject alternative names are taken from the CSR and its signature is verified for proof-of-possession. Cannot be combined with subjectDn or selfSigned. See CSR Workflows.
  • subjectDn: String - Subject distinguished name, e.g. CN=Example Leaf,O=Example Org,C=US. Required unless a CSR is provided.

Issuer (self-signed, or one issuer source)

  • selfSigned: Boolean (default false) - When true, the subject key also signs the certificate. No issuer certificate or issuer DN may be set.
  • issuerCertificateRef: String - Path of a stored issuer certificate, e.g. test.tenant1.x509-store-1.example-iaca. The issuer DN and (if not overridden) the issuer key are taken from it.
  • issuerCertificatePem: String - Issuer certificate PEM, used the same way as issuerCertificateRef. Required when no store is attached and the certificate is not self-signed.
  • issuerKeyRef: String - KMS key path for the signing key. Required if the issuer certificate does not record a subject key reference.
  • issuerKeyJwk: Object - Signing key as a JWK. Mutually exclusive with issuerKeyRef.

For a CA-signed certificate (any issuer other than selfSigned), validFrom / validTo must fall within the issuer certificate's own validity window. A request outside that window fails with 400.

Extensions

  • keyUsage: Array of Strings - Key usage bits: digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment, keyAgreement, keyCertSign, cRLSign, encipherOnly, decipherOnly.
  • extendedKeyUsageOids: Array of Strings - Extended key usage OIDs.
  • basicConstraintsCa: Boolean - true for a CA certificate, false to explicitly mark a leaf.
  • basicConstraintsPathLength: Integer - Maximum number of intermediate CAs below this certificate. Only valid when basicConstraintsCa is true.
  • subjectAlternativeNames: Array - SAN entries, each { "type": ..., "name": ... } with type one of dnsName, email, uri, ipAddress. Overrides SANs carried in a CSR.
  • issuerAlternativeNames: Array - Issuer alternative name entries in the same { type, name } form.
  • crlDistributionPointUri: String - CRL distribution point URI.
  • authorityInfoAccessCaIssuerUris: Array of Strings - Authority Information Access: URI(s) where the issuing CA's own certificate can be retrieved.
  • authorityInfoAccessOcspResponderUris: Array of Strings - Authority Information Access: URI(s) of OCSP responder(s) for this certificate.
  • certificatePolicyOids: Array of Strings - Certificate Policies extension: OID strings identifying the applicable certificate policies (e.g. a TSP-defined or ETSI policy OID).
  • qcStatements: Object (optional) - RFC 3739 / ETSI EN 319 412-5 Qualified Certificate Statements:
    • qcCompliance: Boolean (default false) - Asserts the certificate is a qualified certificate (QcCompliance statement).
    • qcSscd: Boolean (default false) - Asserts the private key resides in a qualified signature/seal creation device (QcSSCD statement).
    • qcTypeOids: Array of Strings - OID(s) naming the certificate type(s) asserted by the QcType statement, e.g. id-etsi-qct-esign / -eseal / -web.
    • otherStatementIds: Array of Strings - Additional statement OIDs not covered above. statementInfo is not supported and is omitted.

Metadata

  • metadata: Object - Arbitrary JSON stored alongside the certificate. The service merges in issuerKeyRef, subjectKeyRef, and issuerCertRef (the resolved references) on top of what you provide. To make a self-signed IACA eligible for a VICAL, include vicalDocType as a JSON array of mdoc document types, e.g. "vicalDocType": ["org.iso.18013.5.1.mDL"].

Certificate profiles

certificateProfile applies a predefined extension set to the certificate being issued and validates the result against it after issuance. certificateProfile is independent of selfSigned / issuer selection — each profile fixes whether it is self-signed or CA-issued, as shown below.

ProfileAliasStandardSelf-signed
iso-iaca-rootorg.iso.18013.5.1.iacaISO/IEC 18013-5 (mDL)Always
iso-document-signerorg.iso.18013.5.1.dsISO/IEC 18013-5 (mDL)Never — issued by an IACA
etsi-pid-provider—ETSI TS 119 412-6Self-signed or CA-issued
etsi-wallet-provider—ETSI TS 119 412-6Self-signed or CA-issued
etsi-wrpac—ETSI TS 119 411-8Never — issued by a national WRP provider
etsi-wrprc—ETSI TS 119 475Never — issued by a national registrar

etsi-wrprc is a draft profile: it applies and validates the baseline end-entity certificate shape shared by every ETSI EUDI profile, but does not yet encode or validate the WRPRC-specific "registered intended use" — that requires a custom extension not modelled yet. A generated etsi-wrprc certificate always carries a registeredIntendedUse warning in the service log; it does not fail issuance.

Self-signed root or CA certificate

Set selfSigned: true and provide one key via subjectKeyRef or subjectKeyJwk (or issuerKeyJwk); that key is both subject and signing key.

CURL

Endpoint: POST /v1/{target}/x509-service-api/certificates | API Reference

Example Request

curl -X 'POST' \
  'https://{orgID}.enterprise-sandbox.waltid.dev/v1/{target}/x509-service-api/certificates' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer {yourToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "selfSigned": true,
  "subjectKeyRef": "test.tenant1.kms1.root-key",
  "subjectDn": "CN=Example Root CA,O=Example Org,C=US",
  "validFrom": "2026-09-01T00:00:00Z",
  "validTo": "2031-09-01T00:00:00Z",
  "basicConstraintsCa": true,
  "basicConstraintsPathLength": 1,
  "keyUsage": ["keyCertSign", "cRLSign"]
}'

Body Parameters

  • selfSigned: Boolean (required here) - Set to true.
  • subjectKeyRef / subjectKeyJwk: String / Object (required) - The root key. Also used as the signing key.
  • subjectDn: String (required) - Distinguished name of the root.
  • validFrom / validTo: String (required) - Validity window.
  • basicConstraintsCa / basicConstraintsPathLength / keyUsage: mixed (optional) - Recommended for a CA certificate.

Example Response

{
  "certificate": {
    "_id": "test.tenant1.x509-store-1.b0e0f4b7-2b1a-4a2e-9d9f-6b2f0d5c1a33",
    "createdAt": "2026-09-01T09:12:44Z",
    "updatedAt": "2026-09-01T09:12:44Z",
    "metadata": {
      "issuerKeyRef": "test.tenant1.kms1.root-key",
      "subjectKeyRef": "test.tenant1.kms1.root-key"
    },
    "certificatePem": "-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----",
    "issuerDn": "CN=Example Root CA,O=Example Org,C=US",
    "issuerDnHex": "3045310f...",
    "subjectDn": "CN=Example Root CA,O=Example Org,C=US",
    "subjectDnHex": "3045310f...",
    "validFrom": "2026-09-01T00:00:00Z",
    "validTo": "2031-09-01T00:00:00Z"
  },
  "persistResult": {
    "test.tenant1.x509-store-1.b0e0f4b7-2b1a-4a2e-9d9f-6b2f0d5c1a33": "CREATED"
  }
}

Response Codes

  • 200 - Certificate generated, no store attached (not persisted).
  • 201 - Certificate issued and persisted.
  • 409 - A certificate with this ID already exists in an attached store.
  • 400 - Invalid request (e.g. conflicting key inputs, missing subject DN).
  • 401 - Invalid or missing authentication token.
  • 500 - The certificate was generated but a required store write failed (e.g. a store was unreachable). Check persistResult for which store reported FAILED; safe to retry with the same request.

CA-signed certificate

Omit selfSigned and point at an issuer. The most common source is a stored issuer certificate (issuerCertificateRef); the signing key is read from that certificate's recorded subjectKeyRef metadata unless you override it with issuerKeyRef / issuerKeyJwk.

CURL

Endpoint: POST /v1/{target}/x509-service-api/certificates | API Reference

Example Request

curl -X 'POST' \
  'https://{orgID}.enterprise-sandbox.waltid.dev/v1/{target}/x509-service-api/certificates' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer {yourToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "issuerCertificateRef": "test.tenant1.x509-store-1.example-root-ca",
  "subjectKeyRef": "test.tenant1.kms1.leaf-key",
  "subjectDn": "CN=Example Leaf,O=Example Org,C=US",
  "validFrom": "2026-09-01T00:00:00Z",
  "validTo": "2027-09-01T00:00:00Z",
  "keyUsage": ["digitalSignature"],
  "subjectAlternativeNames": [
    { "type": "dnsName", "name": "leaf.example.com" }
  ]
}'

Body Parameters

  • issuerCertificateRef: String (required here) - Stored issuer certificate. Use issuerCertificatePem instead when no store is attached.
  • issuerKeyRef / issuerKeyJwk: String / Object (optional) - Override the signing key. Required if the issuer certificate does not record one.
  • subjectKeyRef / subjectKeyJwk / csrPem: mixed (required) - Subject public key source.
  • subjectDn: String (required unless csrPem is used) - Leaf distinguished name.

Example Response

{
  "certificate": {
    "_id": "test.tenant1.x509-store-1.f5a1c0d2-8e7b-4c3a-91d0-2a7e4b9c1f66",
    "createdAt": "2026-09-01T09:20:10Z",
    "updatedAt": "2026-09-01T09:20:10Z",
    "metadata": {
      "issuerKeyRef": "test.tenant1.kms1.root-key",
      "subjectKeyRef": "test.tenant1.kms1.leaf-key",
      "issuerCertRef": "test.tenant1.x509-store-1.example-root-ca"
    },
    "certificatePem": "-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----",
    "issuerDn": "CN=Example Root CA,O=Example Org,C=US",
    "issuerDnHex": "3045310f...",
    "subjectDn": "CN=Example Leaf,O=Example Org,C=US",
    "subjectDnHex": "3043310d...",
    "validFrom": "2026-09-01T00:00:00Z",
    "validTo": "2027-09-01T00:00:00Z"
  },
  "persistResult": {
    "test.tenant1.x509-store-1.f5a1c0d2-8e7b-4c3a-91d0-2a7e4b9c1f66": "CREATED"
  }
}

Response Codes

  • 200 - Certificate generated, no store attached.
  • 201 - Certificate issued and persisted.
  • 409 - Certificate ID already exists in an attached store.
  • 400 - Invalid request (e.g. issuer not resolvable, subject DN in CSR mismatch).
  • 401 - Invalid or missing authentication token.
  • 500 - The certificate was generated but a required store write failed. Check persistResult for which store reported FAILED; safe to retry with the same request.

ISO IACA root certificate

Set certificateProfile to iso-iaca-root and selfSigned to true. The profile adds the ISO/IEC 18013-5 IACA extensions (CA=true, pathLenConstraint=0, keyCertSign + cRLSign marked critical, subject key identifier, issuer alternative name) and validates the result. Supported keys: secp256r1, secp384r1, secp521r1.

CURL

Endpoint: POST /v1/{target}/x509-service-api/certificates | API Reference

Example Request

curl -X 'POST' \
  'https://{orgID}.enterprise-sandbox.waltid.dev/v1/{target}.iaca-prod-2026/x509-service-api/certificates' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer {yourToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "selfSigned": true,
  "certificateProfile": "iso-iaca-root",
  "subjectKeyRef": "test.tenant1.kms1.iaca-key",
  "subjectDn": "CN=Example IACA,O=Example Org,C=US",
  "validFrom": "2026-09-01T00:00:00Z",
  "validTo": "2036-09-01T00:00:00Z",
  "issuerAlternativeNames": [
    { "type": "uri", "name": "https://iaca.example.com" },
    { "type": "email", "name": "iaca@example.com" }
  ],
  "metadata": {
    "vicalDocType": ["org.iso.18013.5.1.mDL"]
  }
}'

Body Parameters

  • certificateProfile: String (required here) - iso-iaca-root.
  • selfSigned: Boolean (required here) - true.
  • subjectKeyRef / subjectKeyJwk: String / Object (required) - The IACA key (secp256r1 / secp384r1 / secp521r1).
  • subjectDn: String (required) - IACA distinguished name. C= must be a valid ISO 3166-1 alpha-2 code.
  • issuerAlternativeNames: Array (required by profile) - At least one uri or email entry.
  • metadata.vicalDocType: Array of Strings (optional) - Required for this IACA to be picked up by the VICAL Service.

Response Codes

  • 200 / 201 - IACA issued (persisted when a store is attached).
  • 400 - Profile validation failed (e.g. missing issuer alternative name, invalid country code, validity window too long).
  • 401 - Invalid or missing authentication token.
  • 500 - The certificate was generated but a required store write failed. Check persistResult for which store reported FAILED; safe to retry with the same request.

ISO Document Signer certificate

Set certificateProfile to iso-document-signer and issue against an IACA via issuerCertificateRef (a stored IACA) or issuerCertificatePem. The profile adds the Document Signer extensions (digitalSignature critical, extended key usage 1.0.18013.5.1.2, subject/authority key identifiers, issuer alternative name from the IACA, CRL distribution point) and validates the result.

CURL

Endpoint: POST /v1/{target}/x509-service-api/certificates | API Reference

Example Request

curl -X 'POST' \
  'https://{orgID}.enterprise-sandbox.waltid.dev/v1/{target}.ds-2026-q1/x509-service-api/certificates' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer {yourToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "certificateProfile": "iso-document-signer",
  "issuerCertificateRef": "test.tenant1.x509-store-1.iaca-prod-2026",
  "subjectKeyRef": "test.tenant1.kms1.ds-key",
  "subjectDn": "CN=Example Document Signer,O=Example Org,C=US",
  "validFrom": "2026-09-01T00:00:00Z",
  "validTo": "2027-11-01T00:00:00Z",
  "issuerAlternativeNames": [
    { "type": "uri", "name": "https://iaca.example.com" }
  ],
  "crlDistributionPointUri": "https://crl.example.com/ds.crl"
}'

Body Parameters

  • certificateProfile: String (required here) - iso-document-signer.
  • issuerCertificateRef / issuerCertificatePem: String (required) - The issuing IACA.
  • subjectKeyRef / subjectKeyJwk / csrPem: mixed (required) - Document Signer public key source.
  • subjectDn: String (required unless csrPem is used) - Document Signer distinguished name. Requires a CN=; C= must match the IACA's C=, and ST= must match the IACA's ST= if the IACA has one.
  • crlDistributionPointUri: String (required by profile) - CRL distribution point.
  • issuerKeyRef / issuerKeyJwk: String / Object (optional) - Override the IACA signing key.

Response Codes

  • 200 / 201 - Document Signer issued (persisted when a store is attached).
  • 400 - Profile validation failed (e.g. missing commonName, country/state mismatch with the IACA, validity window outside the IACA window or over the profile maximum).
  • 401 - Invalid or missing authentication token.
  • 500 - The certificate was generated but a required store write failed. Check persistResult for which store reported FAILED; safe to retry with the same request.

ETSI PID Provider certificate

Set certificateProfile to etsi-pid-provider. Issues an end-entity certificate for a PID (Personal Identification Data) Provider, per ETSI TS 119 412-6 clause 4 — the certificate whose private key signs SD-JWT VC / mdoc Person Identification Data. May be self-signed or CA-issued. The profile adds basicConstraints{cA=false} and keyUsage{digitalSignature} (both critical), a subject key identifier, and a qcStatements QcCompliance + QcType (id-etsi-qct-pid, 0.4.0.194126.1.1) statement automatically — you do not need to set qcStatements yourself unless you want to assert additional statements.

CURL

Endpoint: POST /v1/{target}/x509-service-api/certificates | API Reference

Example Request

curl -X 'POST' \
  'https://{orgID}.enterprise-sandbox.waltid.dev/v1/{target}/x509-service-api/certificates' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer {yourToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "selfSigned": true,
  "certificateProfile": "etsi-pid-provider",
  "subjectKeyRef": "test.tenant1.kms1.pid-provider-key",
  "subjectDn": "CN=Example PID Provider,O=Example PID Provider AT,organizationIdentifier=VATAT-U12345678,C=AT",
  "validFrom": "2026-09-01T00:00:00Z",
  "validTo": "2031-09-01T00:00:00Z",
  "certificatePolicyOids": ["1.3.6.1.4.1.55555.1.1"]
}'

Body Parameters

  • certificateProfile: String (required here) - etsi-pid-provider.
  • selfSigned: Boolean (optional) - true for a self-signed PID Provider certificate; omit and set an issuer for a CA-issued one.
  • subjectDn: String (required) - Must include C= and CN=, plus O= (legal person) or givenName=/surname=/pseudonym= + serialNumber= (natural person). Presence of organizationIdentifier= selects the legal-person shape. When CA-issued, the issuer certificate's subject DN is validated the same way.
  • certificatePolicyOids: Array of Strings (required by profile) - At least one TSP-defined policy OID.
  • authorityInfoAccessCaIssuerUris / authorityInfoAccessOcspResponderUris: Array of Strings (required by profile unless self-signed) - CA issuer / OCSP responder location(s).

Response Codes

  • 200 / 201 - Certificate issued (persisted when a store is attached).
  • 400 - Profile validation failed (e.g. missing certificate policy OID, missing authorityInfoAccess on a CA-issued certificate, malformed subject/issuer DN, unsupported key algorithm/size).
  • 401 - Invalid or missing authentication token.
  • 500 - The certificate was generated but a required store write failed. Check persistResult for which store reported FAILED; safe to retry with the same request.

ETSI Wallet Provider certificate

Set certificateProfile to etsi-wallet-provider. Issues an end-entity certificate for a Wallet Provider, per ETSI TS 119 412-6 clause 5.1 — the certificate whose private key signs Wallet Instance / Unit Attestations (WIA/WUA). Otherwise identical to the ETSI PID Provider certificate: may be self-signed or CA-issued, same DN shape and certificatePolicyOids / authorityInfoAccess requirements, same extensions applied automatically — except the automatic qcStatements QcType OID is id-etsi-qct-wal (0.4.0.194126.1.2).

CURL

Endpoint: POST /v1/{target}/x509-service-api/certificates | API Reference

Example Request

curl -X 'POST' \
  'https://{orgID}.enterprise-sandbox.waltid.dev/v1/{target}/x509-service-api/certificates' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer {yourToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "selfSigned": true,
  "certificateProfile": "etsi-wallet-provider",
  "subjectKeyRef": "test.tenant1.kms1.wallet-provider-key",
  "subjectDn": "CN=Example Wallet Provider,O=Example Wallet Provider AT,organizationIdentifier=VATAT-U87654321,C=AT",
  "validFrom": "2026-09-01T00:00:00Z",
  "validTo": "2031-09-01T00:00:00Z",
  "certificatePolicyOids": ["1.3.6.1.4.1.55555.2.1"]
}'

Body Parameters

Response Codes

Same as ETSI PID Provider certificate.

ETSI Wallet Relying Party Access Certificate (WRPAC)

Set certificateProfile to etsi-wrpac. Issues an end-entity certificate a Relying Party uses to authenticate a presentation request to a wallet, per ETSI TS 119 411-8 clause 6.6.1. Always CA-issued by a national WRP provider — there is no self-signed variant. The profile adds basicConstraints{cA=false}, keyUsage{digitalSignature} (both critical), and a subject key identifier; unlike the Provider profiles, certificatePolicies and qcStatements are not added automatically and must be supplied in the request.

CURL

Endpoint: POST /v1/{target}/x509-service-api/certificates | API Reference

Example Request

curl -X 'POST' \
  'https://{orgID}.enterprise-sandbox.waltid.dev/v1/{target}/x509-service-api/certificates' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer {yourToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "certificateProfile": "etsi-wrpac",
  "issuerCertificateRef": "test.tenant1.x509-store-1.wrp-national-provider",
  "subjectKeyRef": "test.tenant1.kms1.wrp-key",
  "subjectDn": "CN=Example Relying Party,O=Example Relying Party AT,C=AT",
  "validFrom": "2026-09-01T00:00:00Z",
  "validTo": "2027-09-01T00:00:00Z",
  "certificatePolicyOids": ["0.4.0.194118.1.2"],
  "subjectAlternativeNames": [
    { "type": "uri", "name": "https://relying-party.example.com" }
  ],
  "crlDistributionPointUri": "https://crl.example.com/wrpac.crl",
  "authorityInfoAccessCaIssuerUris": ["https://ca.example.com/wrp-national-provider.crt"]
}'

Body Parameters

  • certificateProfile: String (required here) - etsi-wrpac.
  • issuerCertificateRef / issuerCertificatePem: String (required) - The issuing national WRP provider certificate.
  • certificatePolicyOids: Array of Strings (required by profile) - Exactly one WRPAC policy OID (ETSI TS 119 411-8 clause 5.3): 0.4.0.194118.1.1 (NCP natural person), 0.4.0.194118.1.2 (NCP legal person), 0.4.0.194118.1.3 (QCP natural person), 0.4.0.194118.1.4 (QCP legal person).
  • qcStatements: Object (required if the policy OID above is a QCP one) - Set qcCompliance: true and qcSscd: true; for the legal-person QCP OID also set qcTypeOids (any non-empty QcType statement). Not required, and normally omitted, for the NCP policy OIDs.
  • subjectAlternativeNames: Array (required by profile) - At least one uri or email entry carrying Relying Party contact information.
  • crlDistributionPointUri and/or authorityInfoAccessOcspResponderUris: String / Array of Strings (required by profile) - At least one revocation mechanism (CRL or OCSP).
  • authorityInfoAccessCaIssuerUris / authorityInfoAccessOcspResponderUris: Array of Strings (required by profile) - WRPAC is always CA-issued, so authorityInfoAccess is mandatory.

Response Codes

  • 200 / 201 - Certificate issued (persisted when a store is attached).
  • 400 - Profile validation failed (e.g. missing or non-unique WRPAC policy OID, missing contact SAN, no revocation mechanism, missing qcStatements for a QCP policy, malformed DN).
  • 401 - Invalid or missing authentication token.
  • 500 - The certificate was generated but a required store write failed. Check persistResult for which store reported FAILED; safe to retry with the same request.

ETSI Wallet Relying Party Registration Certificate (WRPRC)

Set certificateProfile to etsi-wrprc. Issues an end-entity certificate that registers a Relying Party with a national registrar, per ETSI TS 119 475. Always CA-issued by a national registrar — there is no self-signed variant. The profile adds the same baseline extensions as WRPAC (basicConstraints{cA=false}, keyUsage{digitalSignature}, subject key identifier); certificatePolicyOids and authorityInfoAccess must be supplied in the request, and the subject DN is always treated as a legal person.

etsi-wrprc is a draft profile — it does not yet validate the WRPRC-specific "registered intended use" (the scope a registrar authorizes the Relying Party to request). See Certificate profiles.

CURL

Endpoint: POST /v1/{target}/x509-service-api/certificates | API Reference

Example Request

curl -X 'POST' \
  'https://{orgID}.enterprise-sandbox.waltid.dev/v1/{target}/x509-service-api/certificates' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer {yourToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "certificateProfile": "etsi-wrprc",
  "issuerCertificateRef": "test.tenant1.x509-store-1.wrp-national-provider",
  "subjectKeyRef": "test.tenant1.kms1.wrp-registration-key",
  "subjectDn": "CN=Example Relying Party,O=Example Relying Party AT,C=AT",
  "validFrom": "2026-09-01T00:00:00Z",
  "validTo": "2027-09-01T00:00:00Z",
  "certificatePolicyOids": ["1.3.6.1.4.1.55555.3.1"],
  "authorityInfoAccessCaIssuerUris": ["https://ca.example.com/wrp-national-provider.crt"]
}'

Body Parameters

  • certificateProfile: String (required here) - etsi-wrprc.
  • issuerCertificateRef / issuerCertificatePem: String (required) - The issuing national registrar certificate.
  • subjectDn: String (required unless csrPem is used) - Legal-person DN: requires C=, CN=, and O=.
  • certificatePolicyOids: Array of Strings (required by profile) - At least one policy OID; ETSI TS 119 475 does not yet define a fixed WRPRC OID, so use whatever the issuing registrar's CPS defines.
  • authorityInfoAccessCaIssuerUris / authorityInfoAccessOcspResponderUris: Array of Strings (required by profile) - WRPRC is always CA-issued, so authorityInfoAccess is mandatory.

Response Codes

  • 200 / 201 - Certificate issued (persisted when a store is attached).
  • 400 - Profile validation failed (e.g. missing certificate policy OID, missing authorityInfoAccess, malformed DN).
  • 401 - Invalid or missing authentication token.
  • 500 - The certificate was generated but a required store write failed. Check persistResult for which store reported FAILED; safe to retry with the same request.

Issue or replace a certificate (PUT)

PUT /certificates writes the certificate at the ID given in the path, creating it if it does not exist and updating it if it does.

PUT /certificates requires at least one attached X.509 Store service — there is nowhere to upsert a certificate without one. Use POST /certificates for stateless issuance.

CURL

Endpoint: PUT /v1/{target}/x509-service-api/certificates | API Reference

Example Request

curl -X 'PUT' \
  'https://{orgID}.enterprise-sandbox.waltid.dev/v1/{target}.leaf-service-a/x509-service-api/certificates' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer {yourToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "issuerCertificateRef": "test.tenant1.x509-store-1.example-root-ca",
  "subjectKeyRef": "test.tenant1.kms1.leaf-key",
  "subjectDn": "CN=Service A,O=Example Org,C=US",
  "validFrom": "2026-09-01T00:00:00Z",
  "validTo": "2027-09-01T00:00:00Z",
  "keyUsage": ["digitalSignature"],
  "upsertStrategy": "UPSERT_ALL"
}'

Path Parameters

  • target: resourceIdentifier (required) - Must include the certificate ID: {organizationID}.{tenantID}.{x509ServiceID}.{certificateID}.

Body Parameters

  • Same as POST /certificates, with upsertStrategy instead of createStrategy.

Response Codes

  • 200 - Existing certificate replaced.
  • 201 - Certificate created.
  • 400 - Invalid request.
  • 401 - Invalid or missing authentication token.
  • 500 - The certificate was generated but a required store write failed. Check persistResult for which store reported FAILED; safe to retry with the same request.

Response model

Both endpoints return an UpsertCertificateResponse:

  • certificate: Object - The issued certificate.
    • _id: String - Full path. When a store is attached this is {store}.{certificateID}; otherwise {x509ServiceID}.{certificateID}.
    • certificatePem: String - PEM-encoded certificate.
    • issuerDn / issuerDnHex, subjectDn / subjectDnHex: String - Distinguished names in string and hex-DER form.
    • validFrom / validTo: String - Effective validity window (truncated to whole seconds).
    • metadata: Object - Stored metadata, including the resolved issuerKeyRef, subjectKeyRef, issuerCertRef, and any vicalDocType you supplied.
    • createdAt / updatedAt: String - Persistence timestamps.
  • persistResult: Object - Maps each full stored certificate path to its outcome: CREATED, UPDATED, ALREADY_EXISTS, or FAILED. Empty when no store is attached.

Store persistence

The service's default persistence strategy (set at Setup) decides which attached stores receive the certificate and when the request counts as successful. Any request can override it:

  • upsertStrategy (PUT) — how the write certificates across stores.
  • successStrategy — ANY_OK or ALL_OK.
  • storeRef — restrict the write to one named store.

With successStrategy: ANY_OK a partial failure still returns success as long as at least one store write succeeds; inspect persistResult for per-store FAILED entries. If every write fails (ANY_OK), or any write fails (ALL_OK), the request returns 500 even though the certificate was generated — the FAILED entries in persistResult say which store(s) rejected the write.

Deprecated endpoints

The following endpoints and request shapes are still served for backward compatibility but are superseded by POST / PUT /certificates:

DeprecatedReplacement
POST /x509-service-api/certificate (generic, key-descriptor / issuer objects)POST /x509-service-api/certificates with flat subjectKeyRef / issuerKeyRef / issuer fields
POST /x509-service-api/iso/iacasPOST /x509-service-api/certificates with certificateProfile: "iso-iaca-root", selfSigned: true
POST /x509-service-api/iso/document-signersPOST /x509-service-api/certificates with certificateProfile: "iso-document-signer"
POST /x509-service-api/csrs with type: "generic" / type: "iso-document-signer" and subjectKeyDescriptorPOST /x509-service-api/csrs with flat subjectKeyRef / subjectKeyJwk and subjectDn — see CSR Workflows

New integrations should not use the deprecated endpoints. They accept the old kms-hosted-key-descriptor / jwk-encoded-key-descriptor objects and the old storedCertificateId body field, none of which exist on POST / PUT /certificates.

Last updated on September 28, 2026