> 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/issuer/issuer-and-credential-configurations.md).

# Issuers and Credential Configurations

An **issuer** is a registered entity, bound to an existing DID, that signs and issues credentials. A **credential configuration** describes exactly what an issuer can issue — the combination of a schema version and a credential format. This page explains how to register an issuer, how schemas and formats produce credential configurations, and how those configurations are referenced when you create credential offers. It also covers listing and inspecting issuers and reading an issuer's OpenID4VCI metadata document.

All `/agent` endpoints require an admin Bearer token (`Authorization: Bearer <token>`), and the token must carry the `admin` role in its `realm_access.roles` or in a `resource_access` client-role entry. The issuer endpoints documented here that are not under `/agent` — `POST /issuers/:issuerDid/schemas` and `POST /issuers/:issuerDid/credential-offers` — require a valid Bearer token but no specific role: they validate the token but do not enforce a specific realm role (see [Authentication](/develop/issuer.md#authentication)).

## Registering an issuer

An issuer is created from a DID that already exists. Create the DID first with `POST /agent/did/web` or `POST /agent/did/key` (see **DID Document Management**), then bind an issuer to it. The supplied DID becomes both the issuer identifier and its signing identity, so the service can sign credentials and issuer-metadata tokens on its behalf. Register issuers against the `did:web` or `did:key` identifiers the service creates itself (see [Terminology and Concepts](/develop/issuer/terminology-and-concepts.md) for the ecosystem's `did:empe` method).

**`POST /agent/issuer`**

**Request Body**:

```json
{
  "did": "did:web:issuer.example.com:5f1c2e88-9a3d-4f2b-8c7a-2d4e6f8a1b3c"
}
```

* **did**: An existing DID that will act as the issuer identifier and signing key. The DID must already exist before this call.

**Response Body**:

```json
{
  "issuerId": "did:web:issuer.example.com:5f1c2e88-9a3d-4f2b-8c7a-2d4e6f8a1b3c",
  "credentialConfigurationsSupported": {}
}
```

* **issuerId**: The issuer identifier, which is the DID you registered.
* **credentialConfigurationsSupported**: A map of the credential configurations this issuer can issue, keyed by credential configuration id. It reflects the credential configurations currently available from active schemas; on a clean deployment with no schemas yet it is empty (`{}`).
* The optional fields **authorizationServerConfigs**, **display**, **dpopSigningAlgValuesSupported**, and **batchCredentialIssuance** are absent from the JSON until configured — they are never returned as empty arrays or `null`.

If active schemas already exist when the issuer is registered, the new issuer is created with those configurations. The next step is to make sure the schemas you want to issue are available, which is what produces credential configurations.

## From schema and format to a credential configuration

A **credential schema** defines the claims of a credential type; it is identified by `Name@Version` (for example `EmployeeBadge@1.0`) and declares one or more formats in its `formats` field (`sd-jwt`, `jwt-vc-json`, or both — see **Schema Management**). Each schema-and-format pair produces a **credential configuration id** of the form `Name@Version:format`:

* `EmployeeBadge@1.0:sd-jwt` — the `EmployeeBadge` version `1.0` schema issued as an SD-JWT VC.
* `EmployeeBadge@1.0:jwt-vc-json` — the same schema issued as a JWT-VC-JSON credential.

A schema that declares both formats therefore yields two credential configurations. Assigning a schema to an issuer refreshes that issuer's metadata so its `credentialConfigurationsSupported` map reflects the configurations of all active schemas — including the one you just assigned — rather than an isolated per-issuer subset.

**`POST /issuers/:issuerDid/schemas`**

* **:issuerDid**: The issuer DID (URL-encode the value if it contains characters such as `:`).

**Request Body**:

```json
{
  "schemaId": "EmployeeBadge@1.0"
}
```

* **schemaId**: The schema identifier in `Name@Version` format. The schema must already exist and be active.

**Response Body**:

```json
{
  "status": "assigned"
}
```

After assignment, the issuer's metadata is refreshed to include a credential configuration entry per supported format of every active schema. For an SD-JWT VC, the entry carries the format, the credential type (`vct`), supported cryptographic binding methods, the signing algorithm, and a `claims` map derived from the schema's properties:

```json
{
  "EmployeeBadge@1.0:sd-jwt": {
    "format": "vc+sd-jwt",
    "vct": "https://issuer.example.com/vct/employee-badge",
    "scope": "openid4vc:credential:EmployeeBadge@1.0",
    "cryptographic_binding_methods_supported": ["jwk", "did:key", "did:jwk", "did:web"],
    "credential_signing_alg_values_supported": ["EdDSA"],
    "proof_types_supported": {
      "jwt": { "proof_signing_alg_values_supported": ["EdDSA"] }
    },
    "claims": {
      "employee_id": { "display": [{ "name": "employee_id" }] },
      "full_name": { "display": [{ "name": "full_name" }] },
      "department": { "display": [{ "name": "department" }] }
    }
  }
}
```

For a JWT-VC-JSON credential, the entry instead carries a `credential_definition` with the credential `type` array:

```json
{
  "EmployeeBadge@1.0:jwt-vc-json": {
    "format": "jwt_vc_json",
    "scope": "openid4vc:credential:EmployeeBadge@1.0",
    "cryptographic_binding_methods_supported": ["did:key", "did:jwk", "did:web"],
    "credential_signing_alg_values_supported": ["EdDSA"],
    "credential_definition": {
      "type": ["VerifiableCredential", "EmploymentCredential"]
    },
    "proof_types_supported": {
      "jwt": { "proof_signing_alg_values_supported": ["EdDSA"] }
    }
  }
}
```

If the SD-JWT schema does not define a `vct`, the service derives one from the schema id under the issuer base URL (`<ISSUER_BASE_URL>/schemas/<encoded-schemaId>`). The credential type array for JWT-VC-JSON comes from the schema's `jwtVcTypes`, falling back to `["VerifiableCredential", "<name>"]`. The `scope` value is always `openid4vc:credential:<Name@Version>`.

## Referencing configurations from credential offers

Credential offers reference credentials by their credential configuration id, never by schema name alone. When you create an offer, list the configuration ids in `credentialConfigurationIds` (see **Issuing Credentials & Interacting with Wallets**):

**`POST /issuers/:issuerDid/credential-offers`**

**Request Body**:

```json
{
  "credentialConfigurationIds": ["EmployeeBadge@1.0:sd-jwt"],
  "requirePin": false,
  "issuanceMetadata": {
    "claimsByConfigurationId": {
      "EmployeeBadge@1.0:sd-jwt": {
        "employee_id": "EMP-001",
        "full_name": "Ada Lovelace",
        "department": "Engineering"
      }
    }
  }
}
```

* **credentialConfigurationIds**: One or more credential configuration ids the wallet may claim, each in `Name@Version:format` form. Every id must be assigned to this issuer.
* **issuanceMetadata.claimsByConfigurationId**: The claim values for each configuration id, supplied per configuration so a single offer can include multiple credentials.

Only configuration ids that appear in the issuer's `credentialConfigurationsSupported` map can be offered. Creating a new version of the same schema name (for example `EmployeeBadge@1.1`) supersedes the old version: after the next metadata refresh the old version's configuration ids are removed from `credentialConfigurationsSupported`, and unclaimed offers referencing them fail at claim time with `Schema <id> is not active.`. Credentials that were already issued remain valid.

## Listing and inspecting issuers

**`GET /agent/issuers`**

Returns all registered issuers.

**Response Body**:

```json
[
  {
    "issuerId": "did:web:issuer.example.com:5f1c2e88-9a3d-4f2b-8c7a-2d4e6f8a1b3c",
    "credentialConfigurationsSupported": {
      "EmployeeBadge@1.0:sd-jwt": { "format": "vc+sd-jwt", "...": "..." }
    }
  }
]
```

**`GET /agent/issuer/:did`**

Returns a single issuer record by issuer DID.

* **:did**: The issuer DID (URL-encode the value if it contains characters such as `:`).

**Response Body**:

```json
{
  "issuerId": "did:web:issuer.example.com:5f1c2e88-9a3d-4f2b-8c7a-2d4e6f8a1b3c",
  "credentialConfigurationsSupported": {
    "EmployeeBadge@1.0:sd-jwt": { "format": "vc+sd-jwt", "...": "..." },
    "EmployeeBadge@1.0:jwt-vc-json": { "format": "jwt_vc_json", "...": "..." }
  }
}
```

* **issuerId**: The issuer DID.
* **credentialConfigurationsSupported**: The credential configurations this issuer can issue, keyed by credential configuration id — these are exactly the ids you reference from credential offers.

## Issuer metadata document

Each registered issuer also exposes an OpenID4VCI issuer metadata document. Wallets read this document during issuance to learn the credential issuer identifier, the credential endpoint, the supported credential configurations, and the authorization server they should obtain an access token from. This is issuer metadata used by wallets — it is not a domain-linkage or trust mechanism.

**`GET /agent/issuer/:did/.well-known/did-configuration`**

* **:did**: The issuer DID (URL-encode the value if it contains characters such as `:`).

The response has a `credentialIssuer` object (the OpenID4VCI Credential Issuer Metadata) and an `authorizationServers` array (the authorization-server metadata wallets use to obtain an access token), alongside `originalDraftVersion`, which reports the OpenID4VCI draft the metadata is shaped for. The endpoint URLs are formed by joining `ISSUER_BASE_URL` with the issuer DID, so each issuer has its own metadata namespace.

**Response Body**:

```json
{
  "originalDraftVersion": "V1",
  "credentialIssuer": {
    "credential_issuer": "https://issuer.example.com/did:web:issuer.example.com:5f1c2e88-9a3d-4f2b-8c7a-2d4e6f8a1b3c",
    "credential_endpoint": "https://issuer.example.com/did:web:issuer.example.com:5f1c2e88-9a3d-4f2b-8c7a-2d4e6f8a1b3c/credential",
    "deferred_credential_endpoint": "https://issuer.example.com/did:web:issuer.example.com:5f1c2e88-9a3d-4f2b-8c7a-2d4e6f8a1b3c/deferred-credential",
    "nonce_endpoint": "https://issuer.example.com/did:web:issuer.example.com:5f1c2e88-9a3d-4f2b-8c7a-2d4e6f8a1b3c/nonce",
    "credential_configurations_supported": {
      "EmployeeBadge@1.0:sd-jwt": {
        "format": "vc+sd-jwt",
        "vct": "https://issuer.example.com/vct/employee-badge",
        "scope": "openid4vc:credential:EmployeeBadge@1.0",
        "cryptographic_binding_methods_supported": ["jwk", "did:key", "did:jwk", "did:web"],
        "credential_signing_alg_values_supported": ["EdDSA"],
        "proof_types_supported": {
          "jwt": { "proof_signing_alg_values_supported": ["EdDSA"] }
        },
        "claims": {
          "employee_id": { "display": [{ "name": "employee_id" }] },
          "full_name": { "display": [{ "name": "full_name" }] },
          "department": { "display": [{ "name": "department" }] }
        }
      }
    }
  },
  "authorizationServers": [
    {
      "issuer": "https://issuer.example.com/did:web:issuer.example.com:5f1c2e88-9a3d-4f2b-8c7a-2d4e6f8a1b3c",
      "token_endpoint": "https://issuer.example.com/did:web:issuer.example.com:5f1c2e88-9a3d-4f2b-8c7a-2d4e6f8a1b3c/token",
      "jwks_uri": "https://issuer.example.com/did:web:issuer.example.com:5f1c2e88-9a3d-4f2b-8c7a-2d4e6f8a1b3c/jwks",
      "pre-authorized_grant_anonymous_access_supported": true,
      "grant_types_supported": [
        "authorization_code",
        "urn:ietf:params:oauth:grant-type:pre-authorized_code"
      ]
    }
  ]
}
```

* **originalDraftVersion**: The OpenID4VCI draft version the metadata document is shaped for.
* **credentialIssuer.credential\_issuer**: The credential issuer identifier — `ISSUER_BASE_URL` joined with the issuer DID.
* **credentialIssuer.credential\_endpoint**: The endpoint a wallet calls to request the credential after exchanging the pre-authorized code.
* **credentialIssuer.credential\_configurations\_supported**: The credential configurations on offer, keyed by credential configuration id. This map mirrors the configurations returned by `GET /agent/issuer/:did`, so wallets and offers reference the same `Name@Version:format` keys.
* **authorizationServers**: The authorization-server metadata, including the `token_endpoint`, `jwks_uri`, and the supported grant types. The OpenID4VCI pre-authorized code grant (`urn:ietf:params:oauth:grant-type:pre-authorized_code`) that backs the offer flow is advertised here.

The response can carry a few additional fields beyond those shown above. Inside `credentialIssuer` it may also include `authorization_servers` (a string array of authorization-server identifiers), `batch_credential_issuance` (batch-issuance parameters), and `display` (issuer-level display metadata). At the top level it can carry `knownCredentialConfigurations` (the same configuration map) and `signedMetadataJwt` (a signed copy of the metadata). The contents of `display` and the value of `signedMetadataJwt` depend on how a given issuer is configured.


---

# 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/issuer/issuer-and-credential-configurations.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.
