# Create a credential offer.


Returns a deeplink and QR code for the wallet to scan. Optionally delivers the same offer out-of-band (currently: email).

Endpoint: POST /b2b/v1/credential-offers
Version: 1.0.0
Security: BearerAuth

## Security:

  - `BearerAuth` (unknown)
    http bearer JWT

## Header parameters:

  - `X-Org-Id` (string, required)
    Organization the call is acting on. The authenticated service user must be a member of this organization in DeepAdmin; otherwise the request returns 404.

  - `X-Environment` (string, required)
    Environment the call is scoped to. Resources are isolated per environment.

## Request fields (application/json):

  - `claims` (object, required)
    Claim values for the credential. Key-value object of claim data for the credential. Keys must match the claim names defined in the credential definition, and values must match the expected types (string, boolean, integer, number). All mandatory claims from the credential definition must be provided.

  - `credential_definition_id` (string, required)
    Credential definition to issue: the UUID of one of your organization's own
credential definitions. An official credential definition, or one that belongs to another organization, returns 404.

  - `delivery` (object)
    DeliverySpec selects an out-of-band delivery method for a freshly-created credential offer. Today only `via=email` is supported; future modes (sms, push) slot in here without changing the endpoint signature. Email is validated up-front so a bad input fails before we spend the work of creating the offer.

  - `delivery.email` (string)
    Email recipient, required when via=email.

  - `delivery.via` (string, required)
    "email" is the only accepted value today; any other value rejects.

  - `offer_validity_seconds` (integer)
    Optional offer validity in seconds (default 600). This is the TTL of the QR/deeplink window before the wallet has to pick up the offer, not the lifetime of the credential itself.

  - `reference_id` (string)
    Optional correlation identifier from your system; echoed on the resource.

  - `valid_until` (string)
    The credential's lifetime as an absolute calendar day (ISO 8601, YYYY-MM-DD): it expires at 23:59:59 of that day in the issuer's time zone (Europe/Zurich), whenever the wallet collects it. Mutually exclusive with validity_seconds.

  - `validity_seconds` (integer)
    Optional credential lifetime in seconds (SD-JWT "exp" = iat + N). Omit to fall back to the credential definition's default_validity_seconds; when both are absent the credential is issued without an exp claim (never expires). Bounded by [60, 10 years].

## Response 200:

  - `200` (unknown)
    Success response

## Response 200 fields (application/json):

  - `claims` (object, required)
    Claim values for the credential (echoed from the request).

  - `created_at` (string, required)
    When the offer was created (RFC3339).

  - `deeplink` (string, required)
    OID4VCI wallet deep link (e.g. openid-credential-offer://...).

  - `expires_at` (string, required)
    Offer TTL deadline (RFC3339).

  - `id` (string, required)
    UUID v4 identifier.
    Example: 550e8400-e29b-41d4-a716-446655440000

  - `qr_content_base64` (string, required)
    Base64-encoded PNG; QR payload is the same URI as deeplink.

  - `status` (string, required)
    Lifecycle state of a credential offer. `revoked`: a pending offer withdrawn with POST /b2b/v1/credential-offers/{offerId}/revoke.
    Enum: "pending", "issued", "expired", "failed", "revoked"

## Response 400:

  - `400` (unknown)
    Invalid request. Common causes: missing or malformed X-Org-Id / X-Environment headers, or a missing required field in the request body.

## Response 400 fields (application/json):

  - `code` (string, required)
    Machine-readable error code.

  - `message` (string, required)
    Human-readable error description.

  - `details` (object)
    Optional structured data with additional context.

## Response 401:

  - `401` (unknown)
    Unauthenticated. The Bearer token failed validation (signature, issuer, audience, or expiry).

## Response 401 fields (application/json):

  - `code` (string, required)
    Machine-readable error code.

  - `message` (string, required)
    Human-readable error description.

  - `details` (object)
    Optional structured data with additional context.

## Response 403:

  - `403` (unknown)
    Permission denied. The token does not carry the scope required by this endpoint. Check the `scope` claim on the token you received from the DeepCloud SSO token endpoint.

## Response 403 fields (application/json):

  - `code` (string, required)
    Machine-readable error code.

  - `message` (string, required)
    Human-readable error description.

  - `details` (object)
    Optional structured data with additional context.

## Response 429:

  - `429` (unknown)
    Rate limit exceeded.

## Response 429 fields (application/json):

  - `code` (string, required)
    Machine-readable error code.

  - `message` (string, required)
    Human-readable error description.

  - `details` (object)
    Optional structured data with additional context.

## Response 500:

  - `500` (unknown)
    Internal server error.

## Response 500 fields (application/json):

  - `code` (string, required)
    Machine-readable error code.

  - `message` (string, required)
    Human-readable error description.

  - `details` (object)
    Optional structured data with additional context.

