Create a Credential Offer

This guide walks you through creating a credential offer from an existing profile. The offer generates an OID4VCI credential offer URL that can be claimed by any compliant wallet.

Prerequisites

Before creating an offer, ensure you have:

  1. Issuer2 Service – A running issuer2 service
  2. Credential Profile – An active credential profile

Create an Offer

CURL

Endpoint: POST /v2/{target}/issuer-service-api/credentials/offers | API Reference

Example Request (Pre-Authorized Flow)

curl -X 'POST' \
  'https://{orgID}.enterprise-sandbox.waltid.dev/v2/{profileTarget}/issuer-service-api/credentials/offers' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer {yourToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "authMethod": "PRE_AUTHORIZED",
  "valueMode": "BY_REFERENCE",
  "expiresInSeconds": 300
}'

Body

{
  "authMethod": "PRE_AUTHORIZED",
  "valueMode": "BY_REFERENCE",
  "expiresInSeconds": 300
}

Path Parameters

  • orgID – Your organization's Base URL. For example, if your organization is named test, your default Base URL will be test.enterprise-sandbox.walt.dev when using the sandbox environment.
  • profileTarget – The resource identifier for the credential profile ({organizationID}.{tenantID}.{issuerServiceID}.{profileId}), e.g. waltid.tenant1.issuer1.profile-abc123

Body Parameters

Required Parameters

  • authMethod: String – The authentication method for the offer
    • PRE_AUTHORIZED – Pre-authorized code flow (no user authentication)
    • AUTHORIZED – Authorization code flow (user authentication required)

Optional Parameters

  • valueMode: String – How the credential offer is delivered (default: BY_REFERENCE)
    • BY_REFERENCE – Offer URL contains a reference; wallet fetches full offer
    • BY_VALUE – Full credential offer embedded in URL
  • issuerStateMode: String – Whether to include issuer state (default: OMIT)
    • OMIT – No issuer state
    • INCLUDE – Include issuer state for session correlation
  • expiresInSeconds: Integer – Offer validity duration in seconds (default: 300). Set to -1 for no expiration.
  • txCode: Object – Transaction code (PIN) configuration for pre-authorized flow
  • txCodeValue: String – Specific PIN value (if not provided, one is generated)
  • runtimeOverrides: Object – Override profile values for this offer. See Runtime Overrides.

Response

Success Response (201)

{
  "offerId": "abc123-def456-ghi789",
  "profileId": "waltid.tenant1.issuer1.profile-abc123",
  "profileVersion": 1,
  "authMethod": "PRE_AUTHORIZED",
  "issuerStateMode": "OMIT",
  "expiresAt": 1704067500000,
  "credentialOffer": "openid-credential-offer://?credential_offer_uri=https%3A%2F%2Fwaltid.enterprise-sandbox.waltid.dev%2Fv2%2Fwaltid.tenant1.issuer1%2Fopenid4vci%2Fcredential-offer%3Fid%3Dabc123-def456-ghi789"
}

Response Fields

  • offerId: String – Unique identifier for this offer
  • profileId: String – The profile used to create this offer
  • profileVersion: Integer – The version of the profile used
  • authMethod: String – The authentication method
  • issuerStateMode: String – The issuer state mode
  • expiresAt: Long – Unix timestamp when the offer expires
  • txCodeValue: String – The PIN value (only for pre-authorized flow with txCode)
  • credentialOffer: String – The OID4VCI credential offer URL

Transaction Code (PIN)

For pre-authorized flow, you can require a PIN for additional security:

{
  "authMethod": "PRE_AUTHORIZED",
  "txCode": {
    "inputMode": "numeric",
    "length": 6,
    "description": "Please enter the PIN sent to your email"
  }
}

txCode Parameters:

  • inputMode: String – Input type (numeric or text)
  • length: Integer – Length of the PIN
  • description: String – Description shown to the user

If you want to specify the PIN value:

{
  "authMethod": "PRE_AUTHORIZED",
  "txCode": {
    "inputMode": "numeric",
    "length": 6
  },
  "txCodeValue": "123456"
}

