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

Issuing Credentials and Interacting With Wallets

Issuing a credential means creating a credential offer for one or more credential configurations and letting a recipient claim it into their wallet. The platform implements the OpenID4VCI pre-authorized code flow: the wallet scans a QR code (or follows an openid-credential-offer:// deep link), optionally enters a PIN, and the issuer signs and returns the credential in the configured format (SD-JWT VC or JWT-VC-JSON). This page walks through the full flow against the live API.

All endpoints below require a valid Bearer token, sent as Authorization: Bearer <token> (see Authentication).


1. Prerequisites

Before you can create a credential offer you need:

  • An issuer DID — a did:web or did:key created through the agent API and hosted/served by the service.

  • A registered issuer — bound to that DID (POST /agent/issuer).

  • At least one schema assigned to the issuer — assigning a schema produces one or more credential configurations.

A schema combined with a format yields a credential configuration id of the form Name@Version:format, for example [email protected]:sd-jwt. Credential offers reference these configuration ids. See the schema documentation for how to create and assign schemas.

In the examples below, replace :issuerDid with your issuer's DID (e.g. did:web:issuer.example.com:1f2e... or did:key:z6Mk...).


2. Create a Credential Offer

POST /issuers/:issuerDid/credential-offers
Authorization: Bearer <token>
Content-Type: application/json

Request Body

  • credentialConfigurationIds (array of strings, required): One or more credential configuration ids to include in the offer. At least one is required. Example: ["[email protected]:sd-jwt"]

  • requirePin (boolean, optional): When true, the pre-authorized code is protected by a PIN (transaction code) that the holder must enter in their wallet. Defaults to false.

  • issuanceMetadata (object, optional): Metadata passed to the credential mapper. Use claimsByConfigurationId to supply the claim values for each configuration in the offer.

Example Request

Response Body

  • credentialOfferUri (string): An openid-credential-offer:// URI. Render it as a QR code or present it as a deep link so a wallet can start the issuance flow.

  • issuanceSessionId (string): The identifier of the issuance session created for this offer. Use it to track issuance progress (see Track Issuance Sessions).

  • credentialConfigurationIds (array of strings): The credential configuration ids included in the offer.

  • userPin (string, optional): Returned only when requirePin is true. Deliver this PIN to the holder out-of-band; they must enter it in their wallet to claim the credential.


3. Generate a QR Code

To render the offer as a scannable image without building your own QR encoder, call the QR-code variant. It accepts the same request body as the offer endpoint and returns the QR directly as a PNG.

The response is an image/png body (a 400×400 QR encoding the credentialOfferUri). The offer details are returned in response headers:

  • X-Credential-Offer-Uri — the openid-credential-offer:// URI encoded in the QR.

  • X-Issuance-Session-Id — the issuance session id for this offer.

  • X-Pin — the PIN, present only when requirePin was true.

You can also regenerate a QR for an existing session at any time:

This returns an image/png for the session's offer URI, with the X-Credential-Offer-Uri header set.

Rendered credential-offer QR code
A rendered credential-offer QR code in the operator console

4. Wallet Claims the Credential

Once the offer is presented, the recipient claims it with an SSI-compatible wallet:

  1. The wallet scans the QR code or opens the openid-credential-offer:// link.

  2. It reads the credential offer and the issuer metadata, then runs the OpenID4VCI pre-authorized code flow against the issuer's authorization server.

  3. If the offer required a PIN, the wallet prompts the holder for the PIN (the value returned as userPin / the X-Pin header).

  4. The issuer signs the credential for the requested configuration and returns it to the wallet in the configured format — SD-JWT VC or JWT-VC-JSON.

  5. The wallet stores the credential for later presentation to verifiers.

The wallet handles token exchange and credential retrieval internally as part of the OpenID4VCI flow; there is no separate client-side token endpoint to call. From the issuer's side, you only create the offer and (optionally) track the session.

Two things happen automatically at claim time:

  • The service injects an authorized_user claim (authorizedUser in the JWT-VC-JSON credential subject) into every issued credential payload, set from the sub of the wallet's access token, so each credential records which authenticated user claimed it.

  • For custom schemas, the claim values are validated against the schema: required claims must be present and each claim must match its declared type. A validation failure aborts issuance and moves the session to the Error state.

The platform also provides a cloud (server-side) wallet. A holder tenant can claim an offer programmatically by posting the offer URI to POST /holders/:holderId/process with body { "uri": "openid-credential-offer://..." }. See the holder/wallet documentation for details.


5. Track Issuance Sessions

Each offer creates an issuance session whose state advances as the wallet completes the flow.

List sessions for an issuer

Get a single session

Response Body

  • id (string): The session identifier (the issuanceSessionId returned at offer creation).

  • issuerId (string): The issuer DID associated with the session.

  • state (string): The current state of the issuance session (e.g. OfferCreated, Completed).

  • credentialOfferUri (string): The offer URI tied to the session.

  • createdAt / expiresAt (string): ISO 8601 timestamps for creation and expiry.

  • userPin (string, optional): The PIN, when the session used a PIN-protected pre-authorized code.

  • errorMessage (string, optional): Present only when the session ended in the Error state; omitted otherwise.

  • issuedCredentials (array of strings): The credential configuration ids issued so far in this session (e.g. [email protected]:sd-jwt). Empty until credentials are issued; fully populated once the session reaches Completed.


6. Direct Signing (optional)

When you need a signed credential without the offer/wallet flow — for example to embed a credential in another system or to issue server-to-server — sign it directly. The issuer signs the payload with its DID's key and returns the credential in compact serialized form.

Request Body

  • format (string, required): One of sd-jwt-vc or jwt_vc_json.

  • payload (object, required): The credential claims. For SD-JWT VC, include a vct (verifiable credential type) value.

  • subjectDid (string, optional): The DID of the credential subject, used as the subject identifier in the credential.

  • disclosureFrame (object, optional): SD-JWT only. An object with an _sd array listing the claim keys that should be selectively disclosable.

Example Request

Response Body

  • credential (string): The signed credential in compact serialized form (a compact JWT for jwt_vc_json, or an SD-JWT with appended disclosures for sd-jwt-vc).

  • format (string): The canonical credential format identifier of the signed credential. This is not the request enum value — it is the format the credential is actually issued in: dc+sd-jwt for an SD-JWT VC (request format: "sd-jwt-vc"), and jwt_vc for a JWT-VC-JSON credential (request format: "jwt_vc_json").


That is the whole flow: create an offer, present it as a QR or deep link, and let the wallet claim it — tracking progress through the issuance session. For one-off, non-interactive issuance, use the direct signing endpoint.

Last updated