{
  "openapi": "3.1.0",
  "info": {
    "title": "Verification API",
    "description": "Create and manage OID4VP identity verification sessions.\n\nUse this API to start digital identity verification flows. Each session generates a QR code and deeplink that the end user scans with their wallet app to present verifiable credentials.",
    "version": "1.0.0",
    "x-metadata": {
      "title": "Verification API",
      "description": "Create and manage OID4VP identity verification sessions"
    }
  },
  "servers": [
    {
      "url": "https://api.dev.deepcredentials.swiss",
      "description": "Development"
    },
    {
      "url": "https://api.int.deepcredentials.swiss",
      "description": "Integration"
    }
  ],
  "tags": [
    {
      "name": "Verification Sessions",
      "description": "Create and poll verification sessions."
    }
  ],
  "paths": {
    "/b2b/v1/verification-sessions": {
      "get": {
        "description": "Returns a paginated list of verification sessions for the calling service user's organization (X-Org-Id) and environment (X-Environment).\n",
        "operationId": "GET:b2b.ListVerificationSessions",
        "parameters": [
          {
            "$ref": "#/components/parameters/XOrgIdHeader"
          },
          {
            "$ref": "#/components/parameters/XEnvironmentHeader"
          },
          {
            "allowEmptyValue": true,
            "description": "Maximum number of results to return (1-100, default 20).\n",
            "explode": true,
            "in": "query",
            "name": "limit",
            "schema": {
              "format": "int64",
              "type": "integer"
            },
            "style": "form"
          },
          {
            "allowEmptyValue": true,
            "description": "Number of results to skip for pagination.\n",
            "explode": true,
            "in": "query",
            "name": "offset",
            "schema": {
              "format": "int64",
              "type": "integer"
            },
            "style": "form"
          },
          {
            "allowEmptyValue": true,
            "description": "Filter by session status: pending, success, failed, expired.\n",
            "explode": true,
            "in": "query",
            "name": "status",
            "schema": {
              "type": "string"
            },
            "style": "form"
          },
          {
            "allowEmptyValue": true,
            "description": "Filter by credential definition ID (UUID).\n",
            "explode": true,
            "in": "query",
            "name": "credential_definition_id",
            "schema": {
              "type": "string"
            },
            "style": "form"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "counts": {
                      "$ref": "#/components/schemas/verification.SessionStatusCounts"
                    },
                    "sessions": {
                      "items": {
                        "$ref": "#/components/schemas/b2b.b2bSessionResponse"
                      },
                      "description": "Page of sessions matching the list filters.",
                      "type": "array"
                    },
                    "total": {
                      "format": "int64",
                      "description": "Total count matching filters (for pagination UI).",
                      "type": "integer"
                    }
                  },
                  "required": [
                    "sessions",
                    "total",
                    "counts"
                  ],
                  "type": "object"
                },
                "example": {
                  "sessions": [
                    {
                      "session_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
                      "status": "success",
                      "environment": "beta",
                      "credential_definition": {
                        "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                        "vct": "urn:ietf:params:oauth:vct:PersonId",
                        "name": "Person ID"
                      },
                      "requested_fields": [
                        "given_name",
                        "family_name",
                        "is_over_18"
                      ],
                      "credential_subject_data": {
                        "given_name": "Max",
                        "family_name": "Mustermann",
                        "is_over_18": true
                      },
                      "credential_subject_types": {},
                      "reference_id": "order-12345",
                      "expires_at": "2026-03-29T12:30:00Z",
                      "created_at": "2026-03-29T11:55:00Z",
                      "completed_at": "2026-03-29T12:00:00Z"
                    },
                    {
                      "session_id": "b2c3d4e5-f6a7-8901-2345-6789abcdef01",
                      "status": "pending",
                      "environment": "beta",
                      "credential_definition": {
                        "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                        "vct": "urn:ietf:params:oauth:vct:PersonId",
                        "name": "Person ID"
                      },
                      "requested_fields": [
                        "given_name",
                        "family_name"
                      ],
                      "credential_subject_types": {
                        "given_name": "string",
                        "family_name": "string"
                      },
                      "qr_content_base64": "iVBORw0KGgo...",
                      "deeplink": "swiyu-verify://?client_id=did%3Atest&request_uri=...",
                      "expires_at": "2026-03-29T12:45:00Z",
                      "created_at": "2026-03-29T12:35:00Z"
                    }
                  ],
                  "total": 2
                }
              }
            },
            "description": "Success response"
          },
          "400": {
            "description": "Invalid request. Common causes: missing or malformed X-Org-Id / X-Environment headers, or a missing required field in the request body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "invalid_argument",
                  "message": "X-Org-Id header is required."
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated. The Bearer token failed validation (signature, issuer, audience, or expiry).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "unauthenticated",
                  "message": "Missing or invalid Bearer token."
                }
              }
            }
          },
          "403": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "permission_denied",
                  "message": "Token lacks the required scope."
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "resource_exhausted",
                  "message": "Rate limit exceeded. Retry with backoff."
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected error occurred."
                }
              }
            }
          }
        },
        "summary": "List verification sessions.\n",
        "tags": [
          "Verification Sessions"
        ]
      },
      "post": {
        "description": "Returns a QR code (base64 PNG) and deeplink that the end user scans with their wallet app. Poll GET /b2b/v1/verification-sessions/:sessionId or subscribe to webhook \"verification.completed\".\n",
        "operationId": "POST:b2b.Verify",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "allowed_issuer_dids": {
                    "items": {
                      "type": "string"
                    },
                    "description": "Optional allow-list of issuer DIDs. Ignored for official credential definitions\nwith locked issuers (server uses credential definition policy).",
                    "type": "array"
                  },
                  "case_id": {
                    "format": "uuid",
                    "description": "Saved verification case to launch (multi-credential / OR). Preferred over\ncredential_definition_id/claims.",
                    "type": "string"
                  },
                  "claims": {
                    "items": {
                      "type": "string"
                    },
                    "description": "DEPRECATED: use case_id. Claim names to request in the presentation (must exist\non the credential definition).",
                    "type": "array"
                  },
                  "credential_definition_id": {
                    "format": "uuid",
                    "description": "DEPRECATED: use case_id. Credential definition to verify against (UUID from the\ntenant's credential definition catalog).",
                    "type": "string"
                  },
                  "purpose": {
                    "description": "Optional text shown in wallet/consent context where supported.",
                    "type": "string"
                  },
                  "reference_id": {
                    "description": "Optional correlation identifier from your system; echoed on the resource.",
                    "type": "string"
                  }
                },
                "required": [
                  "credential_definition_id",
                  "claims"
                ],
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "properties": {
                    "deeplink": {
                      "description": "Wallet app link, e.g. openid4vp://?client_id=...&request_uri=...",
                      "type": "string"
                    },
                    "expires_at": {
                      "format": "date-time",
                      "description": "Session TTL deadline (RFC3339).",
                      "type": "string"
                    },
                    "qr_content_base64": {
                      "description": "Base64-encoded PNG; QR payload is the same URI as deeplink.",
                      "type": "string"
                    },
                    "session_id": {
                      "type": "string",
                      "format": "uuid",
                      "description": "UUID v4 identifier.",
                      "example": "550e8400-e29b-41d4-a716-446655440000"
                    },
                    "status": {
                      "$ref": "#/components/schemas/verification.SessionStatus"
                    }
                  },
                  "required": [
                    "session_id",
                    "qr_content_base64",
                    "deeplink",
                    "status",
                    "expires_at"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "Success response"
          },
          "400": {
            "description": "Invalid request. Common causes: missing or malformed X-Org-Id / X-Environment headers, or a missing required field in the request body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "invalid_argument",
                  "message": "X-Org-Id header is required."
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated. The Bearer token failed validation (signature, issuer, audience, or expiry).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "unauthenticated",
                  "message": "Missing or invalid Bearer token."
                }
              }
            }
          },
          "403": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "permission_denied",
                  "message": "Token lacks the required scope."
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "resource_exhausted",
                  "message": "Rate limit exceeded. Retry with backoff."
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected error occurred."
                }
              }
            }
          }
        },
        "summary": "Create a verification session.\n",
        "tags": [
          "Verification Sessions"
        ],
        "parameters": [
          {
            "$ref": "#/components/parameters/XOrgIdHeader"
          },
          {
            "$ref": "#/components/parameters/XEnvironmentHeader"
          }
        ]
      }
    },
    "/b2b/v1/verification-sessions/{sessionId}": {
      "get": {
        "description": "Returns the current status and outcome of a session started via POST /b2b/v1/verification-sessions.\n",
        "operationId": "GET:b2b.GetVerificationSession",
        "parameters": [
          {
            "$ref": "#/components/parameters/XOrgIdHeader"
          },
          {
            "$ref": "#/components/parameters/XEnvironmentHeader"
          },
          {
            "allowEmptyValue": true,
            "explode": false,
            "in": "path",
            "name": "sessionId",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            },
            "style": "simple"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/b2b.b2bSessionResponse"
                },
                "example": {
                  "session_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
                  "status": "success",
                  "environment": "beta",
                  "credential_definition": {
                    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                    "vct": "urn:ietf:params:oauth:vct:PersonId",
                    "name": "Person ID"
                  },
                  "requested_fields": [
                    "given_name",
                    "family_name",
                    "is_over_18"
                  ],
                  "credential_subject_data": {
                    "given_name": "Max",
                    "family_name": "Mustermann",
                    "is_over_18": true
                  },
                  "credential_subject_types": {
                    "given_name": "string",
                    "family_name": "string",
                    "is_over_18": "boolean"
                  },
                  "reference_id": "order-12345",
                  "expires_at": "2026-03-29T12:30:00Z",
                  "created_at": "2026-03-29T11:55:00Z",
                  "completed_at": "2026-03-29T12:00:00Z"
                }
              }
            },
            "description": "Success response"
          },
          "400": {
            "description": "Invalid request. Common causes: missing or malformed X-Org-Id / X-Environment headers, or a missing required field in the request body.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "invalid_argument",
                  "message": "X-Org-Id header is required."
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated. The Bearer token failed validation (signature, issuer, audience, or expiry).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "unauthenticated",
                  "message": "Missing or invalid Bearer token."
                }
              }
            }
          },
          "403": {
            "description": "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.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "permission_denied",
                  "message": "Token lacks the required scope."
                }
              }
            }
          },
          "404": {
            "description": "Resource not found. Either the resource ID is unknown, or the X-Org-Id you supplied is not an organization the authenticated service user is a member of.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "not_found",
                  "message": "Resource not found."
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "resource_exhausted",
                  "message": "Rate limit exceeded. Retry with backoff."
                }
              }
            }
          },
          "500": {
            "description": "Internal server error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "code": "internal",
                  "message": "An unexpected error occurred."
                }
              }
            }
          }
        },
        "summary": "Get verification session.\n",
        "tags": [
          "Verification Sessions"
        ]
      }
    }
  },
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "DeepAdmin-issued Keycloak access token obtained via the OAuth 2.0\nResource Owner Password Credentials grant against the DeepCloud SSO\nrealm. See the Service Users guide for the exchange flow."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "code",
          "message"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "Machine-readable error code."
          },
          "message": {
            "type": "string",
            "description": "Human-readable error description."
          },
          "details": {
            "type": "object",
            "description": "Optional structured data with additional context."
          }
        }
      },
      "verification.SessionStatus": {
        "type": "string",
        "enum": [
          "pending",
          "success",
          "failed",
          "expired"
        ],
        "description": "Lifecycle state of a verification session."
      },
      "b2b.b2bSessionResponse": {
        "properties": {
          "accepted_issuer_dids": {
            "items": {
              "type": "string"
            },
            "description": "Effective issuer allow-list for this session (may come from credential\ndefinition locks).",
            "type": "array"
          },
          "completed_at": {
            "format": "date-time",
            "description": "Set when the session leaves pending (success, failed, or expired).",
            "type": "string"
          },
          "created_at": {
            "format": "date-time",
            "description": "When the session was created (RFC3339).",
            "type": "string"
          },
          "credential_definition": {
            "allOf": [
              {
                "$ref": "#/components/schemas/b2b.credentialDefinitionRef"
              }
            ],
            "description": "Credential definition reference."
          },
          "credential_status": {
            "description": "On success: the token-status-list state of the credential the holder presented — \"valid\", \"suspended\" (shown as \"Locked\" in the portal), or \"revoked\". Resolved when the holder presented and never recomputed.\n\nA \"suspended\" or \"revoked\" value still accompanies a successful verification with its disclosed claims: the wallet can present such a credential and the holder can consent to sharing it, so the verifier is given the state and applies its own policy. Branch on this in addition to \\`status\\` if a locked credential should not be accepted.",
            "type": "string"
          },
          "credential_subject_data": {
            "description": "On success: disclosed subject claims (structure depends on credential definition\nand wallet).",
            "type": "object"
          },
          "credential_subject_types": {
            "additionalProperties": {
              "type": "string"
            },
            "description": "For sessions created through the deprecated `credential_definition_id` plus `claims`\nrequest shape, this maps requested claim names to the types configured on the credential\ndefinition. It is currently an empty object for sessions created with `case_id`.\nPartners using case-based verification must not rely on this field to infer disclosed-value types.",
            "type": "object"
          },
          "deeplink": {
            "description": "Only when status is pending: wallet deep link (e.g. openid4vp://...).",
            "type": "string"
          },
          "environment": {
            "description": "Swiyu environment: \"beta\" or \"production\" (matches the credential binding).",
            "type": "string"
          },
          "error_code": {
            "description": "Machine-readable failure reason when status is failed or expired.",
            "type": "string"
          },
          "error_description": {
            "description": "Human-readable error detail when status is failed or expired.",
            "type": "string"
          },
          "expires_at": {
            "format": "date-time",
            "description": "When a pending session expires if not completed (RFC3339).",
            "type": "string"
          },
          "matched_vct": {
            "description": "On success: the vct of the credential the holder actually presented, relevant when the session accepts more than one credential type.",
            "type": "string"
          },
          "purpose": {
            "description": "Purpose string from the create request, if any.",
            "type": "string"
          },
          "qr_content_base64": {
            "description": "Only when status is pending: Base64 PNG QR code for the wallet.",
            "type": "string"
          },
          "reference_id": {
            "description": "Your reference_id from the create request, if any.",
            "type": "string"
          },
          "requested_fields": {
            "items": {
              "type": "string"
            },
            "description": "Claim keys requested in the presentation definition.",
            "type": "array"
          },
          "session_id": {
            "type": "string",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440000",
            "description": "Session id (matches the value returned at creation time). UUID v4 identifier."
          },
          "status": {
            "allOf": [
              {
                "$ref": "#/components/schemas/verification.SessionStatus"
              }
            ],
            "description": "Current lifecycle state of the session."
          }
        },
        "required": [
          "session_id",
          "status",
          "expires_at",
          "environment",
          "credential_definition",
          "requested_fields",
          "credential_subject_types",
          "created_at"
        ],
        "description": "The current status and outcome of a verification session.",
        "type": "object"
      },
      "b2b.credentialDefinitionRef": {
        "properties": {
          "has_logo": {
            "description": "Whether the credential definition currently has an uploaded logo.",
            "type": "boolean"
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440000",
            "description": "Credential definition UUID. UUID v4 identifier."
          },
          "name": {
            "description": "Human-readable credential definition name.",
            "type": "string"
          },
          "vct": {
            "description": "Verifiable credential type (e.g. \"urn:ietf:params:oauth:vct:PersonId\").",
            "type": "string"
          }
        },
        "required": [
          "id",
          "vct",
          "name",
          "has_logo"
        ],
        "description": "A compact reference to the credential definition.",
        "type": "object"
      },
      "verification.SessionStatusCounts": {
        "properties": {
          "all": {
            "format": "int64",
            "type": "integer"
          },
          "expired": {
            "format": "int64",
            "type": "integer"
          },
          "failed": {
            "format": "int64",
            "type": "integer"
          },
          "pending": {
            "format": "int64",
            "type": "integer"
          },
          "success": {
            "format": "int64",
            "type": "integer"
          }
        },
        "required": [
          "all",
          "success",
          "pending",
          "failed",
          "expired"
        ],
        "description": "SessionStatusCounts mirrors the filter tabs on the verification page — one tenant-wide count per status, plus a grand \\`All\\` total.",
        "type": "object"
      }
    },
    "parameters": {
      "XOrgIdHeader": {
        "name": "X-Org-Id",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "description": "Organization the call is acting on. The authenticated service user must be a member of this organization in DeepAdmin; otherwise the request returns 404."
      },
      "XEnvironmentHeader": {
        "name": "X-Environment",
        "in": "header",
        "required": true,
        "schema": {
          "type": "string",
          "enum": [
            "beta",
            "production"
          ]
        },
        "description": "Environment the call is scoped to. Resources are isolated per environment."
      }
    }
  },
  "security": [
    {
      "BearerAuth": []
    }
  ]
}