> For the complete documentation index, see [llms.txt](https://docs.empe.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.empe.io/getting-started/tutorial/creating-dids-and-issuer.md).

# Creating DIDs and Registering Issuer & Verifier

**What We Are Doing:**

* Creating a decentralized identifier (DID) for the Issuer and one for the Verifier.
* Registering an **Issuer** bound to its DID so it can issue Verifiable Credentials.
* Registering a **Verifier** bound to its DID so it can request and check credential presentations.

**Why:** Every credential the Issuer signs and every authorization request the Verifier sends is anchored to a DID — a portable, cryptographically verifiable identity. A wallet that receives a credential resolves the Issuer's DID to fetch the public key that proves the credential is authentic; a wallet that responds to a presentation request resolves the Verifier's DID to confirm who is asking. Creating these identities is a one-time setup step that you complete before defining schemas or issuing anything.

The service creates two DID methods: **`did:key`** (a self-contained identifier where the public key is encoded directly in the identifier) and **`did:web`** (an identifier whose DID document the service hosts for you over HTTPS). It serves the `did:web` documents itself — you never register or anchor a DID on any external system.

Set up your environment first. The Agent/DID, Issuer, and Verifier registration endpoints live under `/agent` and require a valid OIDC Bearer token carrying the `admin` role. This tutorial keeps a separate base URL for the Issuer and the Verifier so each call clearly targets the right service. In the current One-Click setup both point at the same deployment (one service hosts the Issuer, Verifier, and Holder APIs), though a Verifier can run under its own host in other setups — so the two variables stay distinct:

```bash
ISSUER_BASE_URL=https://your-issuer.evdi.app       # your Issuer deployment
VERIFIER_BASE_URL=https://your-deployment.evdi.app # same deployment today; may be a separate host
TOKEN=<your OIDC access token>                     # JWT with the admin role
```

These are the same base URLs you prepared in your `.env` during [Project Setup](/getting-started/tutorial/project-setup.md). `TOKEN` here is the admin OIDC access token — the same value the deploy pages store per service as `ISSUER_ACCESS_TOKEN` / `VERIFIER_ACCESS_TOKEN`. The upcoming [Deploying the Issuer](/getting-started/tutorial/deploying-issuer.md) and [Deploying the Verifier](/getting-started/tutorial/deploying-verifier.md) pages show where each value comes from in the One-Click portal.

> **Local development:** with `AUTH_DISABLED=true` you can omit the `Authorization` header — see [Authenticating to the API](/getting-started/tutorial/authentication.md).

## Step 1 — Create a DID

You can create either a `did:key` or a `did:web`. A `did:key` requires no configuration and is ideal for getting started; a `did:web` produces a human-readable identifier tied to your domain whose document is resolvable at a stable HTTPS URL.

### Option A — Create a `did:key`

**`POST /agent/did/key`**

This request takes no body and returns the new DID:

```bash
curl -X POST "$ISSUER_BASE_URL/agent/did/key" \
  -H "Authorization: Bearer $TOKEN"
```

**Response Body**

```json
{
  "did": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
  "verificationMethodIds": [
    "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK#z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
  ]
}
```

* **did** — the new `did:key` identifier (an Ed25519 key encoded directly in the DID).
* **verificationMethodIds** — the verification method identifiers you can reference for signing and assertions.

### Option B — Create a `did:web`

**`POST /agent/did/web`**

**Request Body**

```json
{
  "domain": "issuer.example.com"
}
```

* **domain** *(required)* — the hostname that will host the DID document (for example, `issuer.example.com`, optionally with a port like `issuer.example.com:8080`). This must be a valid hostname, not a full URL.

```bash
curl -X POST "$ISSUER_BASE_URL/agent/did/web" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"domain":"issuer.example.com"}'
```

**Response Body**

```json
{
  "did": "did:web:issuer.example.com:0c8f3a52-9b2e-4d31-8a7c-1f6b9e4d2a10",
  "didDocument": {
    "@context": [
      "https://www.w3.org/ns/did/v1",
      "https://w3id.org/security/suites/ed25519-2018/v1"
    ],
    "id": "did:web:issuer.example.com:0c8f3a52-9b2e-4d31-8a7c-1f6b9e4d2a10",
    "verificationMethod": [
      {
        "id": "did:web:issuer.example.com:0c8f3a52-9b2e-4d31-8a7c-1f6b9e4d2a10#key-1",
        "type": "Ed25519VerificationKey2018",
        "controller": "did:web:issuer.example.com:0c8f3a52-9b2e-4d31-8a7c-1f6b9e4d2a10",
        "publicKeyBase58": "H3C2AVvLMv6gmMNam3uVAjZpfkcJCwDwnZn6z3wXmqPV"
      }
    ],
    "authentication": [
      "did:web:issuer.example.com:0c8f3a52-9b2e-4d31-8a7c-1f6b9e4d2a10#key-1"
    ],
    "assertionMethod": [
      "did:web:issuer.example.com:0c8f3a52-9b2e-4d31-8a7c-1f6b9e4d2a10#key-1"
    ]
  }
}
```

* **did** — the created identifier, in the form `did:web:<domain>:<uuid>`. The service generates the trailing UUID for you.
* **didDocument** — the full DID document the service will host. It uses an Ed25519 verification key (`Ed25519VerificationKey2018`) whose public material is published as `publicKeyBase58`; the same key id is referenced from both `authentication` and `assertionMethod`.

The service hosts this `did:web` document for you. It is served, unauthenticated, at a URL derived from the DID's UUID:

```bash
curl "https://issuer.example.com/0c8f3a52-9b2e-4d31-8a7c-1f6b9e4d2a10/did.json"
```

The same document is also available at `https://issuer.example.com/0c8f3a52-9b2e-4d31-8a7c-1f6b9e4d2a10/.well-known/did.json`. When a wallet or another service resolves your `did:web`, this is the document it reads to obtain your public key — so no extra publishing step is required on your side.

**Save the returned `did`.** You will pass it when registering the Issuer (or Verifier) in the next steps, and it becomes the `:issuerDid` path segment used throughout the Issuer API.

## Step 2 — Register the Issuer

A DID on its own is just an identity. To start issuing credentials you register an **Issuer** bound to a DID you created in Step 1. The Issuer signs every credential with that DID's key and advertises the credentials it supports through its OpenID4VCI metadata.

**`POST /agent/issuer`**

**Request Body**

```json
{
  "did": "did:web:issuer.example.com:0c8f3a52-9b2e-4d31-8a7c-1f6b9e4d2a10"
}
```

* **did** *(required)* — the DID the Issuer is bound to. It must be a DID this service already created (from Step 1).

```bash
curl -X POST "$ISSUER_BASE_URL/agent/issuer" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"did":"did:web:issuer.example.com:0c8f3a52-9b2e-4d31-8a7c-1f6b9e4d2a10"}'
```

**Response Body**

```json
{
  "issuerId": "did:web:issuer.example.com:0c8f3a52-9b2e-4d31-8a7c-1f6b9e4d2a10",
  "credentialConfigurationsSupported": {}
}
```

* **issuerId** — the Issuer's identifier, equal to the DID you bound it to. This is the `:issuerDid` path segment you'll use when assigning schemas, creating credential offers, and signing credentials.
* **credentialConfigurationsSupported** — the credential configurations this Issuer can issue. It is built from the platform's active schemas — empty on a fresh deployment — and is refreshed when you create and assign schemas. Each schema and format combination becomes a credential configuration id of the form `Name@Version:format`, such as `KYCCredential@1.0:sd-jwt`.

The response can also carry optional `display` metadata (human-readable details wallets can show) when present on the issuer record.

You can fetch the Issuer record at any time with **`GET /agent/issuer/:did`**, list every registered Issuer with **`GET /agent/issuers`**, and retrieve its OpenID4VCI metadata document with **`GET /agent/issuer/:did/.well-known/did-configuration`**.

## Step 3 — Register the Verifier

The verification side follows the same pattern, but runs against your **Verifier** (`$VERIFIER_BASE_URL`) — which in the current One-Click setup shares the Issuer's deployment, though it may run under its own host in other setups, with its own DIDs and admin access token. Create a DID for the Verifier there (repeat Step 1 against `$VERIFIER_BASE_URL` — a separate `did:key` or `did:web` is fine) and register a **Verifier** bound to it. The Verifier signs the authorization requests it sends to wallets with this DID, so holders can confirm who is asking for a presentation.

**`POST /agent/verifier`**

**Request Body**

```json
{
  "verifierId": "did:key:z6MkjFv8Nq2yT4wXa7Yb3Zc9dE1fG2hJ3kL4mN5pQ6rS7t"
}
```

* **verifierId** *(optional)* — the Verifier's DID. If you omit it, the service generates an identifier for you; pass the DID you just created so the Verifier signs its requests with that key.

```bash
curl -X POST "$VERIFIER_BASE_URL/agent/verifier" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"verifierId":"did:key:z6MkjFv8Nq2yT4wXa7Yb3Zc9dE1fG2hJ3kL4mN5pQ6rS7t"}'
```

**Response Body**

```json
{
  "verifierId": "did:key:z6MkjFv8Nq2yT4wXa7Yb3Zc9dE1fG2hJ3kL4mN5pQ6rS7t"
}
```

* **verifierId** — the Verifier's identifier. Keep it: it is the `:verifierId` path segment you'll use when creating authorization requests later in the tutorial ([Deploying the Verifier](/getting-started/tutorial/deploying-verifier.md) stores it in your `.env` as `VERIFIER_ID`).

You can list every registered Verifier with **`GET /agent/verifiers`**.

## What you have now

You now have two cryptographic identities and the services bound to them:

* An **Issuer** bound to its DID, ready to have schemas assigned and to issue credentials.
* A **Verifier** bound to its DID, ready to request and check presentations.

If you created `did:web` identifiers, their documents are already live at `/<uuid>/did.json` on their respective domains, so any wallet can resolve them without further setup. Next, we'll finish setting up the Issuer deployment itself — saving its base URL and admin access token into our project's `.env`.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.empe.io/getting-started/tutorial/creating-dids-and-issuer.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
