---
title: "Tutorial v2 (30 min)"
description: "Set up the Enterprise Stack and issue, receive, and verify a Verifiable Credential end-to-end using Issuer2, Wallet2, and Verifier2."
stack: "Enterprise Stack (commercial) — version 1.1.1"
stack_version: "1.1.1"
stack_comparison: https://docs.walt.id/community-vs-enterprise.md
canonical_url: https://docs.walt.id/enterprise-stack/home/tutorial-30-min-v2
generated: 2026-10-01
---
# Tutorial v2 (30 min)

In this tutorial, we set up the walt.id Enterprise Stack locally and issue, receive, and verify a Verifiable
Credential end-to-end using **Issuer2**, **Wallet2**, and **Verifier2** — implementing **[OID4VCI 1.0](https://docs.walt.id/concepts/data-exchange-protocols/openid4vci.md)** and
**[OID4VP 1.0](https://docs.walt.id/concepts/data-exchange-protocols/openid4vp.md)**.

**Note:**

Still building against the original **Issuer**, **Wallet**, and **Verifier** Services? Use the
[v1 tutorial](https://docs.walt.id/enterprise-stack/home/tutorial-30-min.md) instead.

**Note:**

Please note: you need to be an Enterprise Stack customer & have access to the private Enterprise Stack images to go
through this tutorial. Running the stack also requires a license — see [Licensing](https://docs.walt.id/enterprise-stack/setup/licensing.md).

We'll work through the resource hierarchy top to bottom — organization, tenant, then services — before issuing,
receiving, and verifying a credential:

1. Bring up the stack and authenticate as super admin
2. Create an organization, an API key, and a tenant
3. Issue a credential with Issuer2
4. Receive the credential into a Wallet2 wallet
5. Verify the credential with Verifier2

---

## Setup

We'll use the [walt.id Enterprise quickstart repository](https://github.com/walt-id/waltid-enterprise-quickstart) to
bring up MongoDB and the Enterprise API via Docker Compose.

**Clone the repo**

```bash
git clone https://github.com/walt-id/waltid-enterprise-quickstart.git && cd waltid-enterprise-quickstart
```

**Add your Docker access token**

```bash
echo "DOCKER_TOKEN_PROVIDED_BY_WALT_ID_HERE" > .docker-token
```

**Run the stack**

```bash
./waltid-enterprise run
```

This brings up a MongoDB instance and the Enterprise API. Once it's running, visit
[enterprise.localhost:3000/swagger](http://enterprise.localhost:3000/swagger) to explore the API interactively.

**Note:**

Learn more about the base domain (`enterprise.localhost`)
configuration [here](https://docs.walt.id/enterprise-stack/setup/configurations/config-files/enterprise.md) and about the Enterprise
Stack's configuration files in
general [here](https://docs.walt.id/enterprise-stack/setup/configurations/config-files/overview.md).

---

## Super Admin

Register and log in as the super admin — every call from here on carries this bearer token. Credentials come from
[`superadmin-registration.conf`](https://docs.walt.id/enterprise-stack/setup/configurations/config-files/superadmin-registration.md) in the
quickstart repo's `config` folder.

### Activate the Super Admin

**Option: CURL**

**Endpoint:** `POST /v1/superadmin/create-by-token` | [API Reference](https://enterprise.sandbox.walt.id/swagger/index.html#/Administration%20%2F%20Superadmin/post_v1_superadmin_create_by_token)

```bash
curl -X POST http://enterprise.localhost:3000/v1/superadmin/create-by-token \
  -H 'Content-Type: application/json' \
  -d '{ "token": "<super-admin-token-from-superadmin-registration.conf>" }'
```

Provide the token as the `token` field of a JSON body — use the token map key from `superadmin-registration.conf` as the
value.

**Response Codes**

- `200` - Super admin account activated successfully.

### Log In as Super Admin

**Option: CURL**

**Endpoint:** `POST /auth/account/emailpass` | [API Reference](https://enterprise.sandbox.walt.id/swagger/index.html#/Authentication/post_auth_account_emailpass)

```bash
curl -X POST http://enterprise.localhost:3000/auth/account/emailpass \
  -H 'Content-Type: application/json' \
  -d '{
    "email": "superadmin@walt.id",
    "password": "pw-according-to-superadmin-registration.conf"
  }'
```

**Example Response**

```json
{
  "session_id": "622b05ef-9f4c-4a0c-97f1-499836a70dc7",
  "status": "SUCCESS",
  "token": "eyJhbGciOiJFZERTQSJ9...",
  "expiration": "2026-07-29T15:38:44.121997Z"
}
```

**Note:**

  Save `token` — pass it as `Authorization: Bearer {token}` on every request from here on.

---

## Organization

Everything in the Enterprise Stack hangs off an **organization** — the top of the resource
hierarchy (new to the resource model? see
[Organizations](https://docs.walt.id/enterprise-stack/administration/organizations/overview.md)). Under it you create tenants, and under
tenants, services — so data for different customers or products stays isolated while sharing one deployment.

**Option: CURL**

**Endpoint:** `POST /v1/admin/organizations` | [API Reference](https://enterprise.sandbox.walt.id/swagger/index.html#/Administration%20(Global)%20%2F%20Organizations/post_v1_admin_organizations)

```bash
curl -X POST http://enterprise.localhost:3000/v1/admin/organizations \
  -H "Authorization: Bearer {yourToken}" \
  -H 'Content-Type: application/json' \
  -d '{
    "_id": "waltid",
    "profile": { "name": "Test GmbH" }
  }'
```

**Body Parameters**

- `_id`: _String_ (required) - Unique identifier for the organization, e.g. `waltid`.
- `profile.name`: _String_ (required) - Human-readable name for the organization.

**Response Codes**

- `201` - Organization created successfully.

Creating an organization automatically generates an admin role named `{orgID}.admin` — here, `waltid.admin` — and a
dedicated subdomain, `{orgID}.enterprise.localhost`, for operations scoped to it. From here on, every call targets
that subdomain: `http://waltid.enterprise.localhost:3000`.

---

## API Key

Rather than using the super-admin token for day-to-day operations, create an API key scoped to this organization and
grant it the `waltid.admin` role generated above.

### Create the Key

**Option: CURL**

**Endpoint:** `POST /v1/{target}/apikeys-api/api-keys/create` | [API Reference](https://enterprise.sandbox.walt.id/swagger/index.html#/Service%20%2F%20API%20Keys/post_v1__target__apikeys_api_api_keys_create)

```bash
curl -X POST http://waltid.enterprise.localhost:3000/v1/waltid.myApiKey/apikeys-api/api-keys/create \
  -H "Authorization: Bearer {yourToken}" \
  -H 'Content-Type: application/json' \
  -d '{ "name": "My API Key", "expiration": "30d" }'
```

**Path Parameters**

- `target`: _resourceIdentifier_ (required) - The API key's own ID, `{organizationID}.[YourID]`, e.g.
  `waltid.myApiKey`.

**Example Response**

```json
{
  "_id": "waltid.myApiKey",
  "name": "My API Key",
  "token": "eyJhbGciOiJFZERTQSJ9..."
}
```

### Assign the Admin Role

A fresh API key has no permissions until you assign it a role.

**Option: CURL**

**Endpoint:** `POST /v1/{target}/roles-api/roles/apikey/assign`

```bash
curl -X POST http://waltid.enterprise.localhost:3000/v1/waltid.admin/roles-api/roles/apikey/assign \
  -H "Authorization: Bearer {yourToken}" \
  -H 'Content-Type: application/json' \
  -d '{ "apikey": "waltid.myApiKey" }'
```

**Response Codes**

- `200` - Role assigned successfully.

From here on, use this API key's token instead of the super-admin token.

---

## Tenant

Create a tenant inside the organization — the level under which services actually live. You can nest sub-tenants
arbitrarily deep to keep customers' or products' data separate; we'll use a single flat tenant here.

**Note:**

We call this tenant `tenant2`, not `tenant1` — if you've also gone through the [v1 tutorial](https://docs.walt.id/enterprise-stack/home/tutorial-30-min.md)
against the same organization, it already created services named `kms1`, `issuer1`, `wallet1`, and `verifier1`
under `waltid.tenant1`. Using a separate tenant keeps this walkthrough's resources from colliding with those.

**Option: CURL**

**Endpoint:** `POST /v1/{target}/resource-api/tenants/create` | [API Reference](https://enterprise.sandbox.walt.id/swagger/index.html#/Service%20%2F%20Resource/post_v1__target__resource_api_tenants_create)

```bash
curl -X POST http://waltid.enterprise.localhost:3000/v1/waltid.tenant2/resource-api/tenants/create \
  -H "Authorization: Bearer {yourToken}" \
  -H 'Content-Type: application/json' \
  -d '{ "name": "My Tenant" }'
```

**Path Parameters**

- `target`: _resourceIdentifier_ (required) - `{organizationID}.[YourID]`, e.g. `waltid.tenant2`.

**Response Codes**

- `201` - Tenant created successfully.

---

## Issue a Credential with Issuer2

For this tutorial, we'll issue an **Open Badge Credential** — a
[W3C Verifiable Credential](https://docs.walt.id/concepts/digital-credentials/verifiable-credentials-w3c.md) with a JWT signature
(`jwt_vc_json`). Issuer2 also supports SD-JWT VC and mdoc (ISO 18013-5); this tutorial sticks to W3C VC to keep the
walkthrough focused.

### Set Up a Key and DID for the Issuer

An issuer needs a **key** to sign credentials with and a **DID** to identify itself as — the `issuer` field on every
credential it issues (new to DIDs? see [Decentralised Identifiers](https://docs.walt.id/concepts/decentralised-identifiers.md)). Both live
in dedicated services under the tenant: a [KMS Service](https://docs.walt.id/enterprise-stack/services/key-management-service/overview.md)
for the key, a [DID Service](https://docs.walt.id/enterprise-stack/services/did-service/overview.md) to derive a DID from it.

**Option: CURL**

**Create a KMS Service**

```bash
curl -X POST http://waltid.enterprise.localhost:3000/v1/waltid.tenant2.kms1/resource-api/services/create \
  -H "Authorization: Bearer {yourToken}" -H 'Content-Type: application/json' \
  -d '{ "type": "kms" }'
```

**Generate a Signing Key**

```bash
curl -X POST http://waltid.enterprise.localhost:3000/v1/waltid.tenant2.kms1.key1/kms-service-api/keys/generate \
  -H "Authorization: Bearer {yourToken}" -H 'Content-Type: application/json' \
  -d '{ "backend": "jwk", "keyType": "Ed25519" }'
```

**Create a DID Service**

```bash
curl -X POST http://waltid.enterprise.localhost:3000/v1/waltid.tenant2.did1/resource-api/services/create \
  -H "Authorization: Bearer {yourToken}" -H 'Content-Type: application/json' \
  -d '{ "type": "did" }'
```

Link the KMS as a dependency so the DID service can derive a DID from `key1`:

```bash
curl -X POST http://waltid.enterprise.localhost:3000/v1/waltid.tenant2.did1/did-service-api/dids/dependencies/add \
  -H "Authorization: Bearer {yourToken}" -H 'Content-Type: application/json' \
  -d '{
  "dependency": "waltid.tenant2.kms1"
}'
```

**Create a `did:key`**

```bash
curl -X POST http://waltid.enterprise.localhost:3000/v1/waltid.tenant2.did1/did-service-api/dids/create/key \
  -H "Authorization: Bearer {yourToken}" -H 'Content-Type: application/json' \
  -d '{ "keyReference": "waltid.tenant2.kms1.key1" }'
```

**Example Response**

```json
{
  "did": "did:key:zmYg9bgKmRiCqTTd9MA1ufVE9tfzUptwQp4GMRxptXquJWw4Uj5cnYqd6qgimWWRVguLp5NMKWtw4ZBJyNbZfqrDXNbAFqhGVJz35PRgCQyABvpg4",
  "document": { "...": "the resolved DID document" }
}
```

**Note:**

  Save this `did` — it's the `issuerDid` for the profile we create next.

### Create the Issuer2 Service

Issuer2 declares which credential types it can issue via `credentialConfigurations` — a map of credential
configuration IDs to their format, signing algorithm, and key-binding rules. This becomes the issuer's published
OID4VCI metadata.

**Option: CURL**

**Endpoint:** `POST /v1/{target}/resource-api/services/create` | [API Reference](https://enterprise.sandbox.walt.id/swagger/index.html#/Service%20%2F%20Resource/post_v1__target__resource_api_services_create)

```bash
curl -X POST http://waltid.enterprise.localhost:3000/v1/waltid.tenant2.issuer1/resource-api/services/create \
  -H "Authorization: Bearer {yourToken}" -H 'Content-Type: application/json' \
  -d '{
    "type": "issuer2",
    "baseUrl": "http://waltid.enterprise.localhost:3000",
    "kms": "waltid.tenant2.kms1",
    "tokenKeyId": "waltid.tenant2.kms1.key1",
    "credentialConfigurations": {
      "OpenBadgeCredential_jwt_vc_json": {
        "format": "jwt_vc_json",
        "cryptographic_binding_methods_supported": ["jwk", "did:key", "did:web", "did:jwk"],
        "credential_signing_alg_values_supported": ["ES256", "EdDSA"],
        "proof_types_supported": {
          "jwt": { "proof_signing_alg_values_supported": ["ES256", "EdDSA"] }
        },
        "credential_definition": {
          "type": ["VerifiableCredential", "OpenBadgeCredential"]
        }
      }
    }
  }'
```

**Body Parameters**

- `type`: _String_ (required) - Must be `issuer2`.
- `kms`: _resourceIdentifier_ (required) - The KMS service created above.
- `tokenKeyId`: _resourceIdentifier_ (required) - Key used to sign OID4VCI access tokens; we reuse `key1`.
- `credentialConfigurations`: _Object_ (required) - Map of credential types this issuer supports, keyed by a
  credential configuration ID of your choosing. `cryptographic_binding_methods_supported` and
  `proof_types_supported` are co-dependent — set both or neither. See
  [Credential Types](https://docs.walt.id/enterprise-stack/services/issuer2-service/configurations/credential-types.md) for the full field
  reference and per-format rules (SD-JWT VC, mDoc).

**Response Codes**

- `201` - Service created successfully.

### Create a Credential Profile

Issuer2 issues from **[profiles](https://docs.walt.id/enterprise-stack/services/issuer2-service/credential-profiles/overview.md)** —
reusable configurations that bind a `credentialConfigurationId` to a signing key, issuer identity, and a credential
data template. A profile is addressed as a child of the issuer service, `{issuerTarget}.{profileId}`.

**Option: CURL**

**Endpoint:** `POST /v2/{target}.{profileId}/issuer-service-api/credentials/profiles` | [API Reference](https://enterprise.sandbox.walt.id/swagger/index.html)

```bash
curl -X POST http://waltid.enterprise.localhost:3000/v2/waltid.tenant2.issuer1.openbadge1/issuer-service-api/credentials/profiles \
  -H "Authorization: Bearer {yourToken}" -H 'Content-Type: application/json' \
  -d '{
    "name": "Open Badge Credential Profile",
    "credentialConfigurationId": "OpenBadgeCredential_jwt_vc_json",
    "issuerKeyId": "waltid.tenant2.kms1.key1",
    "issuerDid": "did:key:zmYg9bgKmRiCqTTd9MA1ufVE9tfzUptwQp4GMRxptXquJWw4Uj5cnYqd6qgimWWRVguLp5NMKWtw4ZBJyNbZfqrDXNbAFqhGVJz35PRgCQyABvpg4",
    "w3cVersion": "W3CV2",
    "credentialData": {
      "@context": [
        "https://www.w3.org/ns/credentials/v2",
        "https://purl.imsglobal.org/spec/ob/v3p0/context-3.0.3.json"
      ],
      "type": ["VerifiableCredential", "OpenBadgeCredential"],
      "credentialSubject": {
        "type": ["AchievementSubject"],
        "achievement": {
          "type": ["Achievement"],
          "name": "Teamwork",
          "description": "This badge recognizes the development of the capacity to collaborate within a group environment."
        }
      }
    },
    "mapping": {
      "id": "<uuid>",
      "credentialSubject": { "id": "<subjectDid>" },
      "validFrom": "<timestamp>"
    }
  }'
```

**Path Parameters**

- `target`: _String_ (required) - The issuer service path plus the new profile's own ID as the final segment, e.g.
  `waltid.tenant2.issuer1.openbadge1`.

**Body Parameters**

- `name`: _String_ (required) - A human-readable name for the profile.
- `credentialConfigurationId`: _String_ (required) - Must match a key in the issuer's `credentialConfigurations`.
- `issuerKeyId`: _resourceIdentifier_ (required) - The signing key.
- `credentialData`: _Object_ (required) - The credential data template.
- `issuerDid`: _String_ (optional) - The issuer's DID, from the previous step.
- `mapping`: _Object_ (optional) - Dynamic value insertion at issuance time — here, a random `id`, the holder's DID
  as `credentialSubject.id`, and the current timestamp as `validFrom`. See
  [Data Functions](https://docs.walt.id/enterprise-stack/services/issuer2-service/data-functions.md).

**Example Response**

```json
{
  "profileId": "openbadge1",
  "name": "Open Badge Credential Profile",
  "version": 1,
  "credentialConfigurationId": "OpenBadgeCredential_jwt_vc_json",
  "createdAt": 1784732057289,
  "updatedAt": 1784732057289
}
```

### Create a Credential Offer

Now let's actually issue a credential from that profile. This call creates a **credential offer** — a one-time,
short-lived pointer to a not-yet-issued credential that a wallet can redeem.

**Option: CURL**

**Endpoint:** `POST /v2/{profileTarget}/issuer-service-api/credentials/offers` | [API Reference](https://enterprise.sandbox.walt.id/swagger/index.html)

```bash
curl -X POST http://waltid.enterprise.localhost:3000/v2/waltid.tenant2.issuer1.openbadge1/issuer-service-api/credentials/offers \
  -H "Authorization: Bearer {yourToken}" -H 'Content-Type: application/json' \
  -d '{ "authMethod": "PRE_AUTHORIZED" }'
```

**Body Parameters**

- `authMethod`: _String_ (required) - `PRE_AUTHORIZED` requires no user login and is the simplest way to get
  started. Issuer2 also supports `AUTHORIZED` (authorization-code flow against an external IdP) — see
  [Protocol Flows](https://docs.walt.id/enterprise-stack/services/issuer2-service/protocol-flows/overview.md).

**Example Response**

```json
{
  "offerId": "a7c6196c-e8d0-4a2e-818d-af66e3d45dc1",
  "profileId": "openbadge1",
  "profileVersion": 1,
  "authMethod": "PRE_AUTHORIZED",
  "expiresAt": 1784733115870,
  "credentialOffer": "openid-credential-offer://?credential_offer_uri=http%3A%2F%2Fwaltid.enterprise.localhost%3A3000%2Fv2%2Fwaltid.tenant2.issuer1%2Fissuer-service-api%2Fopenid4vci%2Fcredential-offer%3Fid%3Da7c6196c-e8d0-4a2e-818d-af66e3d45dc1"
}
```

`credentialOffer` is the [OID4VCI](https://docs.walt.id/concepts/data-exchange-protocols/openid4vci.md) offer URL — hand it directly to
Wallet2 in the next step.

---

## Receive the Credential in a Wallet

### Create a Wallet

A wallet is its own service, `wallet-service-api`, addressed by its own target
(e.g. `waltid.tenant2.wallet1`). Rather than wiring up a KMS, DID Service, DID Store, and Credential Store by hand,
the **init-wallet wizard** creates and links all of them — plus a first key and DID — in a single call.

**Option: CURL**

**Endpoint:** `POST /v1/{target}/wallet-service-api/init-wallet` | [API Reference](https://enterprise.sandbox.walt.id/swagger/index.html#/Service%20%7C%20Wallet%20Service/post_v1__target__wallet_service_api_init_wallet)

```bash
curl -X POST http://waltid.enterprise.localhost:3000/v1/waltid.tenant2.wallet1/wallet-service-api/init-wallet \
  -H "Authorization: Bearer {yourToken}" -H 'Content-Type: application/json' \
  -d '{
    "createKms": true,
    "kmsName": "wallet-kms",
    "createKeyInKms": { "backend": "jwk", "keyType": "Ed25519" },
    "createDidStore": true,
    "didStoreName": "wallet-did-store",
    "createDidService": true,
    "didServiceName": "wallet-did-service",
    "createDidWithDidService": "key",
    "createCredentialStore": true,
    "credentialStoreName": "wallet-credential-store"
  }'
```

**Body Parameters** — see
[Setup](https://docs.walt.id/enterprise-stack/services/wallet-service/setup.md#creating-a-wallet-with-dependencies-wizard) for the full
field reference, including how to link existing services instead of creating new ones.

**Example Response**

```json
{
  "wallet": {
    "_id": "waltid.tenant2.wallet1",
    "dependencies": ["waltid.tenant2.wallet-did-store", "waltid.tenant2.wallet-kms", "waltid.tenant2.wallet-credential-store"]
  },
  "createdResources": {
    "keyId": "waltid.tenant2.wallet-kms.wallet_key",
    "didId": "waltid.tenant2.wallet-did-store.wallet_did",
    "did": "did:key:z6MkidYoMBEPqyVXsihMPdLGfeWyfAUSD2RvW1Yidtr3KPuS"
  }
}
```

The created key and DID automatically become the wallet's **defaults** — every call below can omit `keyId` and `did`
entirely and this key/DID pair is used.

### Receive the Credential

This single call looks up the offer's details, exchanges its one-time code for a short-lived access token, signs a
proof that the wallet owns the key the credential will be bound to, then fetches the credential and stores it in the
linked Credential Store.

**Option: CURL**

**Endpoint:** `POST /v2/{target}/wallet-service-api/credentials/receive` | [API Reference](https://enterprise.sandbox.walt.id/swagger/index.html)

```bash
curl -X POST http://waltid.enterprise.localhost:3000/v2/waltid.tenant2.wallet1/wallet-service-api/credentials/receive \
  -H "Authorization: Bearer {yourToken}" -H 'Content-Type: application/json' \
  -d '{
    "offerUrl": "openid-credential-offer://?credential_offer_uri=http%3A%2F%2Fwaltid.enterprise.localhost%3A3000%2Fv2%2Fwaltid.tenant2.issuer1%2Fissuer-service-api%2Fopenid4vci%2Fcredential-offer%3Fid%3Da7c6196c-e8d0-4a2e-818d-af66e3d45dc1"
  }'
```

**Body Parameters**

- `offerUrl`: _String_ - The `credentialOffer` value from the previous step.

**Example Response**

```json
{
  "credentialIds": ["c58d08ea-4c1a-4c0c-9bf9-bd6df4083936"],
  "deferredTransactionIds": {}
}
```

### Confirm It Was Stored

**Option: CURL**

**Endpoint:** `GET /v2/{target}/wallet-service-api/credentials` | [API Reference](https://enterprise.sandbox.walt.id/swagger/index.html)

```bash
curl http://waltid.enterprise.localhost:3000/v2/waltid.tenant2.wallet1/wallet-service-api/credentials \
  -H "Authorization: Bearer {yourToken}"
```

**Example Response**

```json
[
  {
    "id": "c58d08ea-4c1a-4c0c-9bf9-bd6df4083936",
    "format": "jwt_vc_json",
    "issuer": "did:key:zmYg9bgKmRiCqTTd9MA1ufVE9tfzUptwQp4GMRxptXquJWw4Uj5cnYqd6qgimWWRVguLp5NMKWtw4ZBJyNbZfqrDXNbAFqhGVJz35PRgCQyABvpg4",
    "subject": "did:key:z6MkidYoMBEPqyVXsihMPdLGfeWyfAUSD2RvW1Yidtr3KPuS",
    "addedAt": "2026-07-22T15:07:12.456308Z"
  }
]
```

🎉 You've received an Open Badge Credential into the wallet. To see the full parsed contents, use
`GET /v2/{target}/wallet-service-api/credentials/{credentialId}` — see
[Credential Management](https://docs.walt.id/enterprise-stack/services/wallet-service/management).

---

## Verify the Credential with Verifier2

### Create the Verifier2 Service

**Option: CURL**

**Endpoint:** `POST /v1/{target}/resource-api/services/create` | [API Reference](https://enterprise.sandbox.walt.id/swagger/index.html#/Service%20%2F%20Resource/post_v1__target__resource_api_services_create)

```bash
curl -X POST http://waltid.enterprise.localhost:3000/v1/waltid.tenant2.verifier1/resource-api/services/create \
  -H "Authorization: Bearer {yourToken}" -H 'Content-Type: application/json' \
  -d '{
    "type": "verifier2",
    "baseUrl": "http://waltid.enterprise.localhost:3000",
    "clientMetadata": { "client_name": "walt.id Enterprise Verifier" }
  }'
```

**Body Parameters**

- `type`: _String_ (required) - Must be `verifier2`.
- `clientMetadata`: _Object_ (optional) - Human-readable verifier name/logo shown to the wallet. Defaults to
  `{"client_name": "Verifier"}`.

**Response Codes**

- `201` - Service created successfully.

### Create a Verification Session

We build an [OID4VP](https://docs.walt.id/concepts/data-exchange-protocols/openid4vp.md) authorization request. Verifier2 uses
**[DCQL](https://docs.walt.id/concepts/data-exchange-protocols/dcql.md)** (Credential Query Language) to describe the credentials it
wants — the credential type and format are expressed as a `dcql_query` directly in the request body.

**Option: CURL**

**Endpoint:** `POST /v1/{target}/verifier2-service-api/verification-session/create` | [API Reference](https://enterprise.sandbox.walt.id/swagger/index.html#/Service%20%7C%20Verifier2%20Service%20-%20Verification%20Sessions/post_v1__target__verifier2_service_api_verification_session_create)

```bash
curl -X POST http://waltid.enterprise.localhost:3000/v1/waltid.tenant2.verifier1/verifier2-service-api/verification-session/create \
  -H "Authorization: Bearer {yourToken}" -H 'Content-Type: application/json' \
  -d '{
    "flow_type": "cross_device",
    "core_flow": {
      "dcql_query": {
        "credentials": [
          {
            "id": "credential_1",
            "format": "jwt_vc_json",
            "meta": { "type_values": [["VerifiableCredential", "OpenBadgeCredential"]] }
          }
        ]
      },
      "policies": {
        "vc_policies": [{ "policy": "signature" }]
      }
    }
  }'
```

**Body Parameters**

- `flow_type`: _String_ (required) - `cross_device` (QR code), `same_device` (deep link), `dc_api` / `dc_api-annex-c`
  (browser-native Digital Credentials API). We use `cross_device` here.
- `core_flow.dcql_query.credentials`: _Array_ (required) - one entry per credential to request. `id` is a
  free-choice label used later to key policy results; `format` + `meta.type_values` describe what to accept.
- `core_flow.policies.vc_policies`: _Array_ (optional) - validation rules applied to the received credential. See
  [Policies](https://docs.walt.id/enterprise-stack/services/verifier2-service/policies/overview.md) for the full list.
  **Omitting `policies` entirely defaults to signature-only verification** — the same as what we set explicitly here.

**Example Response**

```json
{
  "sessionId": "2a7033e0-45f7-4837-a042-847270ded8d7",
  "bootstrapAuthorizationRequestUrl": "openid4vp://authorize?request_uri=http%3A%2F%2Fwaltid.enterprise.localhost%3A3000%2Fv1%2Fwaltid.tenant2.verifier1%2Fverifier2-service-api%2F2a7033e0-45f7-4837-a042-847270ded8d7%2Frequest",
  "fullAuthorizationRequestUrl": "openid4vp://authorize?response_type=vp_token&state=...&response_uri=...&dcql_query=...",
  "creationTarget": "waltid.tenant2.verifier1.2a7033e0-45f7-4837-a042-847270ded8d7"
}
```

- `sessionId`: use this to check the result later.
- `bootstrapAuthorizationRequestUrl`: the short, `request_uri`-by-reference form — this is what we hand to the
  wallet next (and what you'd turn into a QR code for a real cross-device flow).
- `creationTarget`: the full resource path including the session ID — use this as `{creationTarget}` when checking
  the result.

---

## Present the Credential

Back on the wallet, a single call resolves the verifier's request, matches it against the wallet's stored
credentials via DCQL, signs with the default key/DID, and submits the response.

**Option: CURL**

**Endpoint:** `POST /v2/{target}/wallet-service-api/credentials/present` | [API Reference](https://enterprise.sandbox.walt.id/swagger/index.html)

```bash
curl -X POST http://waltid.enterprise.localhost:3000/v2/waltid.tenant2.wallet1/wallet-service-api/credentials/present \
  -H "Authorization: Bearer {yourToken}" -H 'Content-Type: application/json' \
  -d '{
    "requestUrl": "openid4vp://authorize?request_uri=http%3A%2F%2Fwaltid.enterprise.localhost%3A3000%2Fv1%2Fwaltid.tenant2.verifier1%2Fverifier2-service-api%2F2a7033e0-45f7-4837-a042-847270ded8d7%2Frequest"
  }'
```

**Body Parameters**

- `requestUrl`: _String_ - the `bootstrapAuthorizationRequestUrl` from the previous step.

**Example Response**

```json
{
  "transmission_success": true,
  "verifier_response": {
    "status": "received",
    "message": "Presentation received and is being processed."
  }
}
```

**Note:**

  `transmission_success: true` only confirms the wallet delivered the presentation — it says nothing about whether the
  credential actually passed verification. Check that on the verifier side next.

---

## Check the Verification Result

**Option: CURL**

**Endpoint:** `GET /v1/{creationTarget}/verifier2-service-api/verification-session/info` | [API Reference](https://enterprise.sandbox.walt.id/swagger/index.html#/Service%20%7C%20Verifier2%20Service%20-%20Verification%20Sessions/get_v1__target__verifier2_service_api_verification_session_info)

```bash
curl http://waltid.enterprise.localhost:3000/v1/waltid.tenant2.verifier1.2a7033e0-45f7-4837-a042-847270ded8d7/verifier2-service-api/verification-session/info \
  -H "Authorization: Bearer {yourToken}"
```

**Example Response** (shortened)

```json
{
  "session": {
    "id": "2a7033e0-45f7-4837-a042-847270ded8d7",
    "status": "SUCCESSFUL",
    "policy_results": {
      "vc_policies": [
        {
          "policy": { "policy": "signature", "id": "signature" },
          "success": true,
          "result": { "verification_result": true }
        }
      ],
      "overallSuccess": true
    }
  }
}
```

`status: "SUCCESSFUL"` and `policy_results.overallSuccess: true` confirm the credential was received and the
signature policy passed. 🎉

You've set up the Enterprise Stack and issued, received, requested, and verified an Open Badge Credential end-to-end
using Issuer2, Wallet2, and Verifier2.

---

## Next Steps

**Note:**

If you enjoy our tools, please leave us a star ⭐ [on GitHub](https://github.com/walt-id/waltid-identity).

You can learn more about the different services in detail below:

- [Issuer2 Service](https://docs.walt.id/enterprise-stack/services/issuer2-service/overview.md)
- [Wallet Service](https://docs.walt.id/enterprise-stack/services/wallet-service/overview.md)
- [Verifier2 Service](https://docs.walt.id/enterprise-stack/services/verifier2-service/overview.md)
