{
  "openapi": "3.1.0",
  "info": {
    "title": "Issued Credentials API",
    "description": "Manage issued verifiable credentials.\n\nUse this API to list, inspect, and update the status (suspend, revoke, reactivate) of credentials that have been issued to wallet holders.",
    "version": "1.0.0",
    "x-metadata": {
      "title": "Issued Credentials API",
      "description": "Manage issued verifiable credentials"
    }
  },
  "servers": [
    {
      "url": "https://api.dev.deepcredentials.swiss",
      "description": "Development"
    },
    {
      "url": "https://api.int.deepcredentials.swiss",
      "description": "Integration"
    }
  ],
  "tags": [
    {
      "name": "Issued Credentials",
      "description": "List, inspect, and manage issued credentials."
    }
  ],
  "paths": {
    "/b2b/v1/issued-credentials": {
      "get": {
        "description": "Returns a paginated list of issued credentials for the calling service user's organization (X-Org-Id) and environment (X-Environment).\n",
        "operationId": "GET:b2b.ListIssuedCredentials",
        "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 credential status: active, suspended, revoked.\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": {
                    "credentials": {
                      "items": {
                        "$ref": "#/components/schemas/b2b.b2bIssuedCredentialResponse"
                      },
                      "description": "Page of issued credentials matching the list filters.",
                      "type": "array"
                    },
                    "total": {
                      "format": "int64",
                      "description": "Total count matching filters (for pagination UI).",
                      "type": "integer"
                    }
                  },
                  "required": [
                    "credentials",
                    "total"
                  ],
                  "type": "object"
                },
                "example": {
                  "credentials": [
                    {
                      "id": "f1e2d3c4-b5a6-7890-1234-567890abcdef",
                      "environment": "beta",
                      "offer_id": "d4e5f6a7-b8c9-0123-4567-89abcdef0123",
                      "credential_definition": {
                        "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                        "vct": "urn:ietf:params:oauth:vct:PersonId",
                        "name": "Person ID"
                      },
                      "holder_did": "did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2In0",
                      "status": "active",
                      "reference_id": "order-12345",
                      "created_at": "2026-03-29T12:01:30Z",
                      "updated_at": "2026-03-29T12:01:30Z"
                    },
                    {
                      "id": "a2b3c4d5-e6f7-8901-2345-6789abcdef02",
                      "environment": "beta",
                      "offer_id": "e5f6a7b8-c9d0-1234-5678-9abcdef01234",
                      "credential_definition": {
                        "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                        "vct": "urn:ietf:params:oauth:vct:PersonId",
                        "name": "Person ID"
                      },
                      "holder_did": "did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2Iiwia2lkIjoiMiJ9",
                      "status": "revoked",
                      "status_updated_at": "2026-03-30T09:00:00Z",
                      "created_at": "2026-03-29T14:00:00Z",
                      "updated_at": "2026-03-30T09:00: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 issued credentials.\n",
        "tags": [
          "Issued Credentials"
        ]
      }
    },
    "/b2b/v1/issued-credentials/{credentialId}": {
      "get": {
        "description": "Returns the status and details of an issued credential.\n",
        "operationId": "GET:b2b.GetIssuedCredential",
        "parameters": [
          {
            "$ref": "#/components/parameters/XOrgIdHeader"
          },
          {
            "$ref": "#/components/parameters/XEnvironmentHeader"
          },
          {
            "allowEmptyValue": true,
            "explode": false,
            "in": "path",
            "name": "credentialId",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            },
            "style": "simple"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/b2b.b2bIssuedCredentialResponse"
                },
                "example": {
                  "id": "f1e2d3c4-b5a6-7890-1234-567890abcdef",
                  "environment": "beta",
                  "offer_id": "d4e5f6a7-b8c9-0123-4567-89abcdef0123",
                  "credential_definition": {
                    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
                    "vct": "urn:ietf:params:oauth:vct:PersonId",
                    "name": "Person ID"
                  },
                  "holder_did": "did:jwk:eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2In0",
                  "status": "active",
                  "claims": {
                    "given_name": "Max",
                    "family_name": "Mustermann",
                    "date_of_birth": "1990-01-15",
                    "is_over_18": true
                  },
                  "reference_id": "order-12345",
                  "created_at": "2026-03-29T12:01:30Z",
                  "updated_at": "2026-03-29T12:01:30Z"
                }
              }
            },
            "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 issued credential.\n",
        "tags": [
          "Issued Credentials"
        ]
      }
    },
    "/b2b/v1/issued-credentials/{credentialId}/status": {
      "patch": {
        "operationId": "PATCH:b2b.UpdateIssuedCredentialStatus",
        "parameters": [
          {
            "$ref": "#/components/parameters/XOrgIdHeader"
          },
          {
            "$ref": "#/components/parameters/XEnvironmentHeader"
          },
          {
            "allowEmptyValue": true,
            "explode": false,
            "in": "path",
            "name": "credentialId",
            "required": true,
            "schema": {
              "format": "uuid",
              "type": "string"
            },
            "style": "simple"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "properties": {
                  "status": {
                    "description": "New status for the credential. One of: \"active\", \"suspended\", \"revoked\".\nValid transitions: active ↔ suspended, active/suspended → revoked (terminal).",
                    "type": "string",
                    "enum": [
                      "active",
                      "suspended",
                      "revoked"
                    ]
                  }
                },
                "required": [
                  "status"
                ],
                "type": "object"
              }
            }
          }
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/b2b.b2bIssuedCredentialResponse"
                }
              }
            },
            "description": "Success response"
          },
          "400": {
            "description": "Invalid request. Either the body or headers were malformed (`invalid_argument`), or the requested status change is not allowed (`failed_precondition`): the credential is already in the requested status, the transition is illegal (e.g. leaving the terminal `revoked` state), or the status was changed concurrently. Setting a credential to its current status is not idempotent — it returns this error.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "invalid_argument": {
                    "summary": "Malformed request",
                    "value": {
                      "code": "invalid_argument",
                      "message": "X-Org-Id header is required."
                    }
                  },
                  "failed_precondition": {
                    "summary": "Illegal or no-op transition",
                    "value": {
                      "code": "failed_precondition",
                      "message": "invalid status transition from revoked to active"
                    }
                  }
                }
              }
            }
          },
          "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": "Update issued credential status (revoke, suspend, reactivate).\n",
        "tags": [
          "Issued Credentials"
        ]
      }
    }
  },
  "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."
          }
        }
      },
      "issuer.CredentialStatus": {
        "type": "string",
        "enum": [
          "active",
          "suspended",
          "revoked"
        ],
        "description": "Credential lifecycle status. Valid transitions: active ↔ suspended, active/suspended → revoked (terminal)."
      },
      "b2b.b2bIssuedCredentialResponse": {
        "properties": {
          "claims": {
            "description": "Credential claim values.",
            "type": "object"
          },
          "created_at": {
            "format": "date-time",
            "description": "When the credential record was created (RFC3339).",
            "type": "string"
          },
          "credential_definition": {
            "allOf": [
              {
                "$ref": "#/components/schemas/b2b.credentialDefinitionRef"
              }
            ],
            "description": "Credential definition reference."
          },
          "environment": {
            "description": "Swiyu environment: \"beta\" or \"production\".",
            "type": "string"
          },
          "holder_did": {
            "description": "DID of the wallet holder, if known.",
            "type": "string"
          },
          "id": {
            "type": "string",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440000",
            "description": "Issued credential id. UUID v4 identifier."
          },
          "offer_id": {
            "type": "string",
            "format": "uuid",
            "example": "550e8400-e29b-41d4-a716-446655440000",
            "description": "ID of the offer that produced this credential. UUID v4 identifier."
          },
          "reference_id": {
            "description": "Your reference_id from the originating offer, if any.",
            "type": "string"
          },
          "status": {
            "allOf": [
              {
                "$ref": "#/components/schemas/issuer.CredentialStatus"
              }
            ],
            "description": "Credential lifecycle status: \"active\", \"suspended\", or \"revoked\"."
          },
          "status_updated_at": {
            "format": "date-time",
            "description": "When the status was last changed (RFC3339).",
            "type": "string"
          },
          "updated_at": {
            "format": "date-time",
            "description": "When the credential record was last updated (RFC3339).",
            "type": "string"
          },
          "valid_from": {
            "format": "date-time",
            "description": "SD-JWT nbf — when the credential becomes valid (mirror of iat).",
            "type": "string"
          },
          "valid_until": {
            "format": "date-time",
            "description": "SD-JWT exp — when the credential stops being valid. Absent when the credential was issued without an exp claim (never expires).",
            "type": "string"
          }
        },
        "required": [
          "id",
          "environment",
          "offer_id",
          "credential_definition",
          "status",
          "valid_from",
          "created_at",
          "updated_at"
        ],
        "description": "The full detail of an issued credential.",
        "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"
      }
    },
    "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": []
    }
  ]
}