API Reference
Complete endpoint reference for the Issuance API
POST /oid4vci/offers
Create a credential offer. Returns a URI that the holder's wallet scans to initiate the issuance flow.
Authentication: Secret API key
Request body
{
"templateId": "550e8400-e29b-41d4-a716-446655440000",
"claims": {
"given_name": "Alice",
"family_name": "Smith",
"birth_date": "1990-01-15"
}
}| Field | Type | Required | Description |
|---|---|---|---|
templateId | string (UUID) | Yes | Credential template ID |
claims | object | No | Claim values to include in the credential |
requireTxCode | boolean | No | Require a transaction code (PIN) |
useAuthorizationCode | boolean | No | Include authorization code grant alongside pre-auth |
offerScheme | string | No | Scheme of the returned offer URI: openid-credential-offer (default), haip-vci or eu-eaa-offer (EUDI wallets, ETSI TS 119 472-3) |
isDeferred | boolean | No | Enable deferred issuance |
deferredSeconds | number | No | Seconds until credential is ready (1–86400, default: 30) |
requirePresentation | boolean | No | Require a verifiable presentation before issuance |
presentationRequirements | object | No | DCQL or PEX query for the required presentation |
dataSourceId | string (UUID) | No | Data source for auto-populating claims |
dataSourceParams | object | No | Lookup parameters for the data source |
Response 200
{
"offer_id": "abc123",
"credential_offer": {
"credential_issuer": "https://api.verifiables.com",
"credential_configuration_ids": ["eu.europa.ec.eudi.pid.1"],
"grants": {
"urn:ietf:params:oauth:grant-type:pre-authorized_code": {
"pre-authorized_code": "oaKazRN8I0IbtZ0C7JuMn5",
"tx_code": {
"input_mode": "numeric",
"length": 6,
"description": "Enter the code"
}
}
}
},
"credential_offer_uri": "openid-credential-offer://?credential_offer_uri=https%3A%2F%2F...",
"tx_code": "847291"
}| Field | Type | Description |
|---|---|---|
offer_id | string | Unique offer identifier |
credential_offer | object | OID4VCI credential offer object |
credential_offer_uri | string | URI for the wallet (display as QR code or deep link) |
tx_code | string | undefined | Transaction code to communicate to the holder out-of-band |
POST /templates
Create or update a credential template. Upserts by type: if a template with the same type exists, it is updated.
Authentication: Secret API key
Request body
{
"type": "eu.europa.ec.eudi.pid.1",
"format": "mso_mdoc",
"title": "EU Personal ID",
"description": "European Union Person Identification Data",
"claims": [
{ "name": "family_name", "type": "string", "mandatory": true },
{ "name": "given_name", "type": "string", "mandatory": true },
{ "name": "birth_date", "type": "string", "mandatory": true },
{ "name": "age_over_18", "type": "boolean" }
]
}| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Credential type identifier (e.g., eu.europa.ec.eudi.pid.1) |
format | "mso_mdoc" | "dc+sd-jwt" | Yes | Credential format |
title | string | No | Display name |
description | string | No | Human-readable description |
claims | ClaimDefinition[] | No | Claim schema definitions |
schema | object | No | JSON Schema for claim validation |
bindingMethods | object | No | Cryptographic binding methods configuration |
algorithms | object | No | Signing algorithm configuration |
proofTypes | object | No | Proof type requirements |
Response 200
Returns the created or updated template object.
GET /templates
List all credential templates.
Authentication: API key (publishable or secret)
Response 200
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"type": "eu.europa.ec.eudi.pid.1",
"format": "mso_mdoc",
"title": "EU Personal ID",
"description": "European Union Person Identification Data",
"claims": [
{ "name": "family_name", "type": "string", "mandatory": true }
]
}
]GET /template/:id
Get a single template by ID.
Authentication: API key (publishable or secret)
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string (UUID) | Template ID |
Response 200
Returns the template object.
POST /type
Find templates by credential type.
Authentication: API key (publishable or secret)
Request body
{
"type": "eu.europa.ec.eudi.pid.1"
}Response 200
Returns an array of matching template objects.
Protocol endpoints
These endpoints are part of the OID4VCI protocol and are called by wallets. You do not call them directly; you only need POST /oid4vci/offers to create offers.
POST /oid4vci/par
Pushed Authorization Request. The wallet sends authorization parameters here instead of to the authorization endpoint directly.
| Parameter | Type | Required | Description |
|---|---|---|---|
response_type | string | Yes | Must be "code" |
client_id | string | Yes | Wallet client identifier |
code_challenge | string | Yes | PKCE S256 code challenge (43–128 chars) |
code_challenge_method | string | Yes | Must be "S256" |
scope | string | No | Requested credential scope |
issuer_state | string | No | Links to a pre-existing offer |
redirect_uri | string | No | Redirect URI for the authorization response |
client_assertion_type | string | No | Wallet attestation type |
client_assertion | string | No | Wallet attestation JWT |
Response 201
{
"request_uri": "urn:ietf:params:oauth:request_uri:abc123",
"expires_in": 60
}GET /oid4vci/authorize
Authorization endpoint. The wallet navigates here with the PAR request URI.
| Parameter | Type | Required | Description |
|---|---|---|---|
request_uri | string | Yes | Request URI from PAR response |
action | "approve" | "deny" | No | User action (default: "approve") |
Returns an authorization code as JSON { "code": "..." } or redirects to redirect_uri?code=...&state=....
POST /oid4vci/token
Token endpoint. Supports two grant types:
Pre-authorized code:
| Parameter | Type | Required | Description |
|---|---|---|---|
grant_type | string | Yes | urn:ietf:params:oauth:grant-type:pre-authorized_code |
pre-authorized_code | string | Yes | Code from the credential offer |
tx_code | string | Conditional | Transaction code if required by the offer |
Authorization code (PKCE):
| Parameter | Type | Required | Description |
|---|---|---|---|
grant_type | string | Yes | authorization_code |
code | string | Yes | Authorization code from /authorize |
code_verifier | string | Yes | PKCE code verifier |
Both grant types accept optional client_assertion_type, client_assertion, and DPoP header.
Response 200
{
"access_token": "eyJ...",
"token_type": "Bearer",
"expires_in": 3600
}token_type is DPoP when the request carries a DPoP proof header, otherwise Bearer.
The token response does not contain a nonce (OID4VCI 1.0). Wallets fetch the proof nonce from POST /oid4vci/nonce before building the credential request.
POST /oid4vci/credential
Credential endpoint. The wallet requests the signed credential using the access token.
| Field | Type | Required | Description |
|---|---|---|---|
credential_configuration_id | string | No | Credential type ID |
format | "mso_mdoc" | "dc+sd-jwt" | No | Credential format |
doctype | string | No | Document type (mDoc) |
vct | string | No | Verifiable credential type (SD-JWT) |
proof | object | No | Single proof of possession |
proof.proof_type | "jwt" | "attestation" | Yes | Proof type |
proof.jwt | string | Conditional | JWS proof JWT |
proof.attestation | string | Conditional | Key attestation |
proofs | object | No | Batch proofs: { "jwt": [...] } or { "attestation": [...] }. One credential is issued per proof, all bound to the same claims |
Response 200
{
"credentials": [
{ "credential": "..." }
],
"c_nonce": "new_nonce_456",
"c_nonce_expires_in": 300
}The primary response shape is the credentials array (one entry per proof); wallets should read credentials[0].credential. For backward compatibility with pre-1.0 wallets, a deprecated top-level credential field is also present when exactly one credential is issued (and it is the only field returned for legacy single-proof requests). New integrations must not rely on it.
Unlike the token response, the credential response still includes c_nonce and c_nonce_expires_in for the next proof.
Response 202 (deferred)
{
"transaction_id": "txn_abc",
"c_nonce": "nonce_789",
"c_nonce_expires_in": 300
}POST /oid4vci/deferred
Check status of a deferred credential.
| Field | Type | Required | Description |
|---|---|---|---|
transaction_id | string | Yes | Transaction ID from deferred response |
Returns 200 with the credential when ready, or 202 with { "transaction_id", "interval": 5 } if still pending.
POST /oid4vci/nonce
Get a fresh nonce for credential requests. This is where wallets obtain the c_nonce to embed in their proof JWT (the token response no longer carries one).
Response 200
{
"c_nonce": "fresh_nonce"
}The response contains only c_nonce. There is no c_nonce_expires_in field; expiry is managed server-side.
Discovery endpoints
| Method | Path | Description |
|---|---|---|
GET | /.well-known/openid-credential-issuer | Credential issuer metadata |
GET | /.well-known/oauth-authorization-server | OAuth authorization server metadata |
GET | /oid4vci/offers/:id | Retrieve a credential offer by ID |
GET | /oid4vci/offers/:id/status | Get the offer status, returns { "status": "..." } |
Schema objects
ClaimDefinition
{
"name": "family_name",
"type": "string",
"mandatory": true,
"sd": true
}| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Claim name |
type | string | Yes | Data type (string, boolean, number, bytes) |
mandatory | boolean | No | Whether the claim must be present |
sd | boolean | No | Selectively disclosable (SD-JWT only) |