For the complete documentation index, see llms.txt. This page is also available as Markdown.

FAQ

Short answers to common questions about the Wallet Server. For full details, see the linked pages elsewhere in this section.

Why use a server-side cloud wallet when mobile wallets exist?

Automation. A cloud wallet lets CI pipelines, backend scripts, and AI agents run the same OpenID4VCI issuance and OpenID4VP presentation flows that a person would run in a phone wallet — without a human scanning QR codes. The flows stay standards-compliant, so a credential claimed or presented by a server-side holder is interoperable with mobile wallets. Use a mobile wallet when an end user holds their own credentials on their device; use a server-side holder when a service or agent needs to hold and use credentials programmatically.

How does multi-tenancy work?

Each cloud wallet is a holder — a per-user tenant identified by a holderId. A holder is created for an authenticated OIDC user via POST /agent/holder, and that user can list their own holders with GET /agent/holders. Every holder operation lives under /holders/:holderId and is guarded by ownership: a user can only process URIs and read credentials for holders they created. Requests to these routes require a valid Bearer token with the admin role.

Authorization: Bearer <token>

How does a holder get a credential?

By processing a credential-offer URI — a single call:

POST /holders/:holderId/process

{ "uri": "openid-credential-offer://?credential_offer_uri=..." }

The holder resolves the offer, requests the offered credential(s), binds them to the holder's DID, and stores them. The response reports how many were stored:

{ "status": "success", "claimed": 1, "ids": ["<record-id>"] }

Credentials are never uploaded as raw JSON; they are always acquired through the offer flow.

How does the wallet present a credential?

You pass the verifier's authorization-request URI to the same /holders/:holderId/process endpoint:

There is no separate confirmation step and you do not pass the credential yourself. The holder resolves the request, automatically selects a matching credential from its store (using DIF Presentation Exchange or DCQL, whichever the request uses), and sends the presentation:

The endpoint dispatches on the URI scheme, so openid-credential-offer:// claims a credential while openid4vp:// (or an https:// authorization request) presents one.

How do I list a holder's stored credentials?

Two endpoints, both scoped to one holder:

  • GET /holders/:holderId/credentials — raw, encoded credentials: [{ id, format, credential }].

  • GET /holders/:holderId/credentials/decoded — decoded payloads: [{ id, format, decoded, error? }].

Both endpoints return every credential the holder currently stores; there is no filtering.

Which DID methods and credential formats are supported?

A holder is provisioned with a did:key (a self-contained Ed25519 identifier, no hosting required) or a did:web (whose document the service hosts over HTTPS). The supported credential formats are SD-JWT VC (sd-jwt-vc / vc+sd-jwt) and JWT-VC-JSON (jwt_vc_json). A holder can additionally store JSON-LD VC, W3C v2 (vc+jwt / vc+sd-jwt), and mdoc credentials it receives. (When listing stored credentials, the holder reports SD-JWT VCs with the media type dc+sd-jwt.)

Can I use did:web without owning a domain?

For production you must control the domain you supply, because the DID document is published under that hostname and other parties resolve it there. The identifier is built as did:web:<domain>:<uuid> and served at /<uuid>/did.json (also at /<uuid>/.well-known/did.json), so the DID encodes the host you choose. The host comes strictly from the domain field of the holder-creation request — you must supply it, and creating a did:web holder without it fails. For local testing you can point a hostname at the service (for example via your hosts file or port forwarding) and pass that hostname as the domain field when you create the holder (POST /agent/holder with { "method": "web", "domain": "<your-host>" }); the DID will encode that host. If you do not control any domain, create the holder with method: "key" instead — did:key needs no hosting.

Does the service create did:empe or anchor DIDs on a blockchain?

No. The EMPE DID method (did:empe) and the EMPE blockchain are separate parts of the wider Empeiria ecosystem. The Wallet Server does not create or anchor did:empe identifiers; its holders use did:key and did:web, and the service serves their DID documents itself over HTTPS.

Last updated