> For the complete documentation index, see [llms.txt](https://docs.empe.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.empe.io/develop/wallet-server.md).

# Wallet Server

The Wallet Server is a server-side, multi-tenant cloud wallet built into the credential platform's HTTP API. Holder and wallet functionality is a module of the same backend that provides the Issuer and Verifier capabilities, addressed under `/agent/holder` and `/holders/:holderId/*`.

It is designed for:

* Running automated tests and integration pipelines without a mobile wallet
* Powering AI agents that need to act as a holder programmatically
* Simulating realistic wallet interactions with issuers and verifiers

It speaks the same **OpenID4VCI** (issuance) and **OpenID4VP** (presentation) credential protocols a mobile wallet uses, so you can drive the same claim and presentation flows from scripts, CI, or agent frameworks — but as plain HTTP calls.

## What It's For

Mobile wallets are great for human-in-the-loop flows. For automated testing or autonomous agents, you often need a non-interactive, scriptable holder that behaves like a real one. The cloud wallet fills that gap: you create a holder once, then process credential-offer and presentation-request URIs over HTTP instead of scanning QR codes by hand.

## Key Capabilities

* **Multi-tenant holders** — one wallet per user, created via `POST /agent/holder` and listed via `GET /agent/holders`. Each holder is identified by a `holderId` (a UUID) used in every wallet call.
* **Holder DIDs** — created as `did:key` or `did:web` when the holder is created. The DID is bound to the holder and used as the cryptographic subject when claiming and presenting credentials.
* **Single-call URI processing** — `POST /holders/:holderId/process` accepts either a credential-offer URI (issuance) or an OpenID4VP authorization-request URI (presentation) and acts automatically based on the URI scheme.
* **Credential store** — list raw stored credentials (`GET /holders/:holderId/credentials`) or decoded payloads (`GET /holders/:holderId/credentials/decoded`).
* **Credential formats** — the wallet can store and present SD-JWT VC, JWT-VC-JSON, JSON-LD VC, W3C v2 (`vc+jwt` / `vc+sd-jwt`), and mdoc credentials it receives.

Holders are created with `did:key` or `did:web`; see the [FAQ](/develop/wallet-server/faq.md#does-the-service-create-didempe-or-anchor-dids-on-a-blockchain) for how this relates to `did:empe`.

## Creating a Holder

Create a holder for the authenticated user. Choose `key` for a self-contained `did:key`, or `web` to have a `did:web` document hosted by the service.

**POST** `/agent/holder`

### Request Body

* **method** (string, required) — `key` or `web`.
* **domain** (string) — required when `method` is `web`; a hostname such as `example.com`.
* **label** (string) — optional human-readable name for the wallet.

```bash
curl -X POST https://issuer.example.com/agent/holder \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{ "method": "key", "label": "CI Test Holder" }'
```

### Response Body

* **holderId** (string) — the tenant id used for all subsequent wallet calls.
* **did** (string) — the holder's DID.
* **cryptographicBindingMethodsSupported** (string\[]) — e.g. `["did:key","did:jwk","did:web"]`.
* **credentialSigningAlgValuesSupported** (string\[]) — e.g. `["EdDSA","ES256"]`.
* **didDocument** (object) — present only for `did:web` holders.

```json
{
  "holderId": "594b464b-20bd-495a-9fb6-87328c759629",
  "did": "did:key:z6Mkf...",
  "cryptographicBindingMethodsSupported": ["did:key", "did:jwk", "did:web"],
  "credentialSigningAlgValuesSupported": ["EdDSA", "ES256"]
}
```

List the holders owned by the authenticated user with **GET** `/agent/holders`.

## Processing a URI

A single endpoint handles both issuance and presentation. It inspects the URI scheme and either claims the offered credential(s) into the wallet (`openid-credential-offer://...`) or selects matching credentials and sends a presentation (`openid4vp://...`).

**POST** `/holders/:holderId/process`

### Request Body

* **uri** (string, required) — the credential-offer or authorization-request URI.

```bash
curl -X POST https://issuer.example.com/holders/594b464b-20bd-495a-9fb6-87328c759629/process \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{ "uri": "openid-credential-offer://?credential_offer_uri=..." }'
```

### Response Body

For an issuance URI, claimed credentials are stored and reported:

```json
{ "status": "success", "claimed": 1, "ids": ["<credentialId>"] }
```

For a presentation URI, the wallet selects matching credentials (via DCQL or Presentation Exchange) and submits them:

```json
{ "status": "success", "action": "presentation_sent" }
```

## Listing Credentials

Fetch what the wallet currently holds. Use the raw endpoint for the encoded credential strings/objects, or the decoded endpoint to read the claims directly.

**GET** `/holders/:holderId/credentials`

```json
[
  { "id": "<credentialId>", "format": "dc+sd-jwt", "credential": "eyJ..." }
]
```

**GET** `/holders/:holderId/credentials/decoded`

```json
[
  {
    "id": "<credentialId>",
    "format": "dc+sd-jwt",
    "decoded": {
      "header": { "alg": "EdDSA", "typ": "dc+sd-jwt" },
      "payload": {
        "vct": "https://issuer.example.com/vct/employee-badge",
        "employee_id": "E-1024",
        "full_name": "Ada Lovelace",
        "department": "Engineering"
      },
      "prettyClaims": {
        "vct": "https://issuer.example.com/vct/employee-badge",
        "employee_id": "E-1024",
        "full_name": "Ada Lovelace",
        "department": "Engineering"
      }
    }
  }
]
```

If a credential cannot be decoded, its entry includes an `error` field and a `null` `decoded` value.

## Architecture Overview

* **Holder/tenant management** — create and list per-user holder wallets (`/agent/holder`, `/agent/holders`).
* **URI processing** — single-call issuance and verification handling (`/holders/:holderId/process`).
* **Credential store** — list raw and decoded credentials held by a tenant.
* **DID document hosting** — `did:web` holder documents are served by the service at `GET /:uuid/did.json` (and at `GET /:uuid/.well-known/did.json`).
* **Auth guard** — validates the Bearer token and enforces the `admin` role plus per-holder ownership.

## Multi-Tenancy

A single deployment hosts many logical wallets. Each holder is a separate tenant identified by its `holderId` and is owned by the OIDC user who created it. Isolation is enforced per holder: an ownership check rejects any request for a `holderId` the caller does not own, so a user can only reach their own wallets.

## Authentication

Every wallet endpoint requires a valid OIDC JWT supplied as `Authorization: Bearer <token>`. The token is validated against the configured JWKS endpoint, and the `admin` role (from `realm_access.roles`) is required on both `/agent/*` and `/holders/:holderId/*` routes. For local development, set `AUTH_DISABLED=true` to treat every request as an authenticated admin.

## API Reference

Interactive API documentation is available through Swagger UI at `GET /api-docs`, and the raw OpenAPI specification is served at `GET /api-docs-json`. The platform also exposes a public `GET /` hello endpoint and `GET /version` for basic service checks.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.empe.io/develop/wallet-server.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
