---
title: "Isolated Flow"
description: "Drive OID4VCI credential receiving step by step: isolated endpoints, the authorization-code grant, deferred issuance, and advanced options."
stack: "Community Stack (open source) — 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/community-stack/wallet2/credential-receiving/isolated-flow
generated: 2026-10-01
---
# Isolated Flow

The [full-flow endpoint](https://docs.walt.id/community-stack/wallet2/credential-receiving/overview.md) runs the whole [**OID4VCI**](https://docs.walt.id/concepts/data-exchange-protocols/openid4vci.md) exchange in a single call. When you're building a wallet UI and need to show intermediate state, prompt for a PIN, support the authorization-code grant, or handle deferred issuance, drive the **isolated endpoints** explained on this page yourself.

[**OID4VCI**](https://docs.walt.id/concepts/data-exchange-protocols/openid4vci.md): The protocol used to deliver the credential from the issuer to the wallet.

[Resolve the offer](#resolve-the-offer) returns a **consent-oriented** preview — issuer branding and per-offer credential details — so UIs can show what will be issued before accepting.

All endpoints on this page are rooted at `/wallet/{walletId}/credentials/receive`. The examples use `http://localhost:7006` as the base URL (Issuer2 defaults to `7005`).

## Prerequisites

Before you begin, ensure you have:

- **Wallet API v2 running** — Follow the [Setup](https://docs.walt.id/community-stack/wallet2/setup.md) guide.
- **A wallet holding a key** — [Create a wallet](https://docs.walt.id/community-stack/wallet2/wallets.md), then [generate a key](https://docs.walt.id/community-stack/wallet2/key-management/local.md) into it. You'll need the wallet's `walletId`.
- **A credential offer** — Use an `openid-credential-offer://` URL or inline offer JSON from an issuer such as [Issuer2](https://docs.walt.id/community-stack/issuer2/getting-started.md).

---

## When to Use the Isolated Flow

Generally speaking, you should use the isolated flow when building any form of wallet user interface leveraging the wallet API v2. This allows you to properly step through the credential receiving process and build the necessary UI elements accordingly.

| You want to… | Use |
|---|---|
| Claim a credential in one call | [Full flow](https://docs.walt.id/community-stack/wallet2/credential-receiving/overview.md) |
| Preview the offer before claiming | [Resolve the offer](#resolve-the-offer) first |
| Issuer requires user auth before claiming | [Authorization-code flow](#authorization-code-flow) |
| Poll for a credential issued later | [Deferred credentials](#deferred-credentials) |
| Manage key material outside the wallet | [Inline keys](#inline-keys) |

---

## Pre-Authorized Code Flow

The isolated pre-authorized flow is:

```text
1. Resolve Offer  →  2. Request Token  →  2b. Get Nonce  →  3. Sign Proof  →  4. Fetch Credential
```

### Resolve the Offer

Parse a credential offer and fetch the issuer metadata needed for a consent preview and the later steps.

**Option: CURL**

**Endpoint:** `POST /wallet/{walletId}/credentials/receive/resolve-offer` | [API Reference](https://docs.walt.id/community-stack/wallet2/full-api-reference.md)

##### Example Request

```bash
curl -X POST http://localhost:7006/wallet/{walletId}/credentials/receive/resolve-offer \
  -H 'Content-Type: application/json' \
  -d '{
    "offerUrl": "openid-credential-offer://?credential_offer_uri=http%3A%2F%2Flocalhost%3A7005%2Fopenid4vci%2Fcredential-offer%3Fid%3D48f1dc54-134c-4e10-b889-17f3c0a595bf"
  }'
```

**Path Parameters**

- **walletId**: _String_ (required) - The wallet handling the exchange. See [how to obtain it](https://docs.walt.id/community-stack/wallet2/wallets.md#create-a-wallet).

**Body Parameters**

- **Credential offer** (required) — provide exactly one of:
    - **offerUrl**: _String_ - The OID4VCI credential offer URL.
    - **offerJson**: _Object_ - The credential offer as an inline JSON object.

---

##### Example Response

```json
{
  "credentialIssuer": "http://localhost:7005/openid4vci",
  "credentialConfigurationIds": ["identity_credential"],
  "grantType": "pre-authorized_code",
  "preAuthorizedCode": "SplxlOBeZQQYbYS6WxSbIA",
  "txCodeRequired": true,
  "credentialEndpoint": "http://localhost:7005/openid4vci/credential",
  "tokenEndpoint": "http://localhost:7005/openid4vci/token",
  "nonceEndpoint": "http://localhost:7005/openid4vci/nonce",
  "issuer": {
    "credentialIssuer": "http://localhost:7005/openid4vci",
    "display": {
      "name": "Example Issuer",
      "locale": "en-US",
      "logoUri": "https://issuer.example.com/logo.png",
      "logoAltText": "Example Issuer"
    }
  },
  "offeredCredentials": [
    {
      "configurationId": "identity_credential",
      "format": "dc+sd-jwt",
      "scope": null,
      "vct": "https://issuer.example.com/identity_credential",
      "doctype": null,
      "display": {
        "name": "Identity Credential",
        "locale": "en-US",
        "description": "Personal identity claims"
      },
      "claims": [
        {
          "path": ["given_name"],
          "mandatory": true,
          "displayName": "Given name"
        },
        {
          "path": ["family_name"],
          "mandatory": false,
          "displayName": "Family name"
        }
      ]
    }
  ],
  "transactionCode": {
    "inputMode": "numeric",
    "length": 6,
    "description": "Enter the PIN you received from the issuer"
  }
}
```

**Response Fields**

- **credentialIssuer**: _String_ - Issuer identifier from the offer.
- **credentialConfigurationIds**: _String[]_ - Flat list of credential configuration IDs listed in the offer.
- **grantType**: _String_ - `pre-authorized_code` or `authorization_code`.
- **preAuthorizedCode**: _String_ (optional) - The pre-authorized code from the credential offer, returned when the offer uses the pre-authorized code grant.
- **txCodeRequired**: _Boolean_ - Whether the pre-authorized flow requires a transaction code (PIN).
- **credentialEndpoint**: _String_ - The issuer's credential endpoint, from its metadata.
- **tokenEndpoint**: _String_ (optional) - The token endpoint from the issuer's authorization-server metadata.
- **nonceEndpoint**: _String_ (optional) - The issuer's nonce endpoint for `c_nonce` flows (OID4VCI 1.0).
- **issuer**: _Object_ - Issuer metadata projection for consent UIs (`credentialIssuer`, optional localized `display` with `name`, `locale`, `logoUri`, `logoAltText`, and related branding fields when present).
- **offeredCredentials**: _Object[]_ - Per-configuration offer details for the UI (not strings). Each entry includes:
  - **configurationId** / **format** — configuration id and credential format.
  - **scope** / **vct** / **doctype** — format-specific identifiers when present.
  - **display** — localized credential display (name, description, colors, logo, …).
  - **claims** — declared claim metadata (`path`, `mandatory`, `displayName`).
- **transactionCode**: _Object_ (optional) - UI hints for collecting a transaction code when required (`inputMode` `numeric` or `text`, optional `length`, optional `description`). Distinct from the PIN value you send later as `txCode`.

### Request Token

Exchange the pre-authorized code for an access token.

**Option: CURL**

**Endpoint:** `POST /wallet/{walletId}/credentials/receive/request-token` | [API Reference](https://docs.walt.id/community-stack/wallet2/full-api-reference.md)

##### Example Request

```bash
curl -X POST http://localhost:7006/wallet/{walletId}/credentials/receive/request-token \
  -H 'Content-Type: application/json' \
  -d '{
    "tokenEndpoint": "http://localhost:7005/openid4vci/token",
    "preAuthorizedCode": "SplxlOBeZQQYbYS6WxSbIA",
    "credentialIssuer": "http://localhost:7005/openid4vci",
    "txCode": "123456"
  }'
```

**Body Parameters**

- **tokenEndpoint**: _String_ (required) - The issuer's token endpoint.
- **preAuthorizedCode**: _String_ (required) - The pre-authorized code from the credential offer.
- **credentialIssuer**: _String_ (optional) - Credential issuer base URL. When supplied, the wallet resolves authorization-server metadata and can apply client-attestation headers if configured.
- **txCode**: _String_ (optional) - Transaction code (PIN), if the offer requires one.
- **clientId**: _String_ (optional) - See [OAuth client identity](#oauth-client-identity). Defaults to `eudiw-abca`.
- **redirectUri**: _String_ (optional) - Defaults to `openid://`.
- **tokenRequestHeaders**: _Object_ (optional) - Additional headers to attach to the token request, such as `OAuth-Client-Attestation` and `OAuth-Client-Attestation-PoP`.
- **anonymousPreAuthorizedCode**: _Boolean_ (optional) - Request an anonymous pre-authorized-code token exchange when the issuer supports it. Defaults to `false`.

---

##### Example Response

```json
{
  "accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expiresIn": 300
}
```

**Response Fields**

- **accessToken**: _String_ - Use it in the fetch-credential request.
- **cNonce**: _String_ (optional) - Proof nonce, **only if** the issuer returns one in the token response. Under OID4VCI 1.0 the issuer (including Issuer2) serves the nonce from a dedicated **Nonce Endpoint** instead, so this is usually absent — fetch it in [Get a Nonce](#get-a-nonce) below.
- **expiresIn**: _Number_ - Token lifetime in seconds.

**Note:**

Under OID4VCI 1.0 the proof nonce (`c_nonce`) comes from the issuer's **Nonce Endpoint**, not the token response — so `request-token` typically returns no `cNonce`. Get it via [Get a Nonce](#get-a-nonce) before signing the proof.

### Get a Nonce

Under OID4VCI 1.0 the proof `c_nonce` comes from the issuer's dedicated **Nonce Endpoint**, not the token response. The wallet does not proxy it — call the **issuer's** nonce endpoint directly. Its URL is returned as **`nonceEndpoint`** from [resolve-offer](#resolve-the-offer) when the issuer publishes one (otherwise use the `nonce_endpoint` from the issuer's credential-issuer metadata; for [Issuer2](https://docs.walt.id/community-stack/issuer2/getting-started.md) it is `{credentialIssuer}/nonce`).

**Option: CURL**

**Endpoint:** `POST {credentialIssuer}/nonce` — the **issuer's** endpoint, not a wallet endpoint.

##### Example Request

```bash
curl -X POST http://localhost:7005/openid4vci/nonce
```

---

##### Example Response

```json
{
  "c_nonce": "wKI4LT5Sadf6a0dyih5b0Ws"
}
```

Use the returned `c_nonce` as the `nonce` in [Sign Proof](#sign-proof).

### Sign Proof

Sign a proof-of-possession JWT that proves the wallet controls the holder key.

**Option: CURL**

**Endpoint:** `POST /wallet/{walletId}/credentials/receive/sign-proof` | [API Reference](https://docs.walt.id/community-stack/wallet2/full-api-reference.md)

##### Example Request

```bash
curl -X POST http://localhost:7006/wallet/{walletId}/credentials/receive/sign-proof \
  -H 'Content-Type: application/json' \
  -d '{
    "issuerUrl": "http://localhost:7005/openid4vci",
    "nonce": "wKI4LT5Sadf6a0dyih5b0Ws",
    "credentialConfigurationId": "identity_credential"
  }'
```

**Body Parameters**

- **issuerUrl**: _String_ (required) - The credential issuer URL, used as the proof audience.
- **nonce**: _String_ (required) - The `c_nonce` from [Get a Nonce](#get-a-nonce) (or from the token response, if the issuer returned one).
- **credentialConfigurationId**: _String_ (required) - The credential configuration ID being requested.
- **keyId**: _String_ (optional) - Key from the wallet's key store. Defaults to the wallet's default key.
- **did**: _String_ (optional) - DID to use as the proof key ID. If omitted, the proof carries the JWK directly.
- **key**: _Object_ (optional) - An [inline key](#inline-keys). Takes precedence over `keyId`.

---

##### Example Response

```json
{
  "proofJwt": "eyJhbGciOiJFUzI1NiIsInR5cCI6Im9wZW5pZDR2Y2ktcHJvb2Yrand0In0..."
}
```

### Fetch Credential

Request the credential from the issuer's credential endpoint.

**Option: CURL**

**Endpoint:** `POST /wallet/{walletId}/credentials/receive/fetch-credential` | [API Reference](https://docs.walt.id/community-stack/wallet2/full-api-reference.md)

##### Example Request

```bash
curl -X POST http://localhost:7006/wallet/{walletId}/credentials/receive/fetch-credential \
  -H 'Content-Type: application/json' \
  -d '{
    "credentialEndpoint": "http://localhost:7005/openid4vci/credential",
    "accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
    "credentialConfigurationId": "identity_credential",
    "proofJwt": "eyJhbGciOiJFUzI1NiIsInR5cCI6Im9wZW5pZDR2Y2ktcHJvb2Yrand0In0...",
    "storeInWallet": true
  }'
```

**Body Parameters**

- **credentialEndpoint**: _String_ (required) - The issuer's credential endpoint.
- **accessToken**: _String_ (required) - The access token from the token response.
- **credentialConfigurationId**: _String_ (required) - The credential configuration ID to request.
- **proofJwt**: _String_ (optional) - The proof JWT from the sign-proof step. Required if the issuer requires proof.
- **clientId**: _String_ (optional) - See [OAuth client identity](#oauth-client-identity). Defaults to `eudiw-abca`.
- **storeInWallet**: _Boolean_ (optional) - Store the fetched credential(s) in the wallet credential store. Defaults to `false`.

---

##### Example Response

```json
{
  "rawCredentials": [
    "eyJraWQiOiJkaWQ6a2V5Ono2TWtmcmlxMU1xTEJvUFdlY0dvRExqZ3VvMXNCOWJyajZ3VDNxWjVCeGtLcHVQNiJ9..."
  ]
}
```

**Note:**

The isolated fetch endpoint always returns raw credential strings. Set `storeInWallet` to `true` to store them during this call. If you leave it as `false`, store a returned credential later with `POST /wallet/{walletId}/credentials/import`, or use the [full flow](https://docs.walt.id/community-stack/wallet2/credential-receiving/overview.md), which stores automatically.

---

## Authorization-Code Flow

When the issuer requires the holder to log in first, use the authorization-code grant. It replaces the "request token" step with a redirect through the issuer's authorization server.

```text
1. Resolve Offer  →  2. Generate Authorization URL  →  (user logs in, issuer redirects back)
→  3. Exchange Code  →  4. Sign Proof  →  5. Fetch Credential
```

Steps 1, 4 and 5 are identical to the pre-authorized flow above.

### Generate Authorization URL

Build the authorization URL to redirect the user to.

**Option: CURL**

**Endpoint:** `POST /wallet/{walletId}/credentials/receive/authorization-url` | [API Reference](https://docs.walt.id/community-stack/wallet2/full-api-reference.md)

##### Example Request

```bash
curl -X POST http://localhost:7006/wallet/{walletId}/credentials/receive/authorization-url \
  -H 'Content-Type: application/json' \
  -d '{
    "offerUrl": "openid-credential-offer://?credential_offer_uri=...",
    "redirectUri": "https://wallet.example.org/callback",
    "usePkce": true
  }'
```

**Body Parameters**

- **Credential offer** (required) — provide exactly one of:
    - **offerUrl**: _String_ - The OID4VCI credential offer URL.
    - **offerJson**: _Object_ - The credential offer as an inline JSON object.
- **redirectUri**: _String_ (optional) - Redirect URI registered with the issuer. Defaults to `openid://`.
- **usePkce**: _Boolean_ (optional) - Whether to use PKCE. Defaults to `true`.
- **clientId**: _String_ (optional) - See [OAuth client identity](#oauth-client-identity). Defaults to `eudiw-abca`.

---

##### Example Response

```json
{
  "authorizationUrl": "http://localhost:7005/openid4vci/authorize?response_type=code&client_id=eudiw-abca&state=abc123&code_challenge=...",
  "state": "abc123",
  "codeVerifier": "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM",
  "credentialConfigurationId": "identity_credential",
  "credentialIssuerBaseUrl": "http://localhost:7005/openid4vci",
  "nonceEndpoint": "http://localhost:7005/openid4vci/nonce"
}
```

**Response Fields**

- **authorizationUrl**: _String_ - Redirect the user here.
- **state**: _String_ - Returned to your redirect URI; use it to correlate the response.
- **codeVerifier**: _String_ - Store it — you'll need it to exchange the code (PKCE).
- **credentialConfigurationId**: _String_ - The credential being requested.
- **credentialIssuerBaseUrl**: _String_ - Credential issuer base URL. Pass this to [Exchange Code](#exchange-code).
- **nonceEndpoint**: _String_ (optional) - The issuer's nonce endpoint for `c_nonce` flows (OID4VCI 1.0), when published. See [Get a Nonce](#get-a-nonce).

After the user authenticates, the issuer redirects to your `redirectUri` with `code` and `state` query parameters.

### Exchange Code

Exchange the authorization code for an access token.

**Option: CURL**

**Endpoint:** `POST /wallet/{walletId}/credentials/receive/exchange-code` | [API Reference](https://docs.walt.id/community-stack/wallet2/full-api-reference.md)

##### Example Request

```bash
curl -X POST http://localhost:7006/wallet/{walletId}/credentials/receive/exchange-code \
  -H 'Content-Type: application/json' \
  -d '{
    "code": "SplxlOBeZQQYbYS6WxSbIA",
    "credentialIssuerBaseUrl": "http://localhost:7005/openid4vci",
    "codeVerifier": "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM",
    "redirectUri": "https://wallet.example.org/callback"
  }'
```

**Body Parameters**

- **code**: _String_ (required) - The authorization code returned to your redirect URI.
- **credentialIssuerBaseUrl**: _String_ (required) - Credential issuer base URL returned by [Generate Authorization URL](#generate-authorization-url). The wallet uses it to resolve authorization-server metadata, including the token endpoint.
- **codeVerifier**: _String_ (optional) - The PKCE code verifier from the authorization-url step.
- **redirectUri**: _String_ (optional) - The redirect URI used in the authorization request. Defaults to `openid://`.
- **clientId**: _String_ (optional) - See [OAuth client identity](#oauth-client-identity). Defaults to `eudiw-abca`.
- **tokenRequestHeaders**: _Object_ (optional) - Additional headers to attach to the token request.

---

##### Example Response

Returns the same shape as [Request Token](#request-token) — an `accessToken`, optional `cNonce`, and `expiresIn`. Continue with [Sign Proof](#sign-proof) and [Fetch Credential](#fetch-credential).

---

## Deferred Credentials

If the issuer isn't ready to issue immediately, the receive result returns a `transaction_id` in `deferredTransactionIds` instead of a credential. Poll the deferred endpoint with that ID until the credential is ready.

**Option: CURL**

**Endpoint:** `POST /wallet/{walletId}/credentials/receive/deferred` | [API Reference](https://docs.walt.id/community-stack/wallet2/full-api-reference.md)

##### Example Request

```bash
curl -X POST http://localhost:7006/wallet/{walletId}/credentials/receive/deferred \
  -H 'Content-Type: application/json' \
  -d '{
    "deferredCredentialEndpoint": "http://localhost:7005/openid4vci/credential_deferred",
    "transactionId": "8xFQ5j9Q8L-3PUdnl4RWxXgB7oNc3mK2pL9vR6tY1zA",
    "accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
  }'
```

**Body Parameters**

- **deferredCredentialEndpoint**: _String_ (required) - The deferred credential endpoint from the issuer metadata.
- **transactionId**: _String_ (required) - The transaction ID returned when issuance was deferred.
- **accessToken**: _String_ (required) - The access token from the original token response.

---

##### Example Response

```json
{
  "credentialIds": ["550e8400-e29b-41d4-a716-446655440000"],
  "deferredTransactionIds": {}
}
```

If the issuer is still not ready, `credentialIds` comes back empty — retry later.

---

## Advanced Options

### OAuth Client Identity

Several endpoints accept a `clientId`. It is the **OAuth 2.0** `client_id` — an identifier in the OAuth 2.0 authorization protocol — the wallet presents to the **issuer's authorization server** in token and authorization requests (and it feeds client-attestation headers when those are used).

The default, `eudiw-abca`, works with issuers that don't require client registration — including the walt.id [Issuer2](https://docs.walt.id/community-stack/issuer2/getting-started.md) in its default configuration. Set `clientId` explicitly when the issuer's authorization server requires a registered or attested client identity.

### Inline Keys

The `sign-proof` step (and the [full-flow receive](https://docs.walt.id/community-stack/wallet2/credential-receiving/overview.md)) accept an inline `key` object instead of a `keyId`. An inline key takes precedence over `keyId` and the wallet's default key. Use it only when the caller manages key material outside the wallet store — most deployments should prefer `keyId` or the default key.

```json
{
  "key": {
    "type": "jwk",
    "jwk": { "kty": "EC", "crv": "P-256", "x": "...", "y": "...", "d": "..." }
  }
}
```

The `key` uses the same serialized shape as the [key import endpoint](https://docs.walt.id/community-stack/wallet2/key-management/local.md).

### Token Request Headers

`ReceiveCredentialRequest` includes a `tokenRequestHeaders` map, intended for headers such as `OAuth-Client-Attestation` and `OAuth-Client-Attestation-PoP`.

**Note:**

The token request endpoints forward `tokenRequestHeaders` to the issuer's token endpoint. If `credentialIssuer` or `credentialIssuerBaseUrl` is supplied and client attestation is configured, the wallet can also build attestation headers from issuer metadata.

---

## Endpoint Reference

| Endpoint | Method | Description |
|----------|--------|-------------|
| `/wallet/{walletId}/credentials/receive` | POST | Full pre-authorized code flow |
| `/wallet/{walletId}/credentials/receive/resolve-offer` | POST | Resolve a credential offer |
| `/wallet/{walletId}/credentials/receive/request-token` | POST | Exchange a pre-authorized code for a token |
| `/wallet/{walletId}/credentials/receive/authorization-url` | POST | Generate an authorization-code redirect URL |
| `/wallet/{walletId}/credentials/receive/exchange-code` | POST | Exchange an authorization code for a token |
| `/wallet/{walletId}/credentials/receive/sign-proof` | POST | Sign a proof-of-possession JWT |
| `/wallet/{walletId}/credentials/receive/fetch-credential` | POST | Fetch credential(s) from the issuer |
| `/wallet/{walletId}/credentials/receive/deferred` | POST | Poll deferred credential issuance |
