Issuing Credentials and Interacting With Wallets
Issuing credentials involves creating an offering and enabling recipients to claim them via their wallets. Wallets, in turn, authenticate, exchange tokens, and retrieve Verifiable Credentials, storing them locally for later presentation.
1. Create Offering
Request Body
credential_type
(string, required): The type of credential to be issued.Example:"ProofOfPurchase"
credential_subject
(object, required): A JSON object containing subject data.Example:recipient
(string, optional): The DID of the recipient. If provided, this will create a Targeted Offering and require proof of DID ownership. If omitted, this will create an Open Offering (any wallet scanning the QR code can claim it).Example:"did:empe:testnet:123456789abcdef"
redemption_limit
(integer, optional): Maximum number of times this offering may be redeemed. Must be ⼠1.Example:1
expires_at
(string, optional): ISO 8601 timestamp (in UTC) after which this offering cannot be redeemed.Example:"2025-05-09T13:14:55.000Z"
Examples
Targeted Offering
Open Offering
Response
On success, the API responds with HTTP 200 and a JSON body containing:
All fields of the newly created
Offering
entity, andThree additional URLs/identifiers to facilitate QRâcode rendering and direct link access:
Field Descriptions
id
(string): The UUID of the newly created offering (matchesoffering_id
).credential_issuer
(string): The DID of the issuer that will sign/issue the credential.credential_configuration_ids
(array of strings): List of one or more configuration IDs that determine how the credential is generated (e.g., template IDs).display
(object):name
(string): Humanâreadable name for this offering (e.g., "Summer Concert Pass").locale
(string): Locale code used for display purposes (e.g.,"en-US"
).
grants
(object): Authorization details; typically contains anauthorization_code
object with anissuer_state
field for the OIDC4VCI flow.credential_subject
(object): The same JSON object provided in the request, representing any payload data that the credential will carry.recipient
(string or null): If this was a Targeted Offering, the recipient DID. If omitted at creation, comes back asnull
.redemption_limit
(integer or null): If a limit was specified, that integer. Otherwisenull
.expires_at
(string or null): ISO 8601 UTC timestamp when this offering expires. If no expiration was set, this isnull
.redemptions
(array): List of redemption records (empty immediately after creation). Each element has the shape of theOfferingRedemption
entity (not shown here).qr_code_url
(string): A URL to fetch a dynamically generated QR code. Any client (web/mobile) should render that image so that endâusers can scan it with a compatible wallet.offering_id
(string): Repeat of theid
field (useful for clarity in client code).offering_full_url
(string): A direct link to the publicâfacing offering page (can embed a widget or landing page to start the claim/issuance flow).
Notes:
The
CreateOfferingRequestDto
validations ensure thatcredential_type
is a nonâempty string, thatcredential_subject
is a nonâempty object, and that optional fields (likerecipient
,redemption_limit
, andexpires_at
) adhere to their respective formats.If you pass
recipient
, the resulting offering will require the wallet to prove ownership of that DID before claiming. If you omitrecipient
, any wallet scanning the QR code (atqr_code_url
) can claim the credential.Clients should render the QR code at
qr_code_url
(for example, embedding it in an<img>
tag or displaying it in a mobile app).offering_full_url
can be used to embed into email campaigns or landing pages so that users can click directly instead of scanning a code.The
redemptions
array will remain empty until someone claims/redeems the offering; thereafter, each redemption record will be populated under that array.
2. QR Code Generation
The client application retrieves
qr_code_url
and generates a corresponding QR code.End users scan the QR code with their SSI-compatible wallet.
3. Wallet Scans & Authorization
3.1. Retrieve Offering Details
When a wallet follows the QR code, it calls:
If this is an Open Offering, the wallet proceeds directly to token exchange.
If this is a Targeted Offering, the wallet must prove DID ownership via Verifiable Presentation.
3.2. Authorization Code Flow (Targeted Offering Only)
Create Authorization Request
Request Body
presentation_definition
(object, required): The Presentation Definition that specifies which credentials the holder must present. Must conform to the Presentation Definition schema.state
(string, required): Random string (32â64 characters, alphanumeric, hyphens, underscores) to prevent CSRF attacks.nonce
(string, required): Random string (32â64 characters, alphanumeric, hyphens, underscores) to ensure request uniqueness and prevent replay attacks.
Example Request:
Response Body
On success, HTTP 200 with a JSON payload matching the AuthorizationRequestResponseDto
:
id
(string, UUID): Unique identifier for the newly created authorization request.presentation_definition
(object): Echoes the Presentation Definition supplied, indicating exactly which credentials must be presented.state
(string, 32â64 chars): The CSRFâprotection string provided by the client. Must be stored on the client side and matched during validation.nonce
(string, 32â64 chars): The uniqueness string provided by the client. Should be embedded in the Verifiable Presentation to prevent replay.expires_at
(string, ISO 8601 dateâtime): Timestamp after which this authorization request is no longer valid (e.g.,"2025-06-12T14:30:00Z"
).code
(string or null): The authorization code that will be returned once the Verifiable Presentation has been successfully validated. Initiallynull
.holder_did
(string or null): The holderâs DID once they have presented valid credentials. Initiallynull
.used
(boolean): Indicates whether the authorization request has already been used to issue a code. Initiallyfalse
.
Submit Verifiable Presentation & Validate
Replace {authorization_id}
with the UUID returned from step 1.
Request Body
presentation_submission
(object, required): Maps the holderâs credentials to thepresentation_definition
from step 1. Must conform to the Presentation Submission schema.vp_token
(object, required): The Verifiable Presentation (VP) itselfâeither a JWT or JSON-LD objectâthat contains one or more credentials.
Example Request:
Response Body On successful validation, HTTP 200 with:
code
(string): The one-time authorization code that the holder will exchange for a credential or access token in the next step of the OIDC4VCI flow.
If validation fails (e.g., missing required credential, invalid presentation structure, or nonce
/state
mismatch), the server will respond with an appropriate 4xx error and an explanatory message.
4. Token Exchange
Request Body
authorization_code
(string, required): The one-time code obtained from the previous authorization step.Must be alphanumeric (letters, digits), hyphens or underscores only.
Length between 32 and 128 characters.
Cannot be empty.
Example Request:
Response Body On success, HTTP 200 with:
access_token
(string): A JWT that the client (wallet) uses to authenticate subsequent API calls.token_type
(string): Always set to"Bearer"
.expires_in
(number): The lifetime of the access token in seconds (e.g.,3600
means it expires in 1 hour).
5. Credential Retrieval
Using the access_token
obtained in step 4, the wallet retrieves the issued Verifiable Credential by its credential_id
:
Replace {credential_id}
with the UUID of the issued credential you want to fetch.
Example Response
id
(string): The unique identifier (UUID) of the issued credential.type
(string): The type of credential that was minted (e.g.,"ProofOfPurchase"
).issuer
(string): The DID of the issuer that signed the Verifiable Credential.recipient
(string): The DID of the credential recipient (holder).credentialSubject
(object): The same subject data that was passed when creating the offeringâcontains fields liketicket
,seat
,description
, etc.vc
(object): The complete Verifiable Credential JSON-LD object, including:@context
(array of URIs)id
(URN or same asid
field)type
(array, e.g.,["VerifiableCredential", "ProofOfPurchase"]
)issuer
(DID)issuanceDate
(ISO 8601 timestamp)credentialSubject
(nested object with holder DID and subject fields)proof
(cryptographic proof, e.g., a JWT or JSON-LD proof)
6. Credential Storage & Presentation
Once received, the wallet stores the Verifiable Credential locally.
The user can later present this credential to verifiers as needed.
By following this combined flowâusing the endpoints exactly as specifiedâissuers can support both Targeted and Open offerings. Wallets authenticate, exchange tokens, retrieve credentials, and store them locally, ensuring a secure, user-friendly SSI experience.
Last updated