The OID4VP verifier, real endpoints, real policy knobs, and one of them means less than it reads
The verifier side is live and auth-gated on the capture API.
Issuance and presentation are different protocols
OID4VCI gets a credential into a holder's wallet. OID4VP gets a proof out of it, to a verifier. They are separate specifications and a vendor can implement one without the other.
We implement both, in different services: the issuance metadata is published at the capture API's well-known endpoint, and the presentation endpoints live on the capture API too. The Verify application backend has none. If you are looking for OID4VP in the wrong service you will correctly conclude it is not there.
What actually exists
Three endpoints, verified live on 2026-07-31:
| endpoint | purpose | probe result |
|---|---|---|
PUT /v1/presentations/policy |
set what this verifier will accept | 401 |
POST /v1/presentations/requests |
mint a presentation request (QR / deep link) | 401 |
POST /v1/presentations/verify |
verify a returned vp_token |
401 |
A 401 means the route exists and requires credentials. A route that does not exist returns 404, we ran that control, and we ran a known-good endpoint as a positive control too. So a stranger can confirm these endpoints are real without having an account; they cannot confirm what they do. That is a weaker proof than the ones on verification reuse and selective disclosure, and we are grading it as weaker.
The verifier policy, which is the interesting part
A verifier does not just check a signature: it declares what it is willing to accept. The policy supports:
requested_claims, which attributes the presentation must carry.accept_min_loa, a minimum assurance level, from our own L1 / L2 / L3 / L3+ scheme.accept_issuers, either every issuer enrolled in the trust registry, or an explicit list of issuer DIDs.freshness_max_days, reject a credential whose underlying verification is older than a chosen window.ttl_seconds, how long a minted request stays valid.
freshness_max_days is the one worth dwelling on, because it is the knob that makes reuse
defensible rather than indefinite: a relying party can decide that a verification from fourteen
months ago is not good enough for its risk appetite, without having to reject reuse entirely.
That is the mechanism by which "verify once" stops meaning "verify once, forever."
Two knobs that mean less than they read
accept_issuers: registry_all currently means "accept Solidus." It sounds like a policy about a
diverse ecosystem. The trust registry has one operator, us, and there is no federation. Until
there is a second issuer, choosing "any registry issuer" and choosing "Solidus" are the same
decision. See vendor lock-in.
Our L1/L2/L3 assurance levels are our own scheme. They are not eIDAS levels of assurance, not an accredited assurance framework, and no external body has evaluated what our levels correspond to. They are useful as a policy dial between our own tiers; they do not import anybody else's meaning.
What this does not do
- It does not decide whether relying on the credential is reasonable. The policy engine enforces the rules you set. Whether those rules are adequate for your regulator is your call, and reliance records that no unaffiliated party has yet made that call in production.
- It does not discharge your obligation. Whether third-party verification satisfies a given regulatory duty is unsettled.
- It does not make an unaudited proof audited. A BBS+ presentation verified by this endpoint is still produced by code nobody independent has reviewed.
Keep reading
- Liveness detection, what is built, what has not cleared review, and what depends on deployment
- Presentation attack detection, whose model this actually is, and a threshold we have never evaluated
- Selective disclosure mechanics, two mechanisms, two different guarantees, one of them unaudited
- EUDI wallet issuance, not certified, not recognised, and gated on a partner we do not have