Docs

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_key

Statuses 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"
    }
  }
}