Download OpenAPI specification:
Client Attestation and Key Attestation issuance for wallets — carved out of the main VC Platform API (../../../docs/api/openapi.yaml) into its own deployable, apps/wallet-backend, for security isolation: these four endpoints are the only fully unauthenticated, pre-auth routes anywhere on the VC Platform system. A private Wallet-Provider↔Wallet-Instance protocol, not part of any OpenID4VC spec — this is this platform's own minimal, PoP-gated version of it. See the User Guide for the full walkthrough with worked examples and error-handling guidance; this reference states the wire contract precisely and nothing more.
Reached through the same public hostname as the rest of the VC Platform API — routing to this separate service happens at the edge, not by the caller knowing two different hosts.
The reference client-attestation issuer. Both OpenID4VCI grants on the main VC Platform API require a wallet to present a Client Attestation JWT from some Wallet Provider; obtaining one in the first place is deliberately left undefined by every OpenID4VC spec, so this service ships its own minimal, PoP-gated version for wallets that don't have a real Wallet Provider of their own yet.
Self-attested: this vouches for whoever holds the private key
matching jwk, not for the key's trustworthiness. pop is a
compact JWS over {"challenge": "<challenge>"}, signed by that
same private key. The challenge is consumed on use — a second call
with the same challenge and pop fails, even if the first succeeded.
| jwk required | object Public JWK of the key being attested — the wallet-instance key for POST /attestations, the credential-holder key for POST /key-attestations. |
| challenge required | string From a prior call to the matching /challenge endpoint above. |
| pop required | string Compact JWS over |
{- "jwk": { },
- "challenge": "string",
- "pop": "string"
}{- "attestation_jwt": "string",
- "client_id": "string"
}ARF Annex 2 Topic 9 Key Attestation issuance. Same PoP-gated
challenge shape as Attestations, but attests the credential-holder
key (what a wallet typically calls identity.jwk) rather than the
wallet-instance key, and describes key storage
(key_storage: ["software"], deliberately no hardware-backed claim)
instead of authenticating an OAuth client.
Same shape and TTL as POST /attestations/challenge, but tracked in a separate namespace — a challenge from one endpoint is never valid at the other, even if the same string were somehow issued by both.
{- "challenge": "string"
}Same self-attested PoP shape as POST /attestations, but jwk here
is the credential-holder key a future credential will be bound to
— allowed to be, and by ARF's own design usually should be, a
different key from the Client Attestation's wallet-instance key.
| jwk required | object Public JWK of the key being attested — the wallet-instance key for POST /attestations, the credential-holder key for POST /key-attestations. |
| challenge required | string From a prior call to the matching /challenge endpoint above. |
| pop required | string Compact JWS over |
{- "jwk": { },
- "challenge": "string",
- "pop": "string"
}{- "key_attestation_jwt": "string"
}