The validation call
The API has one endpoint that does the work: POST /v1/validations. Everything else — fetching stored results, usage, trust-list introspection — reads around it. This page is that call in depth: the request, the result document block by block, and the semantics that matter when you build on it.
The request
curl https://api.quovalis.eu/v1/validations \
-H "Authorization: Bearer $QUOVALIS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"certificate": "-----BEGIN CERTIFICATE-----\n…\n-----END CERTIFICATE-----",
"chain": ["-----BEGIN CERTIFICATE-----\n…\n-----END CERTIFICATE-----"],
"policy": "POLICY_EIDAS_QWAC_V1",
"options": { "checkRevocation": true }
}'certificate(required) — the leaf certificate, PEM-encoded.chain— up to 10 untrusted intermediates. You do not have to reconstruct a perfect chain: trusted-list-derived and AIA-known intermediates are also used when building the path, so a leaf alone often validates. Sending what your TLS handshake produced is still the most reliable input.policy(required) —POLICY_EIDAS_QWAC_V1for website authentication certificates,POLICY_EIDAS_QSEALC_V1for electronic seals (see QWAC vs QSealC). An omitted or unrecognized policy is rejected with400 invalid-request.options.checkRevocation— defaulttrue. Withfalse, the result carries norevocationblock at all.
Request bodies are capped at 256 KiB. Unknown members are ignored, not rejected — a misspelled optional key silently falls back to its default — so check spelling against the OpenAPI schema rather than relying on a 400.
The result document
A computed verdict is always an HTTP 200 — including VERDICT_INVALID and VERDICT_INDETERMINATE. Error status codes are reserved for requests the service could not process at all (malformed input, auth, limits — see errors below). Two wire conventions hold everywhere: a field that does not apply is an absent key, never null, and 64-bit integers such as sequenceNumber are JSON strings so no precision is lost.
{
"id": "0198f3a2-7c11-7000-8000-3f9be1a2c001",
"verdict": "VERDICT_VALID",
"policy": "POLICY_EIDAS_QWAC_V1",
"validatedAt": "2026-07-09T12:00:00Z",
"certificate": {
"subject": "CN=bank.example.com,O=Example Bank N.V.,organizationIdentifier=PSDNL-DNB-123456,C=NL",
"issuer": "CN=Example QTSP QWAC CA,O=Example QTSP,C=NL",
"serialNumber": "0a:1b:2c:3d:4e:5f",
"notBefore": "2026-01-01T00:00:00Z",
"notAfter": "2027-01-01T00:00:00Z",
"sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
},
"qualified": {
"isQualified": true,
"qcType": "QC_TYPE_WEB",
"qcStatements": ["QcCompliance", "QcType", "QcPDS"],
"trustServiceProvider": {
"name": "Example QTSP",
"serviceName": "Example QTSP QWAC CA",
"territory": "NL",
"serviceStatus": "granted"
}
},
"psd2": {
"roles": ["PSD2_ROLE_PSP_AS", "PSD2_ROLE_PSP_AI"],
"ncaId": "NL-DNB",
"ncaName": "De Nederlandsche Bank",
"authorizationNumber": "PSDNL-DNB-123456",
"organizationIdentifier": "PSDNL-DNB-123456"
},
"revocation": {
"status": "REVOCATION_STATUS_GOOD",
"method": "REVOCATION_METHOD_OCSP",
"source": "http://ocsp.example-qtsp.example",
"producedAt": "2026-07-09T11:55:00Z",
"checkedAt": "2026-07-09T12:00:00Z",
"cached": false
},
"trustList": {
"territory": "NL",
"sequenceNumber": "87",
"snapshotId": "0198f3a2-7c11-7000-8000-3f9be1a2c002"
}
}Block by block, with the rules for when each is absent:
verdict/subIndication— the tri-state outcome.subIndicationnames the dominant reason on non-valid verdicts (SUB_INDICATION_REVOKED,SUB_INDICATION_CHAIN_FAILURE,SUB_INDICATION_NOT_QUALIFIED,SUB_INDICATION_REVOCATION_UNAVAILABLE, …) and is absent on a valid one. An indeterminate verdict is never silently upgraded. The full sub-indication table and the semantics of each state are on the tri-state verdict.certificate— the parsed leaf: subject, issuer, serial, validity window and SHA-256 fingerprint, so you can confirm which certificate the verdict is about without re-parsing the PEM yourself.qualified— the qualified-status mapping:isQualified, the parsedqcStatements, theqcType(absent when the certificate declares no known QC type — and also when it declares more than one, since a qualified certificate has exactly one), and the matched trust service provider with its service name, territory and current service status. How this answer is reached is its own page: how qualified status is determined.psd2— the payment-service roles and the competent authority (name and id) parsed from the PSD2 QCStatement, together with the authorization number carried in the subject'sorganizationIdentifier; absent when the certificate carries no PSD2 statement. The attributes are read from the certificate; NCA and EBA registers are not cross-checked.revocation— status, method (OCSP or CRL), the evidence source and itsproducedAt/checkedAttimestamps, and acachedflag telling you whether a previously obtained response was relied on. When fresh OCSP/CRL data is unavailable, a cached response may be relied on for up to 24 hours past its scheduled update time (see the Terms); the timestamps tell you exactly how old the evidence is. Absent when revocation checking was disabled, and when no chain could be built — with no issuer there is nothing to verify revocation evidence against.trustList— the territory, list sequence number and snapshot id the verdict was computed against: the reproducibility anchor. Absent when the chain did not build to any EU trust-list anchor.
Calling semantics
Not idempotent, by design. Every call computes against the current trust-list and revocation state and produces a new, billable validation record. If you want the answer you already got, fetch it instead of re-validating:
curl https://api.quovalis.eu/v1/validations/0198f3a2-7c11-7000-8000-3f9be1a2c001 \ -H "Authorization: Bearer $QUOVALIS_API_KEY"
- Results are stored and re-served from the stored record — the same document on every fetch, never recomputed — for up to your plan's retention window. Quotas, rates and retention per plan are on the pricing page.
- Verdicts are point-in-time. The result document pins its trust-list snapshot and revocation timestamps; a certificate that was valid this morning can be revoked this afternoon. What a stored verdict tells you is what the published data said then — re-validate when the decision matters now.
- Limits surface in headers. Every authenticated response carries
X-RateLimit-*; throttled requests get429withRetry-After, and every response anX-Request-Idto quote at support.
Errors
All non-2xx responses are RFC 9457 problem documents (application/problem+json) with a type of urn:quovalis:problem:<suffix>:
| Type suffix | Status | When |
|---|---|---|
invalid-request | 400 | Malformed JSON, invalid query parameters, unparseable PEM or an unsupported policy — but never an undocumented member, which is ignored |
unauthorized | 401 | Missing, unknown or revoked API key |
key-expired | 401 | Valid key past its expiry date |
ip-not-allowed | 403 | Source IP outside the key's IP allowlist |
insufficient-scope | 403 | Key scopes do not cover the operation |
not-found | 404 | Unknown id (or a resource that is not yours) |
request-too-large | 413 | Request body over the 256 KiB cap |
rate-limited | 429 | Rate limit exceeded; Retry-After is set |
quota-exceeded | 429 | Monthly quota exhausted on a plan that blocks overage |
overage-cap-reached | 429 | Your monthly overage spend cap is reached |
internal | 500 | Unhandled failure on our side |
upstream-unavailable | 503 | A downstream dependency is unavailable; Retry-After is set |
overloaded | 503 | Platform load shedding (not a tenant limit); Retry-After is set |
Problem bodies carry a requestId — quote it when contacting support and we can find the exact request in our logs.
The two monthly-window 429s — quota-exceeded and overage-cap-reached — additionally carry a resetAt extension member: an RFC 3339 timestamp of when the monthly window rolls over. Parse it to back off programmatically rather than scraping the human-readable detail. There is deliberately no Retry-After on these — the wait can be weeks.
Related
- API quickstart — key setup and the surrounding read endpoints.
- How qualified status is determined — the procedure behind the
qualifiedblock. - What is a QWAC? — the certificate type most validations run on.