Quickstart
Issue and verify your first credential in 5 minutes
This guide walks through issuing a credential and verifying it. The examples run server-side, so they use a secret key throughout (publishable keys are rejected server-side because they require an allow-listed browser Origin header).
1. Create a credential template
Templates define the schema and display for a credential type. Create one using your secret key (template creation is a secret-only endpoint):
POST /templates
Authorization: Bearer oat_your_secret_key
Content-Type: application/json
{
"name": "Employee Badge",
"type": "eu.example.employee.1",
"format": "mso_mdoc",
"doctype": "eu.example.employee.1",
"namespace": "eu.example.employee.1",
"display": {
"name": "Employee Badge",
"description": "Company employee credential",
"background_color": "#1a1a2e",
"text_color": "#ffffff"
},
"attributes": [
{ "name": "given_name", "type": "string", "mandatory": true },
{ "name": "family_name", "type": "string", "mandatory": true },
{ "name": "employee_id", "type": "string", "mandatory": true },
{ "name": "department", "type": "string" }
]
}2. Create a credential offer
Generate an OID4VCI offer for a specific holder. This endpoint accepts publishable or secret keys; from a server, use your secret key:
POST /oid4vci/offers
Authorization: Bearer oat_your_secret_key
Content-Type: application/json
{
"templateId": 1,
"claims": {
"given_name": "Alice",
"family_name": "Smith",
"employee_id": "EMP-001",
"department": "Engineering"
}
}The response includes a credential_offer_uri that the wallet scans:
{
"offer_id": "0d4c7a4e-9c7b-4c33-b5e7-2f6a1c9d8e01",
"credential_offer": {
"credential_issuer": "https://api.verifiables.com",
"credential_configuration_ids": ["eu.example.employee.1"],
"grants": {
"urn:ietf:params:oauth:grant-type:pre-authorized_code": {
"pre-authorized_code": "..."
}
}
},
"credential_offer_uri": "openid-credential-offer://?credential_offer_uri=...",
"tx_code": "1234"
}tx_code is only present when the offer was created with requireTxCode: true.
3. Verify a credential
Create a verification request asking for specific attributes:
POST /verifier/request
Authorization: Bearer oat_your_secret_key
Content-Type: application/json
{
"version": "v1",
"dcql_query": {
"credentials": [{
"id": "employee_badge",
"format": "mso_mdoc",
"meta": { "doctype_value": "eu.example.employee.1" },
"claims": [
{ "path": ["eu.example.employee.1", "given_name"] },
{ "path": ["eu.example.employee.1", "family_name"] },
{ "path": ["eu.example.employee.1", "department"] }
]
}]
}
}The response includes a url for the wallet and an id (a UUID) to poll for results:
{
"url": "mdoc-openid4vp://...",
"id": "7f3e2c1a-0b9d-4e5f-8a6b-1c2d3e4f5a6b",
"version": "v1"
}4. Poll for the result
GET /verifier/status/7f3e2c1a-0b9d-4e5f-8a6b-1c2d3e4f5a6b
Authorization: Bearer oat_your_secret_keyStatuses progress through created, then claimed once the wallet scans, and end in valid or failed. Once the wallet responds, the presented claims are in presentation and the per-credential cryptographic checks are in verification:
{
"status": "valid",
"verification": {
"ISSUER_AUTH": [
{ "status": "✅", "check": "Issuer certificate must be valid" }
],
"DEVICE_AUTH": [
{ "status": "✅", "check": "Device signature must be valid" }
]
},
"presentation": {
"eu.example.employee.1": {
"given_name": "Alice",
"family_name": "Smith",
"department": "Engineering"
}
}
}