Issuer
The Issuer Service is a foundational component of the Self-Sovereign Identity (SSI) ecosystem, enabling the creation, issuance, and management of Verifiable Credentials (VCs). A single service instance can register multiple issuers, each bound to its own DID; issuers define credential schemas, sign credentials, and hand them to interoperable wallets using the OpenID4VCI protocol over a standards-based HTTP API.
Key Highlights:
Standards-Based Verifiable Credentials Issues W3C-aligned credentials in two formats: SD-JWT VC (
vc+sd-jwt/sd-jwt-vc), which supports selective disclosure so holders reveal only the claims a verifier needs, and JWT-VC-JSON (jwt-vc-json/jwt_vc_json). Credentials are cryptographically signed with Ed25519 (EdDSA) keys held in the service's internal key-management system.Schema Management and Versioning Credential schemas are JSON-Schema templates that define the claims a credential carries, ensuring consistent credential structures. Each schema has a name (e.g.
EmployeeBadge) and a string version (e.g.1.0), and a schema together with a format yields a credential configuration id of the formName@Version:format(e.g.[email protected]:sd-jwt). New versions can be created without disturbing existing ones, and a schema is assigned to an issuer before it can be offered.OpenID4VCI Credential Offerings An issuer creates a credential offer for one or more credential configurations. The offer is returned as an
openid-credential-offer://URI and can be rendered as a QR code for the recipient to scan. Offers are claimed by a wallet through the OpenID4VCI pre-authorized code flow, with an optional PIN (transaction code) that the recipient must enter to bind the claim to a person who has the PIN.Secure, Standards-Based Wallet Interactions Wallets retrieve credentials using the OpenID4VCI protocol against the issuer's published metadata, exchanging the pre-authorized code (and PIN, if required) for the signed credential. Every issuance is tracked as an issuance session so operators can observe its state.
Direct Credential Signing Alongside the wallet-based flow, the service exposes a direct signing endpoint that returns a signed credential in compact serialized form without a wallet round-trip — useful for server-to-server integrations and testing.
Architecture Overview
Issuer Service Instance Runs as a standalone server backed by PostgreSQL. Each issuer is registered against an existing DID and exposes its own OpenID4VCI issuer metadata. Issuers share the instance's schema store and database: a schema assigned to one issuer becomes available as credential configurations built from all active schemas.
Decentralized Identifiers The service creates and serves
did:webdocuments itself over HTTPS (resolvable at/:uuid/did.json) anddid:keyidentifiers, using Ed25519 keys (see Terminology and Concepts for how this relates todid:empe).Credential Issuance and Storage Defines schemas, creates offers, signs credentials, and tracks issuance sessions.
Wallet Interaction Layer Implements QR code–based offers and the OpenID4VCI pre-authorized code flow (with optional PIN) for secure credential claiming.
Security and Access Controls All operator endpoints require a valid OIDC JWT Bearer token (
Authorization: Bearer <token>), validated against a JWKS endpoint. HTTPS is required in production.
Issuing a Credential
The typical issuer workflow is: create a schema, assign it to an issuer, create an offer, then let a wallet claim it. All requests below require a valid Authorization: Bearer <token> header.
1. Create a Schema
POST /schemas
Request Body:
name / version: Identify the schema. Both are token-like (letters, digits,
.,_,-; no spaces) and together form[email protected].formats: Which credential formats this schema supports — any of
sd-jwtandjwt-vc-json(defaults to both).schema: A JSON-Schema describing the credential claims.
vct: Optional VCT identifier used for SD-JWT credential configurations.
jwtVcTypes: Optional credential
typevalues used for JWT-VC-JSON configurations.disclosureFrame: Optional list of claim keys that are selectively disclosable in SD-JWT.
defaultValues / display: Optional default claim values applied during issuance and display metadata for wallet UI.
2. Assign the Schema to an Issuer
POST /issuers/:issuerDid/schemas
Request Body:
Response Body:
3. Create a Credential Offer
POST /issuers/:issuerDid/credential-offers
Request Body:
credentialConfigurationIds: One or more credential configurations to offer, each of the form
Name@Version:format.requirePin: When
true, a PIN (transaction code) is generated and must be entered by the recipient. Defaults tofalse.issuanceMetadata: Optional per-configuration claim values applied during issuance.
Response Body:
credentialOfferUri: The
openid-credential-offer://URI the wallet uses to claim the credential.issuanceSessionId: Identifier for tracking the issuance session.
credentialConfigurationIds: The credential configurations included in the offer.
userPin: Present only when
requirePinistrue.
To obtain a scannable QR code instead of JSON, call POST /issuers/:issuerDid/credential-offers/qr-code with the same body. It returns a PNG image and exposes the offer details in the X-Credential-Offer-Uri, X-Issuance-Session-Id, and (when a PIN is required) X-Pin response headers.
4. Track the Issuance Session
GET /issuance-sessions/:sessionId
Returns the session state and offer details. List all sessions for an issuer with GET /issuance-sessions?issuerId=<id>, or fetch a QR image for the session's offer URI with GET /issuance-sessions/:sessionId/qr-code.
Direct Credential Signing
For server-to-server use, a credential can be signed directly without the wallet flow.
POST /issuers/:issuerDid/credentials/sign
Request Body:
format:
sd-jwt-vcorjwt_vc_json. This is the input value; note that the response echoes the underlying claim-format identifier, not this value (see below).payload: The credential claims. For SD-JWT include a
vct.subjectDid: Optional DID of the credential subject.
disclosureFrame: SD-JWT only; lists which claims are selectively disclosable. Supplying it with
jwt_vc_jsonis rejected.
Response Body:
The response format field reports the underlying claim-format identifier of the signed credential, which differs from the format value sent in the request. Signing with sd-jwt-vc returns "format": "dc+sd-jwt", and signing with jwt_vc_json returns "format": "jwt_vc".
Authentication
All operator endpoints are protected. Requests must include a valid OIDC-issued JWT in the Authorization header:
Tokens are validated against a JWKS endpoint (OIDC_JWKS_URL). During local development, authentication can be bypassed by setting AUTH_DISABLED=true.

Last updated