> 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/schema-management.md).

# Schema Management

Credential schemas define the structure and required claims of the credentials an issuer can produce, ensuring consistency and interoperability across wallets and verifiers. Each schema is stored by the service under a stable identifier and, once assigned to an issuer, becomes one or more **credential configurations** that credential offers reference.

All schema endpoints require authentication: send an OIDC JWT in the `Authorization: Bearer <token>` header. These endpoints validate the token but do not enforce a specific realm role, so any authenticated caller may use them (see [Authentication](/develop/issuer.md#authentication)).

***

## 1. Schema Identifiers and Credential Configurations

A schema is identified by its name and version joined with `@`:

Both `name` and `version` are caller-supplied tokens matching `^[A-Za-z0-9][A-Za-z0-9._-]*$` (no spaces).

Each active schema produces a **credential configuration id** for every format it declares, in the form `<name>@<version>:<format>`:

* `EmployeeBadge@1.0:sd-jwt`
* `EmployeeBadge@1.0:jwt-vc-json`

These configuration ids are what you pass to the credential-offer endpoint. The direct-signing endpoint does not take a configuration id; it instead takes a `format` value (`sd-jwt-vc` or `jwt_vc_json`) plus a `payload`. The available schema formats are `sd-jwt` (SD-JWT VC) and `jwt-vc-json` (JWT-VC-JSON); if `formats` is omitted, both are generated.

***

## 2. Create Schema

```
POST /schemas
Authorization: Bearer <token>
Content-Type: application/json
```

### Request Body

* **`name`** (string, required): Schema name. Token-like (`^[A-Za-z0-9][A-Za-z0-9._-]*$`), max 120 characters. *Example:* `"EmployeeBadge"`
* **`version`** (string, required): Schema version. Token-like, max 50 characters. Supplied explicitly by the caller (it is not auto-incremented). *Example:* `"1.0"`
* **`description`** (string, optional): Human-readable description, max 500 characters. *Example:* `"Employee access badge credential."`
* **`formats`** (array of strings, optional): Credential formats this schema supports. Each value must be `"sd-jwt"` or `"jwt-vc-json"`, and at least one value is required when the field is present. Defaults to `["sd-jwt", "jwt-vc-json"]`.
* **`schema`** (object, required): A JSON Schema describing the credential claims. If a top-level `type` is present it must be (or include) `"object"`; `properties` (if present) must be an object; `required` (if present) must be an array, and every key it lists must exist in `properties`.
* **`vct`** (string, optional): VCT (Verifiable Credential Type) identifier used for SD-JWT credential configurations, max 500 characters. *Example:* `"https://issuer.example.com/vct/employee-badge"`
* **`jwtVcTypes`** (array of strings, optional): Credential `type` values for JWT-VC-JSON configurations. If omitted while a JWT-VC-JSON format is enabled, it defaults to `["VerifiableCredential", "<name>"]`.
* **`defaultValues`** (object, optional): Default claim values applied during issuance when not supplied per offer. *Example:* `{ "company": "EMPE" }`
* **`disclosureFrame`** (array of strings, optional): Claim keys that are selectively disclosable for SD-JWT issuance.
* **`display`** (array of objects, optional): Display metadata used by wallets to render the credential. Each entry requires `name` and may also include `locale`, `description`, `backgroundColor`, and `textColor`.

### Example Request

```json
{
  "name": "EmployeeBadge",
  "version": "1.0",
  "description": "Employee access badge credential.",
  "formats": ["sd-jwt"],
  "vct": "https://issuer.example.com/vct/employee-badge",
  "schema": {
    "type": "object",
    "properties": {
      "employee_id": { "type": "string" },
      "full_name":   { "type": "string" },
      "department":  { "type": "string" },
      "access_level": { "type": "string" }
    },
    "required": ["employee_id", "full_name", "department"]
  },
  "defaultValues": { "company": "EMPE" },
  "disclosureFrame": ["employee_id", "full_name", "department", "access_level"],
  "display": [
    {
      "name": "Employee Badge",
      "description": "Access badge for internal systems.",
      "backgroundColor": "#0f172a",
      "textColor": "#f8fafc"
    }
  ]
}
```

### Response Body

On success the API responds with the stored schema record:

```json
{
  "schemaId": "EmployeeBadge@1.0",
  "name": "EmployeeBadge",
  "version": "1.0",
  "status": "active",
  "schema": {
    "name": "EmployeeBadge",
    "version": "1.0",
    "description": "Employee access badge credential.",
    "formats": ["sd-jwt"],
    "vct": "https://issuer.example.com/vct/employee-badge",
    "schema": {
      "type": "object",
      "properties": {
        "employee_id": { "type": "string" },
        "full_name":   { "type": "string" },
        "department":  { "type": "string" },
        "access_level": { "type": "string" }
      },
      "required": ["employee_id", "full_name", "department"]
    },
    "defaultValues": { "company": "EMPE" },
    "disclosureFrame": ["employee_id", "full_name", "department", "access_level"],
    "display": [
      {
        "name": "Employee Badge",
        "description": "Access badge for internal systems.",
        "backgroundColor": "#0f172a",
        "textColor": "#f8fafc"
      }
    ]
  },
  "createdAt": "2024-01-01T12:00:00.000Z",
  "updatedAt": "2024-01-01T12:00:00.000Z"
}
```

* **`schemaId`** (string): The `name@version` identifier.
* **`name`** (string): The schema name.
* **`version`** (string): The caller-supplied version.
* **`status`** (string): Either `"active"` or `"superseded"` (see Versioning).
* **`schema`** (object): The normalized schema definition, including any defaults the service applied (such as `formats` and `jwtVcTypes`).
* **`createdAt`** / **`updatedAt`** (string): ISO 8601 timestamps.

Creating a schema whose `name@version` already exists is rejected with HTTP 409 Conflict. A malformed `schema` definition is rejected with HTTP 400 Bad Request.

***

## 3. Assign a Schema to an Issuer

Creating a schema stores its definition but does not yet expose it for issuance. To make a schema's credential configurations available on an issuer, assign it. This refreshes the issuer's metadata so it advertises the configurations derived from active schemas.

```
POST /issuers/{issuerDid}/schemas
Authorization: Bearer <token>
Content-Type: application/json
```

### Request Body

* **`schemaId`** (string, required): The schema identifier in `name@version` format. *Example:* `"EmployeeBadge@1.0"`

### Example Request

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

### Response Body

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

After assignment, the issuer exposes `EmployeeBadge@1.0:sd-jwt` (and `EmployeeBadge@1.0:jwt-vc-json` if that format is enabled), which can then be referenced when creating credential offers. Assignment fails with HTTP 404 Not Found if the schema does not exist, and HTTP 410 Gone if the referenced version has been superseded.

***

## 4. List Schemas

```
GET /schemas?name={name}&includeInactive={true|false}
Authorization: Bearer <token>
```

* **`name`** (query, optional): Filter to schemas with this exact name.
* **`includeInactive`** (query, optional): When `true`, superseded schemas are also returned. Defaults to `false` (active only).

Results are returned newest-first by creation time.

### Example Response

```json
[
  {
    "schemaId": "EmployeeBadge@1.0",
    "name": "EmployeeBadge",
    "version": "1.0",
    "status": "active",
    "schema": { "...": "schema definition" },
    "createdAt": "2024-01-01T12:00:00.000Z",
    "updatedAt": "2024-01-01T12:00:00.000Z"
  }
]
```

***

## 5. List Schema Versions

Returns every stored version for a schema name, including superseded ones — useful for auditing or migrating schema changes.

```
GET /schemas/{schemaName}/versions
Authorization: Bearer <token>
```

### Example Response

```json
[
  {
    "schemaId": "EmployeeBadge@1.1",
    "name": "EmployeeBadge",
    "version": "1.1",
    "status": "active",
    "schema": { "...": "schema definition" },
    "createdAt": "2024-02-01T09:00:00.000Z",
    "updatedAt": "2024-02-01T09:00:00.000Z"
  },
  {
    "schemaId": "EmployeeBadge@1.0",
    "name": "EmployeeBadge",
    "version": "1.0",
    "status": "superseded",
    "schema": { "...": "schema definition" },
    "createdAt": "2024-01-01T12:00:00.000Z",
    "updatedAt": "2024-02-01T09:00:00.000Z"
  }
]
```

***

## 6. Get a Schema by ID

```
GET /schemas/{schemaId}?includeInactive={true|false}
Authorization: Bearer <token>
```

* **`schemaId`** (path): The `name@version` identifier, e.g. `EmployeeBadge@1.0`.
* **`includeInactive`** (query, optional): When `true`, a superseded version is still returned. Defaults to `false`.

A missing schema returns HTTP 404 Not Found. A superseded version returns HTTP 410 Gone unless `includeInactive=true`.

***

## 7. Versioning

Versions are **explicit string versions supplied by the caller**, not auto-incrementing integers. To evolve a schema, create a new version under the same name:

* Creating the same `name@version` twice is rejected with HTTP 409 Conflict.
* Creating a **new** version of an existing name automatically marks the previously active version as `superseded`; the new version becomes `active`.
* A superseded version returns HTTP 410 Gone from `GET /schemas/:schemaId` unless `includeInactive=true` is supplied. Assignment (`POST /issuers/:issuerDid/schemas`) always rejects a superseded version with 410 — it accepts no `includeInactive` option. (The versions list always includes superseded entries.)
* Credentials already issued against an older version remain valid — superseding only affects which configurations an issuer advertises going forward. Re-assign the new version to an issuer to advertise its configurations.

***

## 8. Note on Deletion

There is no delete endpoint for schemas. Schemas are retired by creating a newer version that supersedes them, which preserves an auditable history of every definition.

***

## Security

Schema creation, assignment, listing, and retrieval all require a valid OIDC JWT Bearer token; these endpoints require authentication but do not enforce a specific realm role. This ensures only authenticated operators can define and assign credential definitions, preserving the integrity and trustworthiness of what an issuer can issue.


---

# 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/schema-management.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.
