> 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/develop/verifier/api-reference/verify-credential.md).

# Verify Credential

This page documents the Verifier's **direct credential verification** endpoint. It takes a single credential in its compact serialized form, checks the signature, and returns the decoded payload. This is a stateless, one-shot check of one credential — it is **not** a full presentation flow. If you need a wallet to choose and present credentials interactively (with selective disclosure, holder binding, and session tracking), use the authorization-request and verification-session endpoints described in [Defining What Credentials to Request](/develop/verifier/server-side-vp-query.md) and [Verifier Client Configuration](/develop/verifier/client-configuration.md) instead.

Use this endpoint when you already hold a compact credential string — for example one you received out of band, exported from a wallet, or signed yourself with the Issuer's signing endpoint — and you want to confirm it is authentic and read its claims.

***

## Overview

* The endpoint verifies a **single credential** supplied as a compact string. No verification session is created and nothing is persisted.
* Two credential formats are supported: **SD-JWT VC** and **JWT-VC-JSON**. The format is detected automatically from the string itself — there is no `format` field in the request.
* On success you get back the decoded `payload`, and for SD-JWT credentials a resolved `prettyClaims` object with all disclosed claims merged in.
* On failure you get a `false` result and an `error` message rather than an HTTP error, so your code can branch on the result instead of catching exceptions. (A malformed request body — for example a missing or empty `credential` — still returns `400 Bad Request`.)

***

## Endpoint

This endpoint lives under the **`/credentials`** path. It requires a valid **OIDC JWT Bearer token** (`Authorization: Bearer <token>`); it carries no specific role requirement (see [Authentication](/develop/verifier/authentication.md)).

### Verify a Credential

**`POST /credentials/verify`**

Verifies a credential's signature and returns the decoded payload.

#### Request Body

* **`credential`** (string, required) The credential to verify, in compact serialized form. This is either a compact **SD-JWT** (the issuer-signed JWT followed by `~`-separated disclosures, ending in `~`) or a compact **JWT-VC-JSON** (a three-part `header.payload.signature` JWT). The string must be non-empty.

The format is inferred from the value: a string containing a `~` is treated as an SD-JWT; a string of exactly three non-empty dot-separated segments with no `~` is treated as a JWT-VC. Anything else is rejected (see [Error Responses](#error-responses)).

```json
{
  "credential": "eyJhbGciOiJFZERTQSJ9.eyJ2Y3QiOiJodHRwczovL2lzc3Vlci5leGFtcGxlLmNvbS92Y3QvZW1wbG95ZWUtYmFkZ2UiLCJlbXBsb3llZV9pZCI6IkUtMTAyNCJ9.signature~WyJzYWx0IiwiZnVsbF9uYW1lIiwiQWRhIExvdmVsYWNlIl0~WyJzYWx0IiwiZGVwYXJ0bWVudCIsIkVuZ2luZWVyaW5nIl0~"
}
```

#### Response Body

A `200 OK` response always carries an **`isValid`** boolean. The remaining fields depend on the outcome.

When the credential is valid:

* **`isValid`** (boolean) — `true`.
* **`format`** (string) — the detected credential format. `dc+sd-jwt` for SD-JWT VC, or `jwt_vc` for JWT-VC-JSON.
* **`payload`** (object) — the decoded credential payload (the claims carried in the signed JWT body).
* **`prettyClaims`** (object, SD-JWT only) — the credential's claims with all disclosures resolved and merged. This field is present only for SD-JWT credentials; JWT-VC responses omit it.

When the credential is invalid:

* **`isValid`** (boolean) — `false`.
* **`error`** (string) — a human-readable reason the credential could not be verified.

A failure response contains only `isValid: false` and `error`. It never includes `format` (or `payload`/`prettyClaims`), even when the credential was parseable as an SD-JWT or JWT-VC — the controller discards the detected format on invalid results.

**Example success response (SD-JWT VC)**

For an `EmployeeBadge@1:sd-jwt` credential, the response merges the disclosed claims into `prettyClaims` while `payload` reflects the raw signed body:

```json
{
  "isValid": true,
  "format": "dc+sd-jwt",
  "payload": {
    "vct": "https://issuer.example.com/vct/employee-badge",
    "iss": "did:web:issuer.example.com:1f8a6a8b",
    "iat": 1750593600,
    "employee_id": "E-1024"
  },
  "prettyClaims": {
    "vct": "https://issuer.example.com/vct/employee-badge",
    "employee_id": "E-1024",
    "full_name": "Ada Lovelace",
    "department": "Engineering"
  }
}
```

**Example success response (JWT-VC-JSON)**

```json
{
  "isValid": true,
  "format": "jwt_vc",
  "payload": {
    "iss": "did:web:issuer.example.com:1f8a6a8b",
    "sub": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
    "vc": {
      "type": ["VerifiableCredential", "EmploymentCredential"],
      "credentialSubject": {
        "employee_id": "E-1024",
        "full_name": "Ada Lovelace",
        "department": "Engineering"
      }
    }
  }
}
```

**Example failure response**

```json
{
  "isValid": false,
  "error": "SD-JWT verification failed."
}
```

***

## Example Request

The example below verifies a credential from a backend using TypeScript and `fetch`. The same call works from any HTTP client.

```typescript
const VERIFIER_BASE_URL = 'https://your-verifier.evdi.app';

async function verifyCredential(token: string, credential: string) {
  const res = await fetch(`${VERIFIER_BASE_URL}/credentials/verify`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${token}`,
    },
    body: JSON.stringify({ credential }),
  });

  const result = await res.json();

  if (result.isValid) {
    // Read the credential's claims. For SD-JWT, prefer prettyClaims.
    const claims = result.prettyClaims ?? result.payload;
    console.log('Verified', result.format, claims);
  } else {
    console.warn('Credential is not valid:', result.error);
  }

  return result;
}
```

***

## Error Responses

* **Unsupported format** — if the `credential` string is neither a compact SD-JWT nor a three-part JWT, the response is `200 OK` with `isValid: false` and the message `Unsupported credential format. Expected compact JWT (x.y.z) or SD-JWT.`
* **Verification failure** — if the signature is invalid, the credential is expired, or the issuer's verification material cannot be resolved, the response is `200 OK` with `isValid: false` and an `error` describing the cause.
* **Validation failure** — if the request body is missing the `credential` field or it is empty, the request fails with `400 Bad Request` from the global error filter (`{ statusCode, message, path, timestamp }`).

***

## Notes

* This endpoint verifies **one credential at a time**. It does not evaluate a Verifiable Presentation, check holder binding against a presentation, or apply a DCQL / Presentation Exchange query. For those, create an authorization request and observe the verification session.
* This endpoint requires a valid OIDC JWT Bearer token but no specific role (see [Authentication](/develop/verifier/authentication.md)).
* For SD-JWT credentials, read disclosed values from `prettyClaims`. The `payload` field contains the raw signed body, in which selectively disclosable claims are represented as hashed digests rather than their plaintext values.


---

# 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/develop/verifier/api-reference/verify-credential.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.