Runtime Overrides

You can override any profile value for a specific offer using runtimeOverrides:

{
  "authMethod": "PRE_AUTHORIZED",
  "runtimeOverrides": {
    "issuerDid": "did:key:z6MkNewDid...",
    "issuerKeyId": "waltid.tenant1.kms1.differentKey",
    "subjectId": "did:key:z6MkSubjectDid...",
    "credentialData": {
      "@context": [
        "https://www.w3.org/2018/credentials/v1"
      ],
      "type": ["VerifiableCredential", "CustomCredential"],
      "credentialSubject": {
        "customField": "customValue"
      }
    },
    "mapping": {
      "id": "<uuid>"
    },
    "credentialStatus": {
      "statusCredentialConfig": "waltid.tenant1.credentialstatus.config2",
      "initialStatus": "0x0"
    },
    "notifications": {
      "webhook": {
        "url": "https://different-webhook.com/callback"
      }
    }
  }
}

Available Override Fields

FieldDescription
issuerDidOverride the issuer DID
issuerKeyIdOverride the signing key
x5ChainOverride the X.509 certificate chain
subjectIdSet the credential subject ID
credentialDataOverride the credential data
mappingOverride the data mapping
idTokenClaimsMappingOverride ID token claims mapping
mDocNameSpacesDataMappingConfigOverride mDoc data mapping
credentialStatusOverride credential status configuration
notificationsOverride notification settings

Examples

Pre-Authorized Flow with PIN

curl -X 'POST' \
  'https://{orgID}.enterprise-sandbox.waltid.dev/v2/{profileTarget}/issuer-service-api/credentials/offers' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer {yourToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "authMethod": "PRE_AUTHORIZED",
  "txCode": {
    "inputMode": "numeric",
    "length": 4
  },
  "expiresInSeconds": 600
}'

Authorization Code Flow

curl -X 'POST' \
  'https://{orgID}.enterprise-sandbox.waltid.dev/v2/{profileTarget}/issuer-service-api/credentials/offers' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer {yourToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "authMethod": "AUTHORIZED",
  "issuerStateMode": "INCLUDE",
  "expiresInSeconds": 900
}'

Offer with Custom Credential Data

curl -X 'POST' \
  'https://{orgID}.enterprise-sandbox.waltid.dev/v2/{profileTarget}/issuer-service-api/credentials/offers' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer {yourToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "authMethod": "PRE_AUTHORIZED",
  "runtimeOverrides": {
    "credentialData": {
      "@context": [
        "https://www.w3.org/2018/credentials/v1",
        "https://www.w3.org/2018/credentials/examples/v1"
      ],
      "type": ["VerifiableCredential", "UniversityDegree"],
      "credentialSubject": {
        "degree": {
          "type": "MasterDegree",
          "name": "Master of Computer Science"
        }
      }
    }
  }
}'

Offer by Value

curl -X 'POST' \
  'https://{orgID}.enterprise-sandbox.waltid.dev/v2/{profileTarget}/issuer-service-api/credentials/offers' \
  -H 'accept: application/json' \
  -H 'Authorization: Bearer {yourToken}' \
  -H 'Content-Type: application/json' \
  -d '{
  "authMethod": "PRE_AUTHORIZED",
  "valueMode": "BY_VALUE"
}'

Using the Credential Offer

The credentialOffer URL in the response can be:

  1. Displayed as QR Code – User scans with their wallet app
  2. Sent as Deep Link – User clicks link on mobile device
  3. Embedded in Email/Message – User clicks to open in wallet

Example Decoded Offer URL

openid-credential-offer://?credential_offer_uri=https://waltid.enterprise-sandbox.waltid.dev/v2/waltid.tenant1.issuer1/openid4vci/credential-offer?id=abc123-def456-ghi789

When the wallet resolves this URL, it will:

  1. Fetch the full credential offer from the issuer
  2. Display the credential details to the user
  3. Request the credential using the appropriate flow

Next Steps

Last updated on April 8, 2026