# verifiableCredentials (/docs/reference/api/verifiable-credentials)

> Every organization can issue verifiable credentials: SD-JWT VCs (IETF SD-JWT-based Verifiable Credentials) that people keep in digital wallets and present with…



Every organization can issue verifiable credentials: SD-JWT VCs (IETF SD-JWT-based Verifiable Credentials) that people
keep in digital wallets and present with only the claims a verifier needs. Credentials are bound to a key the holder
proved they own, and they are revoked or suspended through an IETF Token Status List that verifiers read. Wallets get
credentials over OpenID4VCI from endpoints mounted at `{basePath}/vc/{tenantId}`. Apps can call `request` directly. The
group exists when the deployment sets the `verifiableCredentials` option; otherwise every method fails with
`FEATURE_DISABLED`. The guide is
[Verifiable credentials](https://github.com/Better-IAM/better-iam/blob/main/docs/verifiable-credentials.md).

| Method                              | What it does                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | Access     |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
| [`available`](#available)           | The enabled types the caller may request for themselves right now: `vc:request` allowed on `credential-type/{name}`, MFA when the type asks, and not in a "view as" session or a delegated agent session. No permission needed.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             | Credential |
| [`createOffer`](#createoffer)       | An OpenID4VCI credential offer with a pre-authorized code, and its `openid-credential-offer://` link for a wallet to scan. It is for the caller (`vc:request`, like `request`) or, with `identityId`, for someone else (`iam:vc:issue`). `txCode: true` adds a 6-digit PIN the wallet asks for; give it to the person separately. The code works once and expires after the deployment's `offerLifetimeMs`. A token request without the PIN does not count as a guess; five wrong PINs end the offer. For self-service offers, policy and MFA are decided again when the wallet redeems the offer, and the credential ends with the grants that allow it. An administrator's offer records them as the credential's `issuedBy`.                                                             | Credential |
| [`createType`](#createtype)         | Defines a credential type: a `name`, a `displayName` and description for wallets, card colors, a lifetime (default 30 days, at most the deployment's `maxLifetimeMs`), `requireMfa`, and 1 to 32 `claims`. Each claim takes its value from a `source`: `email`, `emailVerified`, `name`, `identityId`, `kind`, `tenantId`, `tenantName`, `teams`, `department`, a declared identity attribute (`attribute:{name}`) or a `static` value. It is selectively disclosable unless `selective: false`, and left out when missing unless `required`. The `vct` defaults to `{issuer}/types/{name}`; a custom one may not sit under another organization's issuer on this deployment. The first type also creates the tenant's issuer key.                                                          | Credential |
| [`deleteType`](#deletetype)         | Deletes a type and its pending offers.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | Credential |
| [`getType`](#gettype)               | One credential type.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | Credential |
| [`issuerMetadata`](#issuermetadata) | The tenant's OpenID4VCI credential issuer metadata: endpoints, and every enabled type with its format (`dc+sd-jwt`), `vct`, binding methods, proof algorithms and wallet display. The same document is served at `/.well-known/openid-credential-issuer{basePath}/vc/{tenantId}`. Public.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | Public     |
| [`listIssued`](#listissued)         | Issued credentials, newest first, by `type`, `identityId` or `state` (`valid`, `suspended`, `revoked`, `expired`), with `limit` (at most 500) and `offset`. Records hold claim names, never values.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | Credential |
| [`listKeys`](#listkeys)             | The issuer's signing keys, oldest first: `active`, `previous` (still published, so its credentials verify) and `retired`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | Credential |
| [`listTypes`](#listtypes)           | Every credential type of the tenant, by name.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | Credential |
| [`mine`](#mine)                     | The caller's own credentials in the tenant, newest first (the last 100), with their state. Refused for delegated agent sessions (`ACCESS_DENIED`).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | Credential |
| [`nonce`](#nonce)                   | A single-use proof nonce (`c_nonce`) for the tenant's issuer, valid five minutes. Nonces are signed rather than stored, so handing one out writes nothing; a proof spends its nonce. Public.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | Public     |
| [`reinstate`](#reinstate)           | Lifts a suspension.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | Credential |
| [`request`](#request)               | Issues a credential of `type` to the caller, bound to the key in `proof`. The proof is an `openid4vci-proof+jwt` with the holder's public key in its `jwk` header, the issuer URL as `aud`, and a nonce from `nonce`. Holder keys may be ES256, ES384 or EdDSA. Returns the SD-JWT VC and its record. The credential ends at the type's lifetime, the person's scheduled account expiry, or the end of the time-limited grant that allows `vc:request` (an expiring binding or membership, a just-in-time activation, an access window), whichever comes first. From an assumed role or temporary credentials it ends with that session.                                                                                                                                                    | Credential |
| [`retireKey`](#retirekey)           | Stops publishing a previous key, after which credentials it signed no longer verify.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | Credential |
| [`revoke`](#revoke)                 | Revokes a credential for good: its status list entry becomes invalid. Holders may revoke their own credentials from their own session; delegated agent sessions and credentials narrowed by a session policy, and anyone revoking someone else's, need the permission.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      | Credential |
| [`rotateKey`](#rotatekey)           | Starts signing with a new ES256 key. The current one stays published as `previous`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         | Credential |
| [`status`](#status)                 | The issuer at a glance: its identifier and metadata URL, the published keys, the number of credential types, issued credentials by state (valid, suspended, revoked, expired), and status lists.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            | Credential |
| [`suspend`](#suspend)               | Suspends a valid credential (a lost phone). Verifiers see it as suspended until `reinstate`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                | Credential |
| [`updateType`](#updatetype)         | Changes a type's display, claims, lifetime, `requireMfa` or `enabled`. The name and `vct` never change.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | Credential |
| [`verify`](#verify)                 | Verifies a presentation of a credential this deployment issued. It checks the issuer's signature (the tenant's current or previous keys), validity times, every disclosure against the signed digests, the key-binding JWT (`audience`, `nonce`, freshness, `sd_hash`) and the credential's status. Returns `{ valid: true, claims, disclosed, type, ... }`, or `{ valid: false, reason }` with reasons such as `revoked`, `suspended`, `wrong-audience`, `wrong-nonce`, `bad-disclosure` or `unknown-issuer`. `audience` and `nonce` are required (`INVALID_INPUT` without them), since a presentation captured by one verifier would otherwise verify for any other; the result's `keyBinding` (`audience`, `nonce`, `issuedAt`) is there for your own record of accepted nonces. Public. | Public     |

## available [#available]

The enabled types the caller may request for themselves right now: `vc:request` allowed on `credential-type/{name}`, MFA when the type asks, and not in a "view as" session or a delegated agent session. No permission needed.

**HTTP:** `POST /api/iam/verifiableCredentials/available` (requires a credential) · **Browser client:** `client.verifiableCredentials.available()`

```ts title="Signature"
iam.api.verifiableCredentials.available(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<VcCredentialTypeView[]>
```

## createOffer [#createoffer]

An OpenID4VCI credential offer with a pre-authorized code, and its `openid-credential-offer://` link for a wallet to scan. It is for the caller (`vc:request`, like `request`) or, with `identityId`, for someone else (`iam:vc:issue`). `txCode: true` adds a 6-digit PIN the wallet asks for; give it to the person separately. The code works once and expires after the deployment's `offerLifetimeMs`. A token request without the PIN does not count as a guess; five wrong PINs end the offer. For self-service offers, policy and MFA are decided again when the wallet redeems the offer, and the credential ends with the grants that allow it. An administrator's offer records them as the credential's `issuedBy`.

**HTTP:** `POST /api/iam/verifiableCredentials/createOffer` (requires a credential) · **Browser client:** `client.verifiableCredentials.createOffer()`

* **Audited as:** `vc:offer:create`; refused redemptions (wrong PIN, lockout, access changed) as a denied
  `vc:offer:redeem`.
* **Errors:** as `request`; `IDENTITY_INACTIVE` for someone who is not an active member.

```ts title="Signature"
iam.api.verifiableCredentials.createOffer(
  credential: CredentialInput,
  input: { tenantId: string; type: string; identityId?: string; txCode?: boolean },
): Promise<VcOfferResult>
```

## createType [#createtype]

Defines a credential type: a `name`, a `displayName` and description for wallets, card colors, a lifetime (default 30 days, at most the deployment's `maxLifetimeMs`), `requireMfa`, and 1 to 32 `claims`. Each claim takes its value from a `source`: `email`, `emailVerified`, `name`, `identityId`, `kind`, `tenantId`, `tenantName`, `teams`, `department`, a declared identity attribute (`attribute:{name}`) or a `static` value. It is selectively disclosable unless `selective: false`, and left out when missing unless `required`. The `vct` defaults to `{issuer}/types/{name}`; a custom one may not sit under another organization's issuer on this deployment. The first type also creates the tenant's issuer key.

**HTTP:** `POST /api/iam/verifiableCredentials/createType` (requires a credential) · **Browser client:** `client.verifiableCredentials.createType()`

* **Permission:** `iam:vc:manage` on `iam/vc/types/{name}`.
* **Errors:** `CONFLICT` for a name in use; `INVALID_INPUT` for reserved or repeated claim names, unknown sources,
  invalid colors, or a `vct` under another organization's issuer.

```ts title="Signature"
iam.api.verifiableCredentials.createType(
  credential: CredentialInput,
  input: VcCredentialTypeInput,
): Promise<VcCredentialTypeView>
```

## deleteType [#deletetype]

Deletes a type and its pending offers.

**HTTP:** `POST /api/iam/verifiableCredentials/deleteType` (requires a credential) · **Browser client:** `client.verifiableCredentials.deleteType()`

* **Permission:** `iam:vc:manage` on `iam/vc/types/{name}`.
* **Errors:** `RESOURCE_IN_USE` while valid credentials of the type exist.

```ts title="Signature"
iam.api.verifiableCredentials.deleteType(
  credential: CredentialInput,
  input: { tenantId: string; name: string },
): Promise<{ deleted: boolean }>
```

## getType [#gettype]

One credential type.

**HTTP:** `POST /api/iam/verifiableCredentials/getType` (requires a credential) · **Browser client:** `client.verifiableCredentials.getType()`

* **Permission:** `iam:vc:read` on `iam/vc/types/{name}`.

```ts title="Signature"
iam.api.verifiableCredentials.getType(
  credential: CredentialInput,
  input: { tenantId: string; name: string },
): Promise<VcCredentialTypeView>
```

## issuerMetadata [#issuermetadata]

The tenant's OpenID4VCI credential issuer metadata: endpoints, and every enabled type with its format (`dc+sd-jwt`), `vct`, binding methods, proof algorithms and wallet display. The same document is served at `/.well-known/openid-credential-issuer{basePath}/vc/{tenantId}`. Public.

**HTTP:** `POST /api/iam/verifiableCredentials/issuerMetadata` (no credential) · **Browser client:** `client.verifiableCredentials.issuerMetadata()`

```ts title="Signature"
iam.api.verifiableCredentials.issuerMetadata(
  input: { tenantId: string },
): Promise<{
  credential_issuer: string;
  credential_endpoint: string;
  nonce_endpoint: string;
  display: { name: string; locale: string }[];
  credential_configurations_supported: {
    [k: string]: {
      format: string;
      vct: string;
      scope: string;
      cryptographic_binding_methods_supported: string[];
      credential_signing_alg_values_supported: string[];
      proof_types_supported: { jwt: { proof_signing_alg_values_supported: string[] } };
      credential_metadata: {
        display: {
          text_color?: string | undefined;
          background_color?: string | undefined;
          description?: string | undefined;
          name: string;
          locale: string;
        }[];
        claims: {
          display?: { name: string; locale: string }[] | undefined;
          path: string[];
        }[];
      };
    };
  };
}>
```

## listIssued [#listissued]

Issued credentials, newest first, by `type`, `identityId` or `state` (`valid`, `suspended`, `revoked`, `expired`), with `limit` (at most 500) and `offset`. Records hold claim names, never values.

**HTTP:** `POST /api/iam/verifiableCredentials/listIssued` (requires a credential) · **Browser client:** `client.verifiableCredentials.listIssued()`

* **Permission:** `iam:vc:read` on `iam/vc/credentials`.

```ts title="Signature"
iam.api.verifiableCredentials.listIssued(
  credential: CredentialInput,
  input: {
    tenantId: string;
    type?: string;
    identityId?: string;
    state?: VcIssuedView['state'];
    limit?: number;
    offset?: number;
  },
): Promise<{ credentials: VcIssuedView[]; total: number }>
```

## listKeys [#listkeys]

The issuer's signing keys, oldest first: `active`, `previous` (still published, so its credentials verify) and `retired`.

**HTTP:** `POST /api/iam/verifiableCredentials/listKeys` (requires a credential) · **Browser client:** `client.verifiableCredentials.listKeys()`

* **Permission:** `iam:vc:read` on `iam/vc/keys`.

```ts title="Signature"
iam.api.verifiableCredentials.listKeys(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<VcIssuerKeyView[]>
```

## listTypes [#listtypes]

Every credential type of the tenant, by name.

**HTTP:** `POST /api/iam/verifiableCredentials/listTypes` (requires a credential) · **Browser client:** `client.verifiableCredentials.listTypes()`

* **Permission:** `iam:vc:read` on `iam/vc/types`.

```ts title="Signature"
iam.api.verifiableCredentials.listTypes(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<VcCredentialTypeView[]>
```

## mine [#mine]

The caller's own credentials in the tenant, newest first (the last 100), with their state. Refused for delegated agent sessions (`ACCESS_DENIED`).

**HTTP:** `POST /api/iam/verifiableCredentials/mine` (requires a credential) · **Browser client:** `client.verifiableCredentials.mine()`

```ts title="Signature"
iam.api.verifiableCredentials.mine(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<VcIssuedView[]>
```

## nonce [#nonce]

A single-use proof nonce (`c_nonce`) for the tenant's issuer, valid five minutes. Nonces are signed rather than stored, so handing one out writes nothing; a proof spends its nonce. Public.

**HTTP:** `POST /api/iam/verifiableCredentials/nonce` (no credential) · **Browser client:** `client.verifiableCredentials.nonce()`

```ts title="Signature"
iam.api.verifiableCredentials.nonce(
  input: { tenantId: string },
): Promise<{ nonce: string; expiresIn: number }>
```

## reinstate [#reinstate]

Lifts a suspension.

**HTTP:** `POST /api/iam/verifiableCredentials/reinstate` (requires a credential) · **Browser client:** `client.verifiableCredentials.reinstate()`

* **Permission:** `iam:vc:revoke` on `iam/vc/credentials/{id}`.
* **Errors:** `INVALID_TRANSITION` unless the credential is suspended.

```ts title="Signature"
iam.api.verifiableCredentials.reinstate(
  credential: CredentialInput,
  input: { tenantId: string; credentialId: string },
): Promise<VcIssuedView>
```

## request [#request]

Issues a credential of `type` to the caller, bound to the key in `proof`. The proof is an `openid4vci-proof+jwt` with the holder's public key in its `jwk` header, the issuer URL as `aud`, and a nonce from `nonce`. Holder keys may be ES256, ES384 or EdDSA. Returns the SD-JWT VC and its record. The credential ends at the type's lifetime, the person's scheduled account expiry, or the end of the time-limited grant that allows `vc:request` (an expiring binding or membership, a just-in-time activation, an access window), whichever comes first. From an assumed role or temporary credentials it ends with that session.

**HTTP:** `POST /api/iam/verifiableCredentials/request` (requires a credential) · **Browser client:** `client.verifiableCredentials.request()`

* **Permission:** a session, not a delegated agent session; `vc:request` on `credential-type/{type}`, and MFA when the
  type requires it.
* **Audited as:** `vc:credential:issue` (type, holder key thumbprint, claim names); a refusal as a denied `vc:request`.
* **Errors:** `ACCESS_DENIED`; `MFA_REQUIRED`; `TYPE_DISABLED`; `IMPERSONATION_RESTRICTED`; `INVALID_PROOF`;
  `INVALID_NONCE`; `CLAIM_UNAVAILABLE` when a required claim has no value; `ACCESS_EXPIRING` when the grant that
  allows it ends within a minute.

```ts title="Signature"
iam.api.verifiableCredentials.request(
  credential: CredentialInput,
  input: { tenantId: string; type: string; proof: string },
): Promise<{ credential: string; record: VcIssuedView }>
```

## retireKey [#retirekey]

Stops publishing a previous key, after which credentials it signed no longer verify.

**HTTP:** `POST /api/iam/verifiableCredentials/retireKey` (requires a credential) · **Browser client:** `client.verifiableCredentials.retireKey()`

* **Permission:** `iam:vc:manage` on `iam/vc/keys`.
* **Errors:** `RESOURCE_IN_USE` while valid credentials it signed exist, unless `force: true`, which revokes them;
  `INVALID_TRANSITION` for the active key.

```ts title="Signature"
iam.api.verifiableCredentials.retireKey(
  credential: CredentialInput,
  input: { tenantId: string; kid: string; force?: boolean },
): Promise<VcIssuerKeyView>
```

## revoke [#revoke]

Revokes a credential for good: its status list entry becomes invalid. Holders may revoke their own credentials from their own session; delegated agent sessions and credentials narrowed by a session policy, and anyone revoking someone else's, need the permission.

**HTTP:** `POST /api/iam/verifiableCredentials/revoke` (requires a credential) · **Browser client:** `client.verifiableCredentials.revoke()`

* **Permission:** none for your own from your own session; otherwise `iam:vc:revoke` on `iam/vc/credentials/{id}`.
* **Audited as:** `vc:credential:revoke` (own) or `iam:vc:revoke`.
* **Errors:** `INVALID_TRANSITION` for a credential already revoked.

```ts title="Signature"
iam.api.verifiableCredentials.revoke(
  credential: CredentialInput,
  input: { tenantId: string; credentialId: string; reason?: string },
): Promise<VcIssuedView>
```

## rotateKey [#rotatekey]

Starts signing with a new ES256 key. The current one stays published as `previous`.

**HTTP:** `POST /api/iam/verifiableCredentials/rotateKey` (requires a credential) · **Browser client:** `client.verifiableCredentials.rotateKey()`

* **Permission:** `iam:vc:manage` on `iam/vc/keys`.

```ts title="Signature"
iam.api.verifiableCredentials.rotateKey(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<VcIssuerKeyView>
```

## status [#status]

The issuer at a glance: its identifier and metadata URL, the published keys, the number of credential types, issued credentials by state (valid, suspended, revoked, expired), and status lists.

**HTTP:** `POST /api/iam/verifiableCredentials/status` (requires a credential) · **Browser client:** `client.verifiableCredentials.status()`

* **Permission:** `iam:vc:read` on `iam/vc/issuer`.

```ts title="Signature"
iam.api.verifiableCredentials.status(
  credential: CredentialInput,
  input: { tenantId: string },
): Promise<{
  issuer: string;
  metadataUrl: string;
  keys: VcIssuerKeyView[];
  types: number;
  credentials: { valid: number; suspended: number; revoked: number; expired: number };
  statusLists: number;
}>
```

## suspend [#suspend]

Suspends a valid credential (a lost phone). Verifiers see it as suspended until `reinstate`.

**HTTP:** `POST /api/iam/verifiableCredentials/suspend` (requires a credential) · **Browser client:** `client.verifiableCredentials.suspend()`

* **Permission:** `iam:vc:revoke` on `iam/vc/credentials/{id}`.
* **Errors:** `INVALID_TRANSITION` unless the credential is valid.

```ts title="Signature"
iam.api.verifiableCredentials.suspend(
  credential: CredentialInput,
  input: { tenantId: string; credentialId: string; reason?: string },
): Promise<VcIssuedView>
```

## updateType [#updatetype]

Changes a type's display, claims, lifetime, `requireMfa` or `enabled`. The name and `vct` never change.

**HTTP:** `POST /api/iam/verifiableCredentials/updateType` (requires a credential) · **Browser client:** `client.verifiableCredentials.updateType()`

* **Permission:** `iam:vc:manage` on `iam/vc/types/{name}`.

```ts title="Signature"
iam.api.verifiableCredentials.updateType(
  credential: CredentialInput,
  input: VcCredentialTypeUpdate,
): Promise<VcCredentialTypeView>
```

## verify [#verify]

Verifies a presentation of a credential this deployment issued. It checks the issuer's signature (the tenant's current or previous keys), validity times, every disclosure against the signed digests, the key-binding JWT (`audience`, `nonce`, freshness, `sd_hash`) and the credential's status. Returns `{ valid: true, claims, disclosed, type, ... }`, or `{ valid: false, reason }` with reasons such as `revoked`, `suspended`, `wrong-audience`, `wrong-nonce`, `bad-disclosure` or `unknown-issuer`. `audience` and `nonce` are required (`INVALID_INPUT` without them), since a presentation captured by one verifier would otherwise verify for any other; the result's `keyBinding` (`audience`, `nonce`, `issuedAt`) is there for your own record of accepted nonces. Public.

**HTTP:** `POST /api/iam/verifiableCredentials/verify` (no credential) · **Browser client:** `client.verifiableCredentials.verify()`

```ts title="Signature"
iam.api.verifiableCredentials.verify(
  input: { presentation: string; audience?: string; nonce?: string; tenantId?: string },
): Promise<VcVerification>
```
