{
  "openapi": "3.1.1",
  "info": {
    "title": "Certifyd API",
    "description": "Certifyd is a platform for issuing certificates and digital badges that anyone can verify. This REST API lets\nyour own systems do what you can do in the app: create events, add attendees, issue certificates one at a time\nor in bulk, revoke them, and let anyone check that a certificate is genuine. It speaks JSON over HTTPS, and API\naccess is included on every plan, including the free Starter plan.\n\nThe base address is `https://certifyd.cloud/api/v1`. A short overview with a worked example, in English and\nSpanish, is at [certifyd.cloud/developers/overview](https://certifyd.cloud/developers/overview).\n\n## Authentication\n\nThere are two ways to call the API, and both send the same header, `Authorization: Bearer <token>`:\n\n- **API key (recommended for integrations).** Create a key in the app under *Settings, then API keys*. Pick\n  only the scopes it needs and, if you like, an expiry in days. The full key starts with `crtfd_live_` and is\n  shown **once**, so store it safely. A key can only do what its scopes allow: a call outside them answers\n  `403` with the code `INSUFFICIENT_SCOPE`. Each operation below lists the scope it needs.\n- **Signed-in session (JWT).** `POST /api/v1/auth/login` with an email and password returns an\n  `accessToken` (short-lived) and a `refreshToken`. Send the access token as the bearer token, and call\n  `POST /api/v1/auth/refresh` with the refresh token to get a new pair. A signed-in person can do everything\n  their account allows, so use a key, not a password, for unattended systems.\n\nAPI keys cannot create or revoke other API keys, and they cannot read or change branding, credits or your\nprofile: those operations need a signed-in session, so a leaked key can never mint a replacement for itself.\nPublic operations (certificate verification) need no credentials at all.\n\nTo try a call from this page, open **Authentication**, choose the *API key* or *Signed-in session* scheme and\npaste your token. Use a key of your own account, and prefer a test event: the calls run against the real\nservice.\n\n## Responses\n\nEvery response is JSON. A successful call wraps its payload in an envelope:\n\n```json\n{ \"data\": { \"id\": \"...\" }, \"meta\": { \"page\": 1, \"pageSize\": 50, \"total\": 120 } }\n```\n\n`meta` is present only on lists that paginate. An error has the same envelope in a different shape, with a\nstable `code` to program against and a `message` for people:\n\n```json\n{ \"error\": { \"code\": \"CREDIT_LIMIT\", \"message\": \"No certificate credits left this month.\" } }\n```\n\n| HTTP status | `error.code` (examples) | Meaning |\n|---|---|---|\n| 400 | `VALIDATION_ERROR` | The request is not valid. `details` lists what to fix. |\n| 401 | `INVALID_API_KEY` | The key or token is missing, wrong, expired or revoked. |\n| 402 | `CREDIT_LIMIT` | No certificate credits are left for this account. |\n| 403 | `INSUFFICIENT_SCOPE` | The key lacks the scope the operation needs (`required` names it). |\n| 404 | `NOT_FOUND` | No such resource in your account. |\n| 409 | `ALREADY_ISSUED`, `IDEMPOTENCY_IN_PROGRESS` | See *Retries never issue twice* below. |\n| 422 | `IDEMPOTENCY_KEY_REUSED` | The same `Idempotency-Key` was sent with a different body. |\n| 429 | `RATE_LIMITED` | Too many requests. Wait for the `Retry-After` header, in seconds. |\n\n## Retries never issue twice\n\nAn attendee holds at most one valid certificate per event and template. Asking again answers `409`\n`ALREADY_ISSUED` with the existing certificate in `error.existing`, and nothing is issued or charged.\n`POST /certificates/generate` and `POST /badges/{id}/issue` also accept an optional `Idempotency-Key` header\n(1 to 255 printable characters, ideally a UUID you generate per request). The same key with the same body within\n24 hours answers `200` with the original result and the header `Idempotent-Replayed: true`, so a network\nretry is always safe.\n\n## Rate limits\n\nLimits are applied per client address and answer `429` with a `Retry-After` header. A global limit of 100\nrequests a minute covers the API. Sign-in is stricter (30 attempts per 5 minutes), public verification lookups\nallow 30 a minute, and certificate downloads use a token bucket of 5 a minute. Spread bulk work over time, or\nuse the bulk generation job, which issues certificates for a whole event in the background.\n\n## Webhooks\n\nInstead of polling, subscribe an HTTPS endpoint with `POST /webhooks` and Certifyd tells you when a certificate\nis issued, emailed, opened, downloaded or revoked. Each delivery is signed. The events, their payloads and the\nsignature check are described in the **Webhooks** section of this reference.",
    "contact": {
      "name": "Certifyd support",
      "url": "https://certifyd.cloud/faq"
    },
    "version": "v1",
    "summary": "Issue, verify and manage digital certificates and badges from your own systems."
  },
  "servers": [
    {
      "url": "https://certifyd.cloud",
      "description": "Production"
    }
  ],
  "paths": {
    "/api/v1/analytics/dashboard": {
      "get": {
        "tags": [
          "Analytics"
        ],
        "summary": "Get the dashboard figures",
        "description": "Totals for the account: credentials issued, views, shares, downloads and the verification rate.\n\n**Needs an API key with the `analytics:read` scope, or a signed-in session.**",
        "operationId": "analytics_getDashboard",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfDashboardStats"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "analytics:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/analytics/events/{eventId}": {
      "get": {
        "tags": [
          "Analytics"
        ],
        "summary": "Get the figures of an event",
        "description": "**Needs an API key with the `analytics:read` scope, or a signed-in session.**",
        "operationId": "analytics_getEventAnalytics",
        "parameters": [
          {
            "name": "eventId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfListOfEventAnalytics"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "analytics:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/api-keys": {
      "get": {
        "tags": [
          "API keys"
        ],
        "summary": "List API keys",
        "description": "Your active keys. The key itself is never shown again after it was created.\n\n**Needs a signed-in session.** API keys cannot call this operation.",
        "operationId": "apiKeys_getAll",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfListOfApiKeySummary"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "This credential is not allowed to do this (`INSUFFICIENT_SCOPE` or `API_KEY_NOT_ALLOWED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "BearerJwt": [ ]
          }
        ]
      },
      "post": {
        "tags": [
          "API keys"
        ],
        "summary": "Create an API key",
        "description": "Creates a key with the scopes you choose. The full `key` is returned **once**. For safety a key is only created for a session that signed in within the last few minutes, or when the request carries `currentPassword` (or a `twoFactorCode`) again; the account owner is emailed about every new key.\n\n**Needs a signed-in session.** API keys cannot call this operation.",
        "operationId": "apiKeys_create",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateApiKeyRequest"
              },
              "example": {
                "name": "Billing integration",
                "scopes": [
                  "events:read",
                  "certificates:generate"
                ],
                "expiresInDays": 365,
                "currentPassword": "your-password"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfCreatedApiKey"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "This credential is not allowed to do this (`INSUFFICIENT_SCOPE` or `API_KEY_NOT_ALLOWED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/api-keys/{id}": {
      "delete": {
        "tags": [
          "API keys"
        ],
        "summary": "Revoke an API key",
        "description": "Revokes the key at once: calls with it start answering `401`.\n\n**Needs a signed-in session.** API keys cannot call this operation.",
        "operationId": "apiKeys_delete",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "This credential is not allowed to do this (`INSUFFICIENT_SCOPE` or `API_KEY_NOT_ALLOWED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/events/{eventId}/attendees/export": {
      "get": {
        "tags": [
          "Attendees"
        ],
        "summary": "Export the attendees of an event as CSV",
        "description": "**Needs an API key with the `attendees:read` scope, or a signed-in session.**",
        "operationId": "attendeeExport_export",
        "parameters": [
          {
            "name": "eventId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "200": {
            "description": "The file.",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "attendees:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/attendees": {
      "get": {
        "tags": [
          "Attendees"
        ],
        "summary": "List attendees",
        "description": "Attendees of all your events. Filter with `eventId` in the query.\n\n**Needs an API key with the `attendees:read` scope, or a signed-in session.**",
        "operationId": "attendees_getAll",
        "parameters": [
          {
            "name": "eventId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfIEnumerableOfAttendeeDto"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "attendees:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      },
      "post": {
        "tags": [
          "Attendees"
        ],
        "summary": "Add an attendee",
        "description": "**Needs an API key with the `attendees:write` scope, or a signed-in session.**",
        "operationId": "attendees_create",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateAttendeeDto"
              },
              "example": {
                "eventId": "a1b2c3d4-0000-4000-8000-000000000001",
                "firstName": "Ana",
                "lastName": "Rivera",
                "email": "ana@example.com"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfAttendeeDto"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "attendees:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/attendees/count": {
      "get": {
        "tags": [
          "Attendees"
        ],
        "summary": "Count attendees",
        "description": "**Needs an API key with the `attendees:read` scope, or a signed-in session.**",
        "operationId": "attendees_getCount",
        "parameters": [
          {
            "name": "eventId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfint"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "attendees:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/attendees/{id}": {
      "get": {
        "tags": [
          "Attendees"
        ],
        "summary": "Get an attendee",
        "description": "**Needs an API key with the `attendees:read` scope, or a signed-in session.**",
        "operationId": "attendees_getById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfAttendeeDto"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "attendees:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      },
      "put": {
        "tags": [
          "Attendees"
        ],
        "summary": "Update an attendee",
        "description": "**Needs an API key with the `attendees:write` scope, or a signed-in session.**",
        "operationId": "attendees_update",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateAttendeeDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfAttendeeDto"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "attendees:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Attendees"
        ],
        "summary": "Delete an attendee",
        "description": "**Needs an API key with the `attendees:write` scope, or a signed-in session.**",
        "operationId": "attendees_delete",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "attendees:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/attendees/import": {
      "post": {
        "tags": [
          "Attendees"
        ],
        "summary": "Import attendees from a CSV file",
        "description": "Uploads a CSV file (form field `file`) and adds its rows as attendees of the event in the query. With `skipDuplicates=true` (the default) an email that is already on the event is skipped. The answer counts the rows added, skipped and rejected, with the reason for each rejected row.\n\n**Needs an API key with the `attendees:write` scope, or a signed-in session.**",
        "operationId": "attendees_import",
        "parameters": [
          {
            "name": "eventId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "skipDuplicates",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": true
            }
          }
        ],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "$ref": "#/components/schemas/IFormFile"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfCsvImportResult"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "attendees:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/auth/login": {
      "post": {
        "tags": [
          "Authentication"
        ],
        "summary": "Sign in and get tokens",
        "description": "Exchanges an email and password for an `accessToken` and a `refreshToken`. Send the access token as `Authorization: Bearer ...` on later calls. Sign-in is limited per client (30 attempts per 5 minutes), and an account with two-step sign-in answers `401` `TWO_FACTOR_REQUIRED` until you also send `twoFactorCode` (or `recoveryCode`). Integrations that run unattended should use an API key instead.\n\n**No credentials needed.** This operation is public.",
        "operationId": "auth_login",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LoginRequest"
              },
              "example": {
                "email": "you@example.com",
                "password": "your-password"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfTokenResponse"
                },
                "example": {
                  "data": {
                    "accessToken": "eyJhbGciOi...",
                    "refreshToken": "q8Zk...",
                    "expiresAt": "2026-10-05T15:30:00Z"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [ ]
      }
    },
    "/api/v1/auth/refresh": {
      "post": {
        "tags": [
          "Authentication"
        ],
        "summary": "Refresh a session",
        "description": "Trades a refresh token for a new access token and a new refresh token. The old refresh token stops working.\n\n**No credentials needed.** This operation is public.",
        "operationId": "auth_refresh",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RefreshRequest"
              },
              "example": {
                "refreshToken": "q8Zk..."
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfTokenResponse"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [ ]
      }
    },
    "/api/v1/auth/me": {
      "get": {
        "tags": [
          "Authentication"
        ],
        "summary": "Get the signed-in profile",
        "description": "Who the current session belongs to, and which account it works in.\n\n**Needs a signed-in session.** API keys cannot call this operation.",
        "operationId": "auth_getMe",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfUserProfileResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "This credential is not allowed to do this (`INSUFFICIENT_SCOPE` or `API_KEY_NOT_ALLOWED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/badges": {
      "get": {
        "tags": [
          "Badges"
        ],
        "summary": "List badges",
        "description": "**Needs an API key with the `badges:read` scope, or a signed-in session.**",
        "operationId": "badges_getAll",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfListOfBadgeDto"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "badges:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      },
      "post": {
        "tags": [
          "Badges"
        ],
        "summary": "Create a badge",
        "description": "**Needs an API key with the `badges:write` scope, or a signed-in session.**",
        "operationId": "badges_create",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateBadgeDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfBadgeDto"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "badges:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/badges/{id}": {
      "get": {
        "tags": [
          "Badges"
        ],
        "summary": "Get a badge",
        "description": "**Needs an API key with the `badges:read` scope, or a signed-in session.**",
        "operationId": "badges_getById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfBadgeDto"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "badges:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      },
      "put": {
        "tags": [
          "Badges"
        ],
        "summary": "Update a badge",
        "description": "**Needs an API key with the `badges:write` scope, or a signed-in session.**",
        "operationId": "badges_update",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateBadgeDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfBadgeDto"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "badges:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Badges"
        ],
        "summary": "Delete a badge",
        "description": "Deletes a badge. A badge that was ever issued is retired instead: it leaves the list and its assertions become inactive, and no issued credential is erased.\n\n**Needs an API key with the `badges:write` scope, or a signed-in session.**",
        "operationId": "badges_delete",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "badges:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/badges/{id}/issue": {
      "post": {
        "tags": [
          "Badges"
        ],
        "summary": "Issue a badge to attendees",
        "description": "Issues the badge to the attendees you list, one credit each. An attendee who already holds the badge comes back with `alreadyIssued: true` and is not charged again, so repeating a request never double-issues. The optional `Idempotency-Key` header works as on certificates. Answers `409` for a deleted badge and `402` `CREDIT_LIMIT` when there are not enough credits.\n\n**Needs an API key with the `badges:write` scope, or a signed-in session.**",
        "operationId": "badges_issueBadge",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IssueBadgeDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfListOfBadgeAssertionDto"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "402": {
            "description": "No credits are left for this account (`CREDIT_LIMIT`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Conflict (`ALREADY_ISSUED`, `IDEMPOTENCY_IN_PROGRESS` or another `CONFLICT`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "The `Idempotency-Key` was already used with a different request (`IDEMPOTENCY_KEY_REUSED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "badges:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/badges/{id}/assertions": {
      "get": {
        "tags": [
          "Badges"
        ],
        "summary": "List the assertions of a badge",
        "description": "Every issuance of the badge: who holds it and whether it is still active.\n\n**Needs an API key with the `badges:read` scope, or a signed-in session.**",
        "operationId": "badges_getAssertions",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfListOfBadgeAssertionDto"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "badges:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/branding": {
      "get": {
        "tags": [
          "Branding"
        ],
        "summary": "Get branding",
        "description": "**Needs a signed-in session.** API keys cannot call this operation.",
        "operationId": "branding_get",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfBrandingSettingsResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "This credential is not allowed to do this (`INSUFFICIENT_SCOPE` or `API_KEY_NOT_ALLOWED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "BearerJwt": [ ]
          }
        ]
      },
      "put": {
        "tags": [
          "Branding"
        ],
        "summary": "Update branding",
        "description": "**Needs a signed-in session.** API keys cannot call this operation.",
        "operationId": "branding_update",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateBrandingRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfBrandingSettingsResponse"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "This credential is not allowed to do this (`INSUFFICIENT_SCOPE` or `API_KEY_NOT_ALLOWED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/branding/email": {
      "get": {
        "tags": [
          "Branding"
        ],
        "summary": "Get the certificate email settings",
        "description": "**Needs a signed-in session.** API keys cannot call this operation.",
        "operationId": "branding_getEmailSettings",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfCertificateEmailSettings"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "This credential is not allowed to do this (`INSUFFICIENT_SCOPE` or `API_KEY_NOT_ALLOWED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "BearerJwt": [ ]
          }
        ]
      },
      "put": {
        "tags": [
          "Branding"
        ],
        "summary": "Update the certificate email settings",
        "description": "**Needs a signed-in session.** API keys cannot call this operation.",
        "operationId": "branding_updateEmailSettings",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CertificateEmailSettings"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfCertificateEmailSettings"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "This credential is not allowed to do this (`INSUFFICIENT_SCOPE` or `API_KEY_NOT_ALLOWED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/branding/email/preview": {
      "post": {
        "tags": [
          "Branding"
        ],
        "summary": "Preview the certificate email",
        "description": "Renders the real certificate email for the settings you send, without sending it.\n\n**Needs a signed-in session.** API keys cannot call this operation.",
        "operationId": "branding_previewEmail",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EmailPreviewRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfEmailPreviewResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "This credential is not allowed to do this (`INSUFFICIENT_SCOPE` or `API_KEY_NOT_ALLOWED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/branding/email/test": {
      "post": {
        "tags": [
          "Branding"
        ],
        "summary": "Send yourself a test email",
        "description": "Sends the preview to your own address.\n\n**Needs a signed-in session.** API keys cannot call this operation.",
        "operationId": "branding_sendTestEmail",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EmailPreviewRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfTestEmailResult"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "This credential is not allowed to do this (`INSUFFICIENT_SCOPE` or `API_KEY_NOT_ALLOWED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/branding/logo": {
      "post": {
        "tags": [
          "Branding"
        ],
        "summary": "Upload the logo",
        "description": "Uploads the logo as an image file (form field `file`; PNG or JPEG).\n\n**Needs a signed-in session.** API keys cannot call this operation.",
        "operationId": "branding_uploadLogo",
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "$ref": "#/components/schemas/IFormFile"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfBrandingSettingsResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "This credential is not allowed to do this (`INSUFFICIENT_SCOPE` or `API_KEY_NOT_ALLOWED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/branding/signatures/{slot}": {
      "post": {
        "tags": [
          "Branding"
        ],
        "summary": "Upload a signature image",
        "description": "Uploads the signature image of signer 1 or 2 (`slot`). PNG or JPEG up to 1 MB; a transparent PNG looks best.\n\n**Needs a signed-in session.** API keys cannot call this operation.",
        "operationId": "branding_uploadSignature",
        "parameters": [
          {
            "name": "slot",
            "in": "path",
            "required": true,
            "schema": {
              "maximum": 2,
              "minimum": 1,
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "file": {
                    "$ref": "#/components/schemas/IFormFile"
                  }
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfBrandingSettingsResponse"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "This credential is not allowed to do this (`INSUFFICIENT_SCOPE` or `API_KEY_NOT_ALLOWED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "BearerJwt": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Branding"
        ],
        "summary": "Remove a signature image",
        "description": "Removes the signature image of signer 1 or 2; the certificate falls back to the signer's name in a script font.\n\n**Needs a signed-in session.** API keys cannot call this operation.",
        "operationId": "branding_deleteSignature",
        "parameters": [
          {
            "name": "slot",
            "in": "path",
            "required": true,
            "schema": {
              "maximum": 2,
              "minimum": 1,
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfBrandingSettingsResponse"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "This credential is not allowed to do this (`INSUFFICIENT_SCOPE` or `API_KEY_NOT_ALLOWED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/certificates/generate": {
      "post": {
        "tags": [
          "Certificates"
        ],
        "summary": "Issue a certificate",
        "description": "Issues one certificate (one credit) to an attendee, from a template, for an event.\n\n`sendEmail` is optional: `true` emails the recipient their certificate once it is ready, `false` holds the email back (send it later with `POST /api/v1/certificates/{id}/send`), and when you leave it out the account's \"Send automatically on issue\" setting decides. The automatic email goes out at most once per certificate and costs no credits.\n\n**Retries never issue twice.** An attendee holds at most one valid certificate per event and template: a second request answers `409` `ALREADY_ISSUED` with the existing certificate in `error.existing` and a `Location` header, and nothing is issued, emailed or charged. Add an optional `Idempotency-Key` header (1 to 255 printable characters, ideally a UUID) and a repeat of the same request within 24 hours answers `200` with the original certificate and `Idempotent-Replayed: true`. The same key with a different body answers `422` `IDEMPOTENCY_KEY_REUSED`; a repeat while the first is still running answers `409` `IDEMPOTENCY_IN_PROGRESS`.\n\nWhen the account has no credits left the answer is `402` `CREDIT_LIMIT`.\n\n**Needs an API key with the `certificates:generate` scope, or a signed-in session.**",
        "operationId": "certificates_generate",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GenerateCertificateDto"
              },
              "example": {
                "templateId": "5b0f6c2e-1d44-4c55-9e0a-7a3b2c1d0e91",
                "eventId": "a1b2c3d4-0000-4000-8000-000000000001",
                "attendeeId": "a1b2c3d4-0000-4000-8000-000000000002",
                "sendEmail": false
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Issued. `Location` points at the new certificate.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfCertificateDto"
                },
                "example": {
                  "data": {
                    "id": "3f2c9a52-6b0e-4c1d-9a37-0e5d8f7b1c24",
                    "eventId": "a1b2c3d4-0000-4000-8000-000000000001",
                    "attendeeId": "a1b2c3d4-0000-4000-8000-000000000002",
                    "templateId": "5b0f6c2e-1d44-4c55-9e0a-7a3b2c1d0e91",
                    "certificateNumber": "CERT-2026-000123",
                    "verificationCode": "CFD-7X4K-92QM",
                    "issueDate": "2026-10-05T14:30:00Z",
                    "expirationDate": null,
                    "status": "Generated",
                    "attendeeName": "Ana Rivera",
                    "eventName": "Safety Training",
                    "templateName": "Classic achievement"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "402": {
            "description": "No credits are left for this account (`CREDIT_LIMIT`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Conflict (`ALREADY_ISSUED`, `IDEMPOTENCY_IN_PROGRESS` or another `CONFLICT`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "422": {
            "description": "The `Idempotency-Key` was already used with a different request (`IDEMPOTENCY_KEY_REUSED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "certificates:generate"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/certificates/{id}": {
      "get": {
        "tags": [
          "Certificates"
        ],
        "summary": "Get a certificate",
        "description": "**Needs an API key with the `certificates:read` scope, or a signed-in session.**",
        "operationId": "certificates_getById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfCertificateDto"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "certificates:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Certificates"
        ],
        "summary": "Delete a certificate",
        "description": "Deletes a certificate that was never issued. A certificate that has been issued is **revoked** instead, with the reason \"Deleted by issuer\": it stays on record and public verification then reports it as revoked.\n\n**Needs an API key with the `certificates:write` scope, or a signed-in session.**",
        "operationId": "certificates_delete",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "certificates:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/certificates/{id}/regenerate": {
      "post": {
        "tags": [
          "Certificates"
        ],
        "summary": "Regenerate a certificate",
        "description": "Renders the certificate again. Without `templateId` it keeps its current template; with `templateId` in the query it is rendered with that template (which must be one of yours). Sends the `certificate.issued` webhook again.\n\n**Needs an API key with the `certificates:generate` scope, or a signed-in session.**",
        "operationId": "certificates_regenerate",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "templateId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfCertificateDto"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "certificates:generate"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/certificates/{id}/revoke": {
      "post": {
        "tags": [
          "Certificates"
        ],
        "summary": "Revoke a certificate",
        "description": "Marks the certificate as revoked, with a reason. Public verification then reports it as revoked, and the `certificate.revoked` webhook is sent.\n\n**Needs an API key with the `certificates:write` scope, or a signed-in session.**",
        "operationId": "certificates_revoke",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RevokeRequest"
              },
              "example": {
                "reason": "Issued to the wrong person"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfCertificateDto"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "certificates:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/certificates/{id}/download": {
      "get": {
        "tags": [
          "Certificates"
        ],
        "summary": "Download a certificate PDF",
        "description": "Returns the certificate as a PDF file. Add `inline=true` to open it in the browser instead of downloading it.\n\n**Needs an API key with the `certificates:read` scope, or a signed-in session.**",
        "operationId": "certificates_download",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "inline",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "200": {
            "description": "The file.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "certificates:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/certificates/{id}/send": {
      "post": {
        "tags": [
          "Certificates"
        ],
        "summary": "Email a certificate to its recipient",
        "description": "Sends (or sends again) the certificate by email. Answers `502` `EMAIL_NOT_SENT` when the email could not be handed to the mail provider.\n\n**Needs an API key with the `certificates:write` scope, or a signed-in session.**",
        "operationId": "certificates_send",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfCertificateSendResult"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "502": {
            "description": "The email could not be handed to the mail provider (`EMAIL_NOT_SENT`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "certificates:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/credits": {
      "get": {
        "tags": [
          "Credits"
        ],
        "summary": "Get your credit usage",
        "description": "How many certificate credits this month's plan includes, how many are used and how many remain. Issuing returns `402` `CREDIT_LIMIT` when none remain.\n\n**Needs a signed-in session.** API keys cannot call this operation.",
        "operationId": "credits_getUsage",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfCreditUsageDto"
                },
                "example": {
                  "data": {
                    "creditsUsed": 42,
                    "creditsLimit": 100,
                    "creditsRemaining": 58,
                    "tier": "Starter",
                    "periodStart": "2026-10-01T00:00:00Z"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "This credential is not allowed to do this (`INSUFFICIENT_SCOPE` or `API_KEY_NOT_ALLOWED`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/email/send-bulk": {
      "post": {
        "tags": [
          "Email"
        ],
        "summary": "Email all certificates of an event",
        "description": "Emails the certificate to every attendee of the event. Recipients who were already emailed are skipped unless you add `includeAlreadyEmailed=true`.\n\n**Needs an API key with the `certificates:write` scope, or a signed-in session.**",
        "operationId": "email_sendBulk",
        "parameters": [
          {
            "name": "eventId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "includeAlreadyEmailed",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfBulkEmailResult"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "certificates:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/email/deliveries": {
      "get": {
        "tags": [
          "Email"
        ],
        "summary": "List email deliveries",
        "description": "The certificate emails that were sent and what happened to them.\n\n**Needs an API key with the `certificates:read` scope, or a signed-in session.**",
        "operationId": "email_getDeliveries",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfListOfEmailDeliveryDto"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "certificates:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/events": {
      "get": {
        "tags": [
          "Events"
        ],
        "summary": "List events",
        "description": "Your events. Without paging parameters every event is returned. Add `page` and `pageSize` to paginate: the answer then carries `meta` with the total.\n\n**Needs an API key with the `events:read` scope, or a signed-in session.**",
        "operationId": "events_getAll",
        "parameters": [
          {
            "name": "Page",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          {
            "name": "PageSize",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            }
          },
          {
            "name": "SortBy",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "SortDir",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "Search",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfIEnumerableOfEventDto"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "events:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      },
      "post": {
        "tags": [
          "Events"
        ],
        "summary": "Create an event",
        "description": "**Needs an API key with the `events:write` scope, or a signed-in session.**",
        "operationId": "events_create",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateEventDto"
              },
              "example": {
                "name": "Safety Training",
                "eventDate": "2026-10-05T09:00:00Z",
                "templateId": "5b0f6c2e-1d44-4c55-9e0a-7a3b2c1d0e91",
                "location": "Madrid"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfEventDto"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "events:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/events/count": {
      "get": {
        "tags": [
          "Events"
        ],
        "summary": "Count events",
        "description": "**Needs an API key with the `events:read` scope, or a signed-in session.**",
        "operationId": "events_getCount",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfint"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "events:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/events/{id}": {
      "get": {
        "tags": [
          "Events"
        ],
        "summary": "Get an event",
        "description": "**Needs an API key with the `events:read` scope, or a signed-in session.**",
        "operationId": "events_getById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfEventDto"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "events:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      },
      "put": {
        "tags": [
          "Events"
        ],
        "summary": "Update an event",
        "description": "**Needs an API key with the `events:write` scope, or a signed-in session.**",
        "operationId": "events_update",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateEventDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfEventDto"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "events:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Events"
        ],
        "summary": "Delete an event",
        "description": "Deletes the event. Certificates already issued for it stay valid and verifiable.\n\n**Needs an API key with the `events:write` scope, or a signed-in session.**",
        "operationId": "events_delete",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "events:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/events/{id}/search-visibility": {
      "put": {
        "tags": [
          "Events"
        ],
        "summary": "Show or hide an event from search engines",
        "description": "Controls whether the public verification pages of this event's certificates may be offered to search engines.\n\n**Needs an API key with the `events:write` scope, or a signed-in session.**",
        "operationId": "events_setSearchVisibility",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetSearchVisibilityRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfEventDto"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "events:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/events/{id}/attendees": {
      "get": {
        "tags": [
          "Events"
        ],
        "summary": "List the attendees of an event",
        "description": "**Needs an API key with the `events:read` scope, or a signed-in session.**",
        "operationId": "events_getAttendees",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfIEnumerableOfAttendeeDto"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "events:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/events/{id}/certificates": {
      "get": {
        "tags": [
          "Events"
        ],
        "summary": "List the certificates of an event",
        "description": "**Needs an API key with the `events:read` scope, or a signed-in session.**",
        "operationId": "events_getCertificates",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfListOfCertificateDto"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "events:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/events/{id}/certificates/generate": {
      "post": {
        "tags": [
          "Bulk issuing"
        ],
        "summary": "Issue certificates for a whole event",
        "description": "Issues a certificate for the attendees of the event with the template in `templateId`, and waits until they are done. Good for small events; for large ones, start a background job instead. Needs one credit per certificate (`402` `CREDIT_LIMIT` when there are not enough). With `regenerate=true` certificates that already exist are rendered again.\n\n**Needs an API key with the `certificates:generate` scope, or a signed-in session.**",
        "operationId": "events_bulkGenerate",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The event.",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "templateId",
            "in": "query",
            "description": "The template to issue with.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "regenerate",
            "in": "query",
            "description": "Re-render attendees who already hold a certificate instead of skipping them.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "sendEmail",
            "in": "query",
            "description": "Email each newly issued certificate to its recipient. Optional: left out, the organizer's \"Send automatically on issue\" setting decides (on by default); false holds every email back. Skipped and regenerated attendees are never emailed again, and emails never consume credits.",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfBulkCertificateGenerationResponse"
                }
              }
            }
          },
          "402": {
            "description": "No credits are left for this account (`CREDIT_LIMIT`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "certificates:generate"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/events/{id}/certificates/generate/jobs": {
      "post": {
        "tags": [
          "Bulk issuing"
        ],
        "summary": "Start a bulk issuing job",
        "description": "Queues a background job that issues a certificate for every attendee of the event with the template in `templateId`, and answers at once with the job. Follow it with `GET .../jobs/{jobId}` until it has finished, or cancel it. Starting the same job again while it is running answers the running job; starting it with different options (`regenerate`, `sendEmail`) answers `409` `CONFLICT`. Needs one credit per certificate (`402` `CREDIT_LIMIT`).\n\n**Needs an API key with the `certificates:generate` scope, or a signed-in session.**",
        "operationId": "events_startBulkGenerationJob",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "templateId",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "regenerate",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "sendEmail",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Accepted",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfBulkGenerationJobStatus"
                },
                "example": {
                  "data": {
                    "jobId": "7a1b9c2d4e",
                    "eventId": "a1b2c3d4-0000-4000-8000-000000000001",
                    "eventName": "Safety Training",
                    "regenerate": false,
                    "status": "Queued",
                    "processedCount": 0,
                    "totalCount": 120,
                    "isStalled": false,
                    "result": null
                  }
                }
              }
            }
          },
          "402": {
            "description": "No credits are left for this account (`CREDIT_LIMIT`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "409": {
            "description": "Conflict (`ALREADY_ISSUED`, `IDEMPOTENCY_IN_PROGRESS` or another `CONFLICT`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "certificates:generate"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/events/{id}/certificates/generate/jobs/{jobId}/cancel": {
      "post": {
        "tags": [
          "Bulk issuing"
        ],
        "summary": "Cancel a bulk issuing job",
        "description": "Stops the job. Certificates already issued stay issued, and credits are charged only for those.\n\n**Needs an API key with the `certificates:generate` scope, or a signed-in session.**",
        "operationId": "events_cancelBulkGenerationJob",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "jobId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "certificates:generate"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/events/{id}/certificates/generate/jobs/{jobId}": {
      "get": {
        "tags": [
          "Bulk issuing"
        ],
        "summary": "Get a bulk issuing job",
        "description": "The progress of a job: `status`, how many attendees are `processedCount` of `totalCount`, and, when it has finished, the `result` with the success, failure and skipped counts.\n\n**Needs an API key with the `events:read` scope, or a signed-in session.**",
        "operationId": "events_getBulkGenerationJob",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "jobId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfBulkGenerationJobStatus"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "events:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/certificates/generate/jobs/active": {
      "get": {
        "tags": [
          "Bulk issuing"
        ],
        "summary": "List running bulk issuing jobs",
        "description": "Every bulk issuing job of the account that is still queued or running.\n\n**Needs an API key with the `events:read` scope, or a signed-in session.**",
        "operationId": "events_getActiveBulkGenerationJobs",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfListOfBulkGenerationJobStatus"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "events:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/events/{id}/registration": {
      "get": {
        "tags": [
          "Registration"
        ],
        "summary": "Get the sign-up settings of an event",
        "description": "The public sign-up page of the event: whether it is open, its text and its limits.\n\n**Needs an API key with the `events:read` scope, or a signed-in session.**",
        "operationId": "events_getRegistration",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfRegistrationSettingsDto"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "events:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      },
      "put": {
        "tags": [
          "Registration"
        ],
        "summary": "Change the sign-up settings of an event",
        "description": "**Needs an API key with the `events:write` scope, or a signed-in session.**",
        "operationId": "events_updateRegistration",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateRegistrationSettingsDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfRegistrationSettingsDto"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "events:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/events/{eventId}/registration/questions": {
      "get": {
        "tags": [
          "Registration"
        ],
        "summary": "List the questions of the sign-up form",
        "description": "**Needs an API key with the `events:read` scope, or a signed-in session.**",
        "operationId": "registrationQuestions_list",
        "parameters": [
          {
            "name": "eventId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfIReadOnlyListOfRegistrationQuestionDto"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "events:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      },
      "post": {
        "tags": [
          "Registration"
        ],
        "summary": "Add a question to the sign-up form",
        "description": "**Needs an API key with the `events:write` scope, or a signed-in session.**",
        "operationId": "registrationQuestions_create",
        "parameters": [
          {
            "name": "eventId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SaveRegistrationQuestionDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfRegistrationQuestionDto"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "events:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/events/{eventId}/registration/questions/{questionId}": {
      "put": {
        "tags": [
          "Registration"
        ],
        "summary": "Change a question",
        "description": "**Needs an API key with the `events:write` scope, or a signed-in session.**",
        "operationId": "registrationQuestions_update",
        "parameters": [
          {
            "name": "eventId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "questionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SaveRegistrationQuestionDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfRegistrationQuestionDto"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "events:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Registration"
        ],
        "summary": "Delete a question",
        "description": "**Needs an API key with the `events:write` scope, or a signed-in session.**",
        "operationId": "registrationQuestions_delete",
        "parameters": [
          {
            "name": "eventId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "questionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "events:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/events/{eventId}/registration/questions/{questionId}/hidden": {
      "put": {
        "tags": [
          "Registration"
        ],
        "summary": "Hide or show a question",
        "description": "**Needs an API key with the `events:write` scope, or a signed-in session.**",
        "operationId": "registrationQuestions_setHidden",
        "parameters": [
          {
            "name": "eventId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "questionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SetRegistrationQuestionHiddenDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfRegistrationQuestionDto"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "events:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/events/{eventId}/registration/questions/{questionId}/move": {
      "post": {
        "tags": [
          "Registration"
        ],
        "summary": "Move a question up or down",
        "description": "**Needs an API key with the `events:write` scope, or a signed-in session.**",
        "operationId": "registrationQuestions_move",
        "parameters": [
          {
            "name": "eventId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "questionId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MoveRegistrationQuestionDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfIReadOnlyListOfRegistrationQuestionDto"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "events:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/system-templates": {
      "get": {
        "tags": [
          "Template library"
        ],
        "summary": "List the template library",
        "description": "The ready-made designs of the Certifyd library. Add `category` to narrow the list.\n\n**Needs an API key with the `templates:read` scope, or a signed-in session.**",
        "operationId": "systemTemplates_list",
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/TemplateCategory"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfIReadOnlyListOfSystemTemplateSummaryDto"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "templates:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/system-templates/search": {
      "get": {
        "tags": [
          "Template library"
        ],
        "summary": "Search the template library",
        "description": "One page of the library, filtered by category, orientation, background and a text search. 24 designs a page by default, at most 48.\n\n**Needs an API key with the `templates:read` scope, or a signed-in session.**",
        "operationId": "systemTemplates_search",
        "parameters": [
          {
            "name": "category",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/TemplateCategory"
            }
          },
          {
            "name": "orientation",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/PageOrientation"
            }
          },
          {
            "name": "background",
            "in": "query",
            "schema": {
              "$ref": "#/components/schemas/TemplateBackground"
            }
          },
          {
            "name": "q",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32",
              "default": 1
            }
          },
          {
            "name": "pageSize",
            "in": "query",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32",
              "default": 24
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfSystemTemplatePageDto"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "templates:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/system-templates/{slug}/preview": {
      "get": {
        "tags": [
          "Template library"
        ],
        "summary": "Preview a library design",
        "description": "An HTML preview of the design with sample data.\n\n**Needs an API key with the `templates:read` scope, or a signed-in session.**",
        "operationId": "systemTemplates_preview",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "lang",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "en"
            }
          },
          {
            "name": "paper",
            "in": "query",
            "schema": {
              "type": "string",
              "default": "A4"
            }
          },
          {
            "name": "badge",
            "in": "query",
            "schema": {
              "type": "boolean",
              "default": false
            }
          },
          {
            "name": "name",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "title",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "200": {
            "description": "The file.",
            "content": {
              "text/html": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "templates:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/system-templates/{slug}/use": {
      "post": {
        "tags": [
          "Template library"
        ],
        "summary": "Add a library design to your templates",
        "description": "Copies a library design into your own templates, ready to issue from. Choose the page size (`A4` or `Letter`) and the language (`en` or `es`).\n\n**Needs an API key with the `templates:write` scope, or a signed-in session.**",
        "operationId": "systemTemplates_use",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UseSystemTemplateDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfTemplateDto"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "templates:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/templates": {
      "get": {
        "tags": [
          "Templates"
        ],
        "summary": "List templates",
        "description": "Your certificate templates. Add `active=true` to leave out the ones you switched off.\n\n**Needs an API key with the `templates:read` scope, or a signed-in session.**",
        "operationId": "templates_getAll",
        "parameters": [
          {
            "name": "active",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfListOfTemplateDto"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "templates:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      },
      "post": {
        "tags": [
          "Templates"
        ],
        "summary": "Create a template",
        "description": "**Needs an API key with the `templates:write` scope, or a signed-in session.**",
        "operationId": "templates_create",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateTemplateDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "description": "Created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfTemplateDto"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "templates:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/templates/{id}": {
      "get": {
        "tags": [
          "Templates"
        ],
        "summary": "Get a template",
        "description": "**Needs an API key with the `templates:read` scope, or a signed-in session.**",
        "operationId": "templates_getById",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfTemplateDto"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "templates:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      },
      "put": {
        "tags": [
          "Templates"
        ],
        "summary": "Update a template",
        "description": "**Needs an API key with the `templates:write` scope, or a signed-in session.**",
        "operationId": "templates_update",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateTemplateDto"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfTemplateDto"
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "templates:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Templates"
        ],
        "summary": "Delete a template",
        "description": "**Needs an API key with the `templates:write` scope, or a signed-in session.**",
        "operationId": "templates_delete",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "templates:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/templates/{id}/duplicate": {
      "post": {
        "tags": [
          "Templates"
        ],
        "summary": "Duplicate a template",
        "description": "**Needs an API key with the `templates:write` scope, or a signed-in session.**",
        "operationId": "templates_duplicate",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          },
          {
            "name": "newName",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfTemplateDto"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "templates:write"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/templates/{id}/preview": {
      "post": {
        "tags": [
          "Templates"
        ],
        "summary": "List the merge fields of a template",
        "description": "Returns the template and the merge fields it expects (such as `{{recipient_name}}`). It changes nothing.\n\n**Needs an API key with the `templates:read` scope, or a signed-in session.**",
        "operationId": "templates_preview",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfTemplatePreviewResult"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "templates:read"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/verify/{verificationCode}": {
      "get": {
        "tags": [
          "Verification"
        ],
        "summary": "Verify a certificate",
        "description": "Looks a certificate up by its credential ID (for example `CFD-7X4K-92QM`; the short form printed in the QR code, `7X4K92QM`, works too) and answers whether it is genuine. **No credentials needed**: anyone can call this.\n\n`status` is `valid`, `expired` or `revoked`. The answer carries the recipient, the credential, the issuer and, for a valid certificate, ready-made links to the PDF, the badge images and the share pages. A code that does not exist answers `404`.\n\nLookups are limited to 30 a minute per client.\n\n**No credentials needed.** This operation is public.",
        "operationId": "verify_verify",
        "parameters": [
          {
            "name": "verificationCode",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfVerifiedCertificate"
                },
                "example": {
                  "data": {
                    "status": "valid",
                    "recipientName": "Ana Rivera",
                    "eventName": "Safety Training",
                    "eventDate": "2026-10-05T09:00:00Z",
                    "issueDate": "2026-10-05T14:30:00Z",
                    "certificateNumber": "CERT-2026-000123",
                    "expirationDate": null,
                    "credentialId": "CFD-7X4K-92QM",
                    "credentialTitle": "Safety Training",
                    "issuerName": "Acme Training",
                    "verifyUrl": "https://certifyd.cloud/verify/CFD-7X4K-92QM",
                    "pdfUrl": "/api/v1/verify/7X4K92QM/certificate.pdf",
                    "badgeImageUrl": "/api/v1/verify/7X4K92QM/badge.png",
                    "badgeSvgUrl": "/api/v1/verify/7X4K92QM/badge.svg"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [ ]
      }
    },
    "/api/v1/verify/hash/{sha256Hex}": {
      "get": {
        "tags": [
          "Verification"
        ],
        "summary": "Check a PDF by its fingerprint",
        "description": "Answers \"is this exactly a file Certifyd issued?\" from the **SHA-256 fingerprint** of a PDF (exactly 64 hexadecimal characters). Compute the fingerprint on your side: the file itself is never sent. **No credentials needed.**\n\n`match` is `valid` or `expired` (the file is the current one), `superseded` (an older file of a certificate that was regenerated), `revoked`, or `unknown`. A fingerprint Certifyd has never seen and one it cannot disclose give the same `unknown` answer, so the endpoint cannot be used to probe for certificates. `credentialId` is present only when the file is known and valid or superseded; no name or other personal data is ever returned.\n\nLimited to 30 lookups a minute per client.\n\n**No credentials needed.** This operation is public.",
        "operationId": "verify_hashLookup",
        "parameters": [
          {
            "name": "sha256Hex",
            "in": "path",
            "description": "Exactly 64 hexadecimal characters.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfDocumentCheckResult"
                },
                "example": {
                  "data": {
                    "match": "valid",
                    "credentialId": "CFD-7X4K-92QM"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [ ]
      }
    },
    "/api/v1/verify/{verificationCode}/qr": {
      "get": {
        "tags": [
          "Verification"
        ],
        "summary": "Get the QR code of a certificate",
        "description": "Redirects to the QR code image of the certificate. The code opens the public verification page.\n\n**No credentials needed.** This operation is public.",
        "operationId": "verify_getQrCode",
        "parameters": [
          {
            "name": "verificationCode",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Found"
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [ ]
      }
    },
    "/api/v1/verify/{verificationCode}/badge.png": {
      "get": {
        "tags": [
          "Verification"
        ],
        "summary": "Get the badge image (PNG)",
        "description": "The badge image of a valid certificate. A revoked certificate answers `404`.\n\n**No credentials needed.** This operation is public.",
        "operationId": "verify_badgePng",
        "parameters": [
          {
            "name": "verificationCode",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "200": {
            "description": "The file.",
            "content": {
              "image/png": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          }
        },
        "security": [ ]
      }
    },
    "/api/v1/verify/{verificationCode}/badge.svg": {
      "get": {
        "tags": [
          "Verification"
        ],
        "summary": "Get the badge image (SVG)",
        "description": "The badge image of a valid certificate as a vector file. A revoked certificate answers `404`.\n\n**No credentials needed.** This operation is public.",
        "operationId": "verify_badgeSvg",
        "parameters": [
          {
            "name": "verificationCode",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "200": {
            "description": "The file.",
            "content": {
              "image/svg+xml": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        },
        "security": [ ]
      }
    },
    "/api/v1/verify/{verificationCode}/certificate.pdf": {
      "get": {
        "tags": [
          "Verification"
        ],
        "summary": "Download a certificate PDF by credential ID",
        "description": "The certificate PDF of a valid credential. A revoked certificate answers `404`. Downloads are limited per client (a token bucket of 5 a minute).\n\n**No credentials needed.** This operation is public.",
        "operationId": "verify_certificatePdf",
        "parameters": [
          {
            "name": "verificationCode",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "200": {
            "description": "The file.",
            "content": {
              "application/pdf": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          }
        },
        "security": [ ]
      }
    },
    "/api/v1/webhooks": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List webhook subscriptions",
        "description": "Your subscriptions. The signing secret is never listed.\n\n**Needs an API key with the `webhooks:manage` scope, or a signed-in session.**",
        "operationId": "webhooks_getAll",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfListOfWebhookSubscriptionDto"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "webhooks:manage"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      },
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Create a webhook subscription",
        "description": "Subscribes an HTTPS endpoint to the events you choose (see the **Webhooks** section for the event names and the payloads). The URL must use HTTPS and resolve to a public address. The answer carries the `secret` used to sign every delivery: **it is shown only here**, so store it now.\n\n**Needs an API key with the `webhooks:manage` scope, or a signed-in session.**",
        "operationId": "webhooks_create",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateWebhookRequest"
              },
              "example": {
                "url": "https://your-server.example/certifyd-hook",
                "events": [
                  "certificate.issued",
                  "certificate.revoked"
                ]
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfCreatedWebhookSubscriptionDto"
                },
                "example": {
                  "data": {
                    "id": "9d0c1b2a-0000-4000-8000-000000000009",
                    "url": "https://your-server.example/certifyd-hook",
                    "events": [
                      "certificate.issued",
                      "certificate.revoked"
                    ],
                    "isActive": true,
                    "lastTriggeredAt": null,
                    "consecutiveFailureCount": 0,
                    "createdAt": "2026-10-05T14:30:00Z",
                    "secret": "whsec_example_not_a_real_secret"
                  }
                }
              }
            }
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "webhooks:manage"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/webhooks/{id}": {
      "put": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Change a webhook subscription",
        "description": "**Needs an API key with the `webhooks:manage` scope, or a signed-in session.**",
        "operationId": "webhooks_update",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateWebhookRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "204": {
            "description": "No Content"
          },
          "400": {
            "description": "The request is not valid (`VALIDATION_ERROR`). `error.details` lists what to fix.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "webhooks:manage"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      },
      "delete": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Delete a webhook subscription",
        "description": "**Needs an API key with the `webhooks:manage` scope, or a signed-in session.**",
        "operationId": "webhooks_delete",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "No Content"
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "webhooks:manage"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/webhooks/{id}/enable": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Turn a webhook subscription back on",
        "description": "A subscription that fails 10 times in a row is switched off. Fix your endpoint, then call this to switch it on again; its URL, events and secret stay as they were.\n\n**Needs an API key with the `webhooks:manage` scope, or a signed-in session.**",
        "operationId": "webhooks_enable",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "The subscription is active again."
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "webhooks:manage"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/webhooks/{id}/deliveries": {
      "get": {
        "tags": [
          "Webhooks"
        ],
        "summary": "List the deliveries of a subscription",
        "description": "The delivery log of the subscription, newest first: each attempt, the status your endpoint answered and whether it succeeded.\n\n**Needs an API key with the `webhooks:manage` scope, or a signed-in session.**",
        "operationId": "webhooks_getDeliveries",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiResponseOfListOfWebhookDeliveryDto"
                }
              }
            }
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "webhooks:manage"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    },
    "/api/v1/webhooks/{id}/test": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "Send a test delivery",
        "description": "Queues a `webhook.test` delivery to the subscription so you can check your endpoint and your signature code.\n\n**Needs an API key with the `webhooks:manage` scope, or a signed-in session.**",
        "operationId": "webhooks_test",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "202": {
            "description": "Accepted"
          },
          "404": {
            "description": "Nothing with that id (or code) exists in your account (`NOT_FOUND`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (`RATE_LIMITED`). Wait for the seconds in the `Retry-After` header, then try again.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "401": {
            "description": "The API key or token is missing, wrong, expired or revoked (`INVALID_API_KEY`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          },
          "403": {
            "description": "The API key does not have the scope this operation needs (`INSUFFICIENT_SCOPE`, with the scope in `error.required`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorResponse"
                }
              }
            }
          }
        },
        "security": [
          {
            "ApiKey": [
              "webhooks:manage"
            ]
          },
          {
            "BearerJwt": [ ]
          }
        ]
      }
    }
  },
  "components": {
    "schemas": {
      "ApiError": {
        "required": [
          "code",
          "message"
        ],
        "type": "object",
        "properties": {
          "code": {
            "type": "string",
            "description": "Stable, machine-readable error code (e.g. `INVALID_API_KEY`, `INSUFFICIENT_SCOPE`)."
          },
          "message": {
            "type": "string",
            "description": "Human-readable description, safe to display or log."
          },
          "details": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            },
            "description": "Optional list of granular validation messages, when Code is a validation failure."
          },
          "required": {
            "type": [
              "null",
              "string"
            ],
            "description": "Optional scope string the caller's API key was missing, set only for `INSUFFICIENT_SCOPE` responses (see `RequireApiScopeAttribute`). Left `null` for every other error code so existing consumers that already parse ApiError are unaffected."
          },
          "existing": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The certificate that already exists, set only for `ALREADY_ISSUED` so a client that retried an issue request gets the original without a second GET. Omitted from the JSON entirely when null, so every other error keeps its exact shape.",
                "$ref": "#/components/schemas/ExistingCertificateRef"
              }
            ]
          }
        },
        "description": "A single machine-readable API error."
      },
      "ApiErrorResponse": {
        "required": [
          "error"
        ],
        "type": "object",
        "properties": {
          "error": {
            "description": "The error that occurred.",
            "$ref": "#/components/schemas/ApiError"
          }
        },
        "description": "Envelope wrapping a single ApiError, the body of every non-2xx v1 API response."
      },
      "ApiKeySummary": {
        "required": [
          "id",
          "name",
          "prefix",
          "scopes",
          "expiresAt",
          "lastUsedAt",
          "createdAt"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The key id.",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "The name you gave it."
          },
          "prefix": {
            "type": "string",
            "description": "The first characters of the key, to recognise it."
          },
          "scopes": {
            "type": [
              "null",
              "string"
            ],
            "description": "The scopes it was created with, as a JSON array in text form."
          },
          "expiresAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When it expires. Null when it does not.",
            "format": "date-time"
          },
          "lastUsedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When it was last used. Null when never.",
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "description": "When it was created.",
            "format": "date-time"
          }
        },
        "description": "One API key as listed by `GET /api/v1/api-keys`. The secret itself is never shown again."
      },
      "ApiResponseOfAttendeeDto": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/AttendeeDto"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfBadgeDto": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/BadgeDto"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfBrandingSettingsResponse": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/BrandingSettingsResponse"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfBulkCertificateGenerationResponse": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/BulkCertificateGenerationResponse"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfBulkEmailResult": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/BulkEmailResult"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfBulkGenerationJobStatus": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/BulkGenerationJobStatus"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfCertificateDto": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/CertificateDto"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfCertificateEmailSettings": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/CertificateEmailSettings"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfCertificateSendResult": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/CertificateSendResult"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfCreatedApiKey": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/CreatedApiKey"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfCreatedWebhookSubscriptionDto": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/CreatedWebhookSubscriptionDto"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfCreditUsageDto": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/CreditUsageDto"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfCsvImportResult": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/CsvImportResult"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfDashboardStats": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/DashboardStats"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfDocumentCheckResult": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/DocumentCheckResult"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfEmailPreviewResult": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/EmailPreviewResult"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfEventDto": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/EventDto"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfIEnumerableOfAttendeeDto": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/AttendeeDto"
            },
            "description": "The payload."
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfIEnumerableOfEventDto": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/EventDto"
            },
            "description": "The payload."
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfint": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The payload.",
            "format": "int32"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfIReadOnlyListOfRegistrationQuestionDto": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "description": "The organizer's view of one custom registration question.",
              "$ref": "#/components/schemas/RegistrationQuestionDto"
            },
            "description": "The payload."
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfIReadOnlyListOfSystemTemplateSummaryDto": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/SystemTemplateSummaryDto"
            },
            "description": "The payload."
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfListOfApiKeySummary": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "description": "One API key as listed by `GET /api/v1/api-keys`. The secret itself is never shown again.",
              "$ref": "#/components/schemas/ApiKeySummary"
            },
            "description": "The payload."
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfListOfBadgeAssertionDto": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "description": "An issued badge (assertion) as the API returns it.",
              "$ref": "#/components/schemas/BadgeAssertionDto"
            },
            "description": "The payload."
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfListOfBadgeDto": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/BadgeDto"
            },
            "description": "The payload."
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfListOfBulkGenerationJobStatus": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/BulkGenerationJobStatus"
            },
            "description": "The payload."
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfListOfCertificateDto": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/CertificateDto"
            },
            "description": "The payload."
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfListOfEmailDeliveryDto": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/EmailDeliveryDto"
            },
            "description": "The payload."
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfListOfEventAnalytics": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/EventAnalytics"
            },
            "description": "The payload."
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfListOfTemplateDto": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "$ref": "#/components/schemas/TemplateDto"
            },
            "description": "The payload."
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfListOfWebhookDeliveryDto": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "description": "One row of a subscription's delivery log.",
              "$ref": "#/components/schemas/WebhookDeliveryDto"
            },
            "description": "The payload."
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfListOfWebhookSubscriptionDto": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "description": "A webhook subscription as returned by list/get. Deliberately has no secret.",
              "$ref": "#/components/schemas/WebhookSubscriptionDto"
            },
            "description": "The payload."
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfRegistrationQuestionDto": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/RegistrationQuestionDto"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfRegistrationSettingsDto": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/RegistrationSettingsDto"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfSystemTemplatePageDto": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/SystemTemplatePageDto"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfTemplateDto": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/TemplateDto"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfTemplatePreviewResult": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/TemplatePreviewResult"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfTestEmailResult": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/TestEmailResult"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfTokenResponse": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/TokenResponse"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfUserProfileResponse": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/UserProfileResponse"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "ApiResponseOfVerifiedCertificate": {
        "required": [
          "data"
        ],
        "type": "object",
        "properties": {
          "data": {
            "description": "The payload.",
            "$ref": "#/components/schemas/VerifiedCertificate"
          },
          "meta": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Optional pagination metadata, present on list endpoints that paginate.",
                "$ref": "#/components/schemas/PaginationMeta"
              }
            ]
          }
        },
        "description": "Standard success envelope returned by every v1 API endpoint."
      },
      "AttendeeDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique identifier of the attendee.",
            "format": "uuid"
          },
          "eventId": {
            "type": "string",
            "description": "Event ID this attendee is associated with.",
            "format": "uuid"
          },
          "eventName": {
            "type": "string"
          },
          "firstName": {
            "type": "string",
            "description": "Attendee's first name."
          },
          "lastName": {
            "type": "string",
            "description": "Attendee's last name."
          },
          "email": {
            "type": "string",
            "description": "Attendee's email address."
          },
          "organization": {
            "type": [
              "null",
              "string"
            ],
            "description": "Attendee's organization or company name."
          },
          "additionalInfo": {
            "type": [
              "null",
              "string"
            ],
            "description": "Additional information about the attendee in JSON format. Used for custom fields and merge data."
          },
          "importBatchId": {
            "type": [
              "null",
              "string"
            ],
            "description": "Import batch ID if this attendee was imported from a file.",
            "format": "uuid"
          },
          "registrationAnswers": {
            "type": "object",
            "description": "Attendee's answers to the event's custom registration questions, keyed by question key: a string (Dropdown, Short text, Long text), an array of strings (Multiple choice) or a boolean (Yes/No). Read-only: organizer edits and imports never change it. Empty when the attendee answered nothing."
          },
          "createdAt": {
            "type": "string",
            "description": "Date and time when the attendee record was created.",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "description": "Date and time when the attendee record was last updated.",
            "format": "date-time"
          },
          "fullName": {
            "type": [
              "null",
              "string"
            ],
            "description": "Attendee's full name (computed property)."
          },
          "hasCertificate": {
            "type": "boolean",
            "description": "Value indicating whether this attendee has a certificate issued. This is a computed statistic loaded separately from the Certificates collection."
          }
        }
      },
      "BadgeAssertionDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The assertion id.",
            "format": "uuid"
          },
          "badgeId": {
            "type": "string",
            "description": "The badge definition it was issued from.",
            "format": "uuid"
          },
          "badgeName": {
            "type": "string",
            "description": "The badge definition's name."
          },
          "attendeeId": {
            "type": "string",
            "description": "The attendee who holds it.",
            "format": "uuid"
          },
          "attendeeName": {
            "type": "string",
            "description": "The attendee's display name."
          },
          "evidenceUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "Optional evidence URL recorded at issue time."
          },
          "bakedImageUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "URL of the baked badge image, when one exists."
          },
          "issuedAt": {
            "type": "string",
            "description": "When it was issued (UTC).",
            "format": "date-time"
          },
          "alreadyIssued": {
            "type": "boolean",
            "description": "True when an issue request found this assertion already in place instead of creating it: the attendee already held the badge, so nothing new was issued or charged. Always false outside an issue response."
          }
        },
        "description": "An issued badge (assertion) as the API returns it."
      },
      "BadgeDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "imageUrl": {
            "type": [
              "null",
              "string"
            ]
          },
          "criteriaText": {
            "type": [
              "null",
              "string"
            ]
          },
          "criteriaUrl": {
            "type": [
              "null",
              "string"
            ]
          },
          "skillTags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "issuerName": {
            "type": "string"
          },
          "issuerUrl": {
            "type": [
              "null",
              "string"
            ]
          },
          "isActive": {
            "type": "boolean"
          },
          "assertionCount": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "BadgeShape": {
        "type": "integer"
      },
      "BrandingSettingsResponse": {
        "required": [
          "id",
          "logoUrl",
          "primaryColor",
          "secondaryColor",
          "customDomain",
          "customDomainVerified",
          "customFromEmail",
          "customFromName",
          "customFooterText",
          "hidePoweredBy",
          "securityLineEnabled",
          "hideFromSearch",
          "issuerName",
          "signer1Name",
          "signer1Title",
          "hasSigner1Signature",
          "signer2Name",
          "signer2Title",
          "hasSigner2Signature",
          "updatedAt"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The branding record id.",
            "format": "uuid"
          },
          "logoUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "Path of the logo."
          },
          "primaryColor": {
            "type": [
              "null",
              "string"
            ],
            "description": "The main colour, as a hex code."
          },
          "secondaryColor": {
            "type": [
              "null",
              "string"
            ],
            "description": "The second colour, as a hex code."
          },
          "customDomain": {
            "type": [
              "null",
              "string"
            ],
            "description": "A custom domain, when set."
          },
          "customDomainVerified": {
            "type": "boolean",
            "description": "Whether that domain has been verified."
          },
          "customFromEmail": {
            "type": [
              "null",
              "string"
            ],
            "description": "The sender address of certificate emails, when customised."
          },
          "customFromName": {
            "type": [
              "null",
              "string"
            ],
            "description": "The sender name of certificate emails, when customised."
          },
          "customFooterText": {
            "type": [
              "null",
              "string"
            ],
            "description": "A custom footer line."
          },
          "hidePoweredBy": {
            "type": "boolean",
            "description": "Whether the \"Powered by Certifyd\" line is hidden."
          },
          "securityLineEnabled": {
            "type": "boolean",
            "description": "Whether certificates carry the security line border."
          },
          "hideFromSearch": {
            "type": "boolean",
            "description": "Whether the account's public pages are kept out of search engines."
          },
          "issuerName": {
            "type": [
              "null",
              "string"
            ],
            "description": "The issuer name printed on certificates and shown when verifying."
          },
          "signer1Name": {
            "type": [
              "null",
              "string"
            ],
            "description": "Name of the first signer."
          },
          "signer1Title": {
            "type": [
              "null",
              "string"
            ],
            "description": "Title of the first signer."
          },
          "hasSigner1Signature": {
            "type": "boolean",
            "description": "Whether a signature image is uploaded for the first signer."
          },
          "signer2Name": {
            "type": [
              "null",
              "string"
            ],
            "description": "Name of the second signer."
          },
          "signer2Title": {
            "type": [
              "null",
              "string"
            ],
            "description": "Title of the second signer."
          },
          "hasSigner2Signature": {
            "type": "boolean",
            "description": "Whether a signature image is uploaded for the second signer."
          },
          "updatedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When the branding last changed.",
            "format": "date-time"
          }
        },
        "description": "The account's branding, answered by `GET /api/v1/branding`."
      },
      "BulkCertificateGenerationResponse": {
        "type": "object",
        "properties": {
          "certificates": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CertificateDto"
            }
          },
          "summary": {
            "description": "Represents the result of a bulk certificate generation operation. Provides detailed metrics, timing information, and error tracking for the job.",
            "$ref": "#/components/schemas/BulkGenerationResult"
          }
        },
        "description": "Response envelope for the synchronous bulk certificate generation endpoint. Carries both the list of certificates that actually generated AND a per-attendee failure breakdown so the caller can tell the difference between \"0 attendees\" and \"10 attendees, all failed with PDF error X\"."
      },
      "BulkEmailResult": {
        "required": [
          "deliveryIds",
          "count",
          "sent",
          "skippedAlreadyEmailed",
          "failed"
        ],
        "type": "object",
        "properties": {
          "deliveryIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            },
            "description": "The ids of the emails that were attempted."
          },
          "count": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How many emails were attempted (the same as the length of `deliveryIds`).",
            "format": "int32"
          },
          "sent": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How many were sent.",
            "format": "int32"
          },
          "skippedAlreadyEmailed": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How many recipients were skipped because they had already been emailed.",
            "format": "int32"
          },
          "failed": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "How many could not be sent.",
            "format": "int32"
          }
        },
        "description": "The answer of `POST /api/v1/email/send-bulk`."
      },
      "BulkGenerationError": {
        "type": "object",
        "properties": {
          "attendeeId": {
            "type": "string",
            "description": "Attendee ID that failed to generate a certificate.",
            "format": "uuid"
          },
          "attendeeName": {
            "type": "string",
            "description": "Full name of the attendee for easier identification. Format: \"FirstName LastName\""
          },
          "errorMessage": {
            "examples": [
              null
            ],
            "type": "string",
            "description": "Error message describing why the certificate generation failed."
          },
          "erroredAt": {
            "type": "string",
            "description": "Timestamp when the error occurred.",
            "format": "date-time"
          }
        },
        "description": "Represents an error that occurred during bulk certificate generation for a specific attendee."
      },
      "BulkGenerationJobStatus": {
        "type": "object",
        "properties": {
          "jobId": {
            "type": "string"
          },
          "eventId": {
            "type": "string",
            "description": "The event the job issues for. Lets a client that only knows the job poll it and link back to the event.",
            "format": "uuid"
          },
          "eventName": {
            "type": [
              "null",
              "string"
            ]
          },
          "regenerate": {
            "type": "boolean",
            "description": "Whether the job re-renders existing certificates (Regenerate) rather than issuing new ones."
          },
          "status": {
            "type": "string",
            "description": "\"Queued\" until a worker picks the job up, then the generator's own status (\"Processing\", \"Completed\")."
          },
          "processedCount": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Attendees handled so far, including skipped ones.",
            "format": "int32"
          },
          "totalCount": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Attendees in the job. While the job still waits for a worker (\"Queued\") this is the event's attendee count recorded when it was queued; once a worker has loaded the attendees it is the generator's own total.",
            "format": "int32"
          },
          "isStalled": {
            "type": "boolean"
          },
          "result": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The counts so far, including who was skipped as already issued. Null until the job starts.",
                "$ref": "#/components/schemas/BulkGenerationResult"
              }
            ]
          }
        }
      },
      "BulkGenerationResult": {
        "type": "object",
        "properties": {
          "jobId": {
            "type": "string",
            "description": "Unique job identifier (GUID without hyphens). Used to track and retrieve job progress and results."
          },
          "totalCount": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Total number of certificates requested for generation. This includes successful, failed, and skipped certificates.",
            "format": "int32"
          },
          "successCount": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Number of certificates successfully generated.",
            "format": "int32"
          },
          "failureCount": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Number of certificates that failed to generate. See Errors collection for detailed failure information.",
            "format": "int32"
          },
          "skippedCount": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Number of attendees that were skipped because they already hold a non-revoked certificate for the event. See List&lt;BulkGenerationSkip&gt; BulkGenerationResult.Skipped.",
            "format": "int32"
          },
          "skipped": {
            "type": "array",
            "items": {
              "description": "An attendee a bulk run left alone because they already hold a certificate for the event.",
              "$ref": "#/components/schemas/BulkGenerationSkip"
            }
          },
          "regeneratedCount": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "errors": {
            "type": "array",
            "items": {
              "description": "Represents an error that occurred during bulk certificate generation for a specific attendee.",
              "$ref": "#/components/schemas/BulkGenerationError"
            },
            "description": "Collection of errors that occurred during bulk generation. Each error includes attendee information and the specific error message."
          },
          "status": {
            "type": [
              "null",
              "string"
            ]
          },
          "startedAt": {
            "type": "string",
            "description": "Timestamp when the job started processing.",
            "format": "date-time"
          },
          "completedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "Timestamp when the job completed processing. Null if the job is still in progress.",
            "format": "date-time"
          },
          "duration": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": [
              "null",
              "string"
            ],
            "description": "Total duration of the job execution. Returns null if the job has not completed yet."
          },
          "isCompleted": {
            "type": "boolean",
            "description": "Value indicating whether the job has completed processing. True if CompletedAt has a value, regardless of success/failure counts."
          },
          "successRate": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "description": "Success rate as a percentage (0-100). Calculated as (SuccessCount / TotalCount) * 100. Returns 0 if TotalCount is 0.",
            "format": "double"
          }
        },
        "description": "Represents the result of a bulk certificate generation operation. Provides detailed metrics, timing information, and error tracking for the job."
      },
      "BulkGenerationSkip": {
        "type": "object",
        "properties": {
          "attendeeId": {
            "type": "string",
            "description": "Attendee that was skipped.",
            "format": "uuid"
          },
          "attendeeName": {
            "type": "string",
            "description": "Attendee's full name, for display."
          },
          "existingCertificateId": {
            "type": "string",
            "description": "Id of the certificate the attendee already holds.",
            "format": "uuid"
          },
          "existingCertificateNumber": {
            "type": "string",
            "description": "Number of the certificate the attendee already holds."
          },
          "reason": {
            "type": "string",
            "description": "Why the attendee was skipped."
          }
        },
        "description": "An attendee a bulk run left alone because they already hold a certificate for the event."
      },
      "CertificateDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique identifier for this certificate.",
            "format": "uuid"
          },
          "eventId": {
            "type": "string",
            "description": "Event ID this certificate is for.",
            "format": "uuid"
          },
          "attendeeId": {
            "type": "string",
            "description": "Attendee ID this certificate is issued to.",
            "format": "uuid"
          },
          "templateId": {
            "type": "string",
            "description": "Template ID used to generate this certificate.",
            "format": "uuid"
          },
          "userId": {
            "type": "string"
          },
          "certificateNumber": {
            "type": "string",
            "description": "Unique certificate number for tracking and verification. Format: CERT-YYYY-NNNNNN (e.g., CERT-2025-000123)"
          },
          "verificationCode": {
            "type": [
              "null",
              "string"
            ]
          },
          "issueDate": {
            "type": "string",
            "description": "Date this certificate was officially issued. This is the date printed on the certificate itself.",
            "format": "date-time"
          },
          "expirationDate": {
            "type": [
              "null",
              "string"
            ],
            "description": "When this certificate expires, or null when it never expires. Printed as \"Valid until\" on library designs that have that slot.",
            "format": "date-time"
          },
          "pdfUrl": {
            "type": "string",
            "description": "URL to the generated PDF file. This URL can be used to download or view the certificate PDF."
          },
          "generatedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "Date and time when the PDF was generated. This may differ from IssueDate if the certificate is regenerated.",
            "format": "date-time"
          },
          "status": {
            "type": "string",
            "description": "Current status of the certificate. Valid values: \"Draft\", \"Generated\", \"Sent\", \"Viewed\", \"Active\", \"Expired\", \"Revoked\"."
          },
          "wasIssued": {
            "type": "boolean"
          },
          "customData": {
            "type": [
              "null",
              "string"
            ],
            "description": "Custom data for this certificate in JSON format. Used for merge fields and personalization."
          },
          "createdAt": {
            "type": "string",
            "description": "Date and time when this certificate record was created.",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "description": "Date and time when this certificate record was last updated.",
            "format": "date-time"
          },
          "attendeeName": {
            "type": [
              "null",
              "string"
            ]
          },
          "eventName": {
            "type": [
              "null",
              "string"
            ]
          },
          "templateName": {
            "type": [
              "null",
              "string"
            ]
          },
          "hideFromSearch": {
            "type": "boolean",
            "description": "Whether the recipient hid the public verify page from search engines (mapped from `Certificate.HideFromSearch`; the recipient portal shows it as a switch)."
          }
        }
      },
      "CertificateEmailSettings": {
        "type": "object",
        "properties": {
          "sendAutomatically": {
            "type": "boolean",
            "description": "Whether newly issued certificates email the recipient automatically. It is the default for the \"Send certificate emails now\" checkbox in the Generate dialogs and for API calls that leave `sendEmail` out. Default on."
          },
          "subjectEn": {
            "type": [
              "null",
              "string"
            ],
            "description": "English subject line; blank uses the built-in one."
          },
          "subjectEs": {
            "type": [
              "null",
              "string"
            ],
            "description": "Spanish subject line; blank uses the built-in one."
          },
          "headlineEn": {
            "type": [
              "null",
              "string"
            ],
            "description": "English headline; blank uses \"Congratulations, {recipient_name}!\"."
          },
          "headlineEs": {
            "type": [
              "null",
              "string"
            ],
            "description": "Spanish headline; blank uses \"¡Felicidades, {recipient_name}!\"."
          },
          "messageEn": {
            "type": [
              "null",
              "string"
            ],
            "description": "English message paragraph (plain text; line breaks are kept)."
          },
          "messageEs": {
            "type": [
              "null",
              "string"
            ],
            "description": "Spanish message paragraph (plain text; line breaks are kept)."
          },
          "showDownloadPdf": {
            "type": "boolean",
            "description": "Show the \"Download PDF\" button. \"View your certificate\" is always shown."
          },
          "showLinkedIn": {
            "type": "boolean",
            "description": "Show the \"Add to LinkedIn\" button."
          },
          "showShare": {
            "type": "boolean",
            "description": "Show the share links (LinkedIn feed, X, Facebook)."
          },
          "showVerify": {
            "type": "boolean",
            "description": "Show the \"Verify this credential\" link."
          },
          "attachPdf": {
            "type": "boolean",
            "description": "Attach the certificate PDF to the email. Default off: a link keeps the email small."
          },
          "replyToEmail": {
            "type": [
              "null",
              "string"
            ],
            "description": "Where recipients' replies go. Validated as a single plain address."
          },
          "fromName": {
            "type": [
              "null",
              "string"
            ],
            "description": "Display name for the organizer. The From <em>address</em> is always the verified ACS sender, and ACS fixes the From display name on the sender resource, so this name is used as the Reply-To display name and in the email's own sign-off."
          },
          "buttonColor": {
            "type": [
              "null",
              "string"
            ]
          },
          "effectiveButtonColor": {
            "type": [
              "null",
              "string"
            ],
            "description": "The primary button colour: the organizer's valid #RRGGBB, else the brand amber."
          }
        },
        "description": "The organizer's settings for the automatic \"certificate-ready\" email. Every value is optional: blank means \"use the built-in default\", so an account that never opens the Emails section still sends the full designed email."
      },
      "CertificateSendResult": {
        "required": [
          "deliveryId"
        ],
        "type": "object",
        "properties": {
          "deliveryId": {
            "type": "string",
            "description": "The id of the email delivery, to follow in `GET /api/v1/email/deliveries`.",
            "format": "uuid"
          }
        },
        "description": "The answer of `POST /api/v1/certificates/{id}/send`."
      },
      "CreateApiKeyRequest": {
        "required": [
          "name",
          "scopes",
          "expiresInDays"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Display name of the key."
          },
          "scopes": {
            "type": [
              "null",
              "array"
            ],
            "items": {
              "type": "string"
            },
            "description": "Granted scopes."
          },
          "expiresInDays": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Days until the key expires; null never expires.",
            "format": "int32"
          },
          "currentPassword": {
            "type": [
              "null",
              "string"
            ],
            "description": "The account password, needed when the session did not sign in within the last 10 minutes."
          },
          "twoFactorCode": {
            "type": [
              "null",
              "string"
            ],
            "description": "Alternatively a current two-step code or a recovery code, for accounts with two-step sign-in (and the only option for accounts without a password)."
          }
        },
        "description": "Body of `POST api/v1/api-keys`. Name is at most 100 characters (the ApiKeys column; longer was a 500, review H7). ExpiresInDays, when given, is 1 to 3650: zero or less created a key that was already expired while answering 200, and a huge value made `DateTime.AddDays` throw (a 500)."
      },
      "CreateAttendeeDto": {
        "type": "object",
        "properties": {
          "eventId": {
            "type": "string",
            "description": "Event ID this attendee is associated with.",
            "format": "uuid"
          },
          "firstName": {
            "type": "string",
            "description": "Attendee's first name."
          },
          "lastName": {
            "type": "string",
            "description": "Attendee's last name."
          },
          "email": {
            "type": "string",
            "description": "Attendee's email address."
          },
          "organization": {
            "type": [
              "null",
              "string"
            ],
            "description": "Attendee's organization or company name."
          },
          "additionalInfo": {
            "type": [
              "null",
              "string"
            ],
            "description": "Additional information about the attendee in JSON format. Used for custom fields and merge data."
          },
          "importBatchId": {
            "type": [
              "null",
              "string"
            ],
            "description": "Import batch ID if this attendee is being created as part of a bulk import.",
            "format": "uuid"
          }
        }
      },
      "CreateBadgeDto": {
        "type": "object",
        "properties": {
          "name": {
            "maxLength": 200,
            "minLength": 0,
            "type": "string"
          },
          "description": {
            "maxLength": 1000,
            "minLength": 0,
            "type": "string"
          },
          "imageUrl": {
            "maxLength": 500,
            "minLength": 0,
            "type": [
              "null",
              "string"
            ]
          },
          "criteriaText": {
            "maxLength": 2000,
            "minLength": 0,
            "type": [
              "null",
              "string"
            ]
          },
          "criteriaUrl": {
            "maxLength": 500,
            "minLength": 0,
            "type": [
              "null",
              "string"
            ]
          },
          "skillTags": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "issuerName": {
            "maxLength": 200,
            "minLength": 0,
            "type": "string"
          },
          "issuerUrl": {
            "maxLength": 500,
            "minLength": 0,
            "type": [
              "null",
              "string"
            ]
          }
        },
        "description": "Body of badge create and update. The length limits match the Badges columns: without them an over-long value reached SQL Server and came back as a 500 (review H7). They are checked by the API's model validation, so the answer is a 400 naming the field."
      },
      "CreatedApiKey": {
        "required": [
          "id",
          "name",
          "prefix",
          "scopes",
          "expiresAt",
          "createdAt",
          "key"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The key id.",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "The name you gave it."
          },
          "prefix": {
            "type": "string",
            "description": "The first characters of the key, to recognise it."
          },
          "scopes": {
            "type": [
              "null",
              "string"
            ],
            "description": "The scopes it was created with, as a JSON array in text form."
          },
          "expiresAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When it expires. Null when it does not.",
            "format": "date-time"
          },
          "createdAt": {
            "type": "string",
            "description": "When it was created.",
            "format": "date-time"
          },
          "key": {
            "type": "string",
            "description": "The full key. It is shown only in this response: store it now."
          }
        },
        "description": "A new API key, answered once by `POST /api/v1/api-keys`."
      },
      "CreatedWebhookSubscriptionDto": {
        "type": "object",
        "properties": {
          "secret": {
            "type": "string",
            "description": "HMAC-SHA256 key for the `X-Certifyd-Signature` header, which signs `\"{X-Certifyd-Timestamp}.{raw body}\"`. Shown once; store it on the receiving side."
          },
          "id": {
            "type": "string",
            "description": "Subscription id.",
            "format": "uuid"
          },
          "url": {
            "type": "string",
            "description": "The https endpoint deliveries are POSTed to."
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Event names this subscription listens for."
          },
          "isActive": {
            "type": "boolean",
            "description": "False once disabled (for example after too many consecutive failures)."
          },
          "lastTriggeredAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When an event last matched this subscription.",
            "format": "date-time"
          },
          "consecutiveFailureCount": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Failed attempts since the last success.",
            "format": "int32"
          },
          "createdAt": {
            "type": "string",
            "description": "Creation time (UTC).",
            "format": "date-time"
          }
        },
        "description": "The create response: the subscription plus its signing secret. The secret is shown only in this response, like a new API key: it is never shown again and cannot be fetched later."
      },
      "CreateEventDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Event name."
          },
          "description": {
            "type": [
              "null",
              "string"
            ],
            "description": "Event description."
          },
          "eventDate": {
            "type": "string",
            "description": "Date and time when the event occurred or will occur.",
            "format": "date-time"
          },
          "location": {
            "type": [
              "null",
              "string"
            ],
            "description": "Event location."
          },
          "templateId": {
            "type": [
              "null",
              "string"
            ],
            "description": "Template ID to use for this event's certificates. This is optional; events can be created without a default template.",
            "format": "uuid"
          },
          "certificateValidityMonths": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "How many months certificates issued for this event stay valid, counted from their issue date. Null means they never expire. Years are sent as months (years × 12).",
            "format": "int32"
          }
        }
      },
      "CreateTemplateDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Template name."
          },
          "description": {
            "type": [
              "null",
              "string"
            ],
            "description": "Template description."
          },
          "htmlTemplate": {
            "type": "string"
          },
          "cssStyles": {
            "type": "string"
          },
          "pageSize": {
            "type": "string",
            "description": "Page size (e.g., \"A4\", \"Letter\", \"A5\"). Defaults to \"A4\"."
          },
          "orientation": {
            "type": "string"
          },
          "backgroundImageUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "URL to the background image for the certificate."
          },
          "design": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "A design document (schema v1). With it, the template is created as a Visual template whose version 1 is published; without it (and without HTML) it starts on the blank design as a draft.",
                "$ref": "#/components/schemas/JsonElement"
              }
            ]
          },
          "language": {
            "type": [
              "null",
              "string"
            ],
            "description": "Default certificate language (en, es, fr, pt-BR); null means en."
          }
        }
      },
      "CreateWebhookRequest": {
        "required": [
          "url",
          "events"
        ],
        "type": "object",
        "properties": {
          "url": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "CreditUsageDto": {
        "required": [
          "creditsUsed",
          "creditsLimit",
          "creditsRemaining",
          "tier",
          "periodStart"
        ],
        "type": "object",
        "properties": {
          "creditsUsed": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "creditsLimit": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "creditsRemaining": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "tier": {
            "type": "string"
          },
          "periodStart": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "CsvImportError": {
        "type": "object",
        "properties": {
          "rowNumber": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Row number in the CSV file where the error occurred. Row numbers are 1-indexed and account for the header row (first data row is 2).",
            "format": "int32"
          },
          "email": {
            "type": "string",
            "description": "Email address from the row that failed (if available). Helps identify which attendee record caused the error."
          },
          "errorMessage": {
            "type": "string",
            "description": "Detailed error message explaining what went wrong."
          },
          "errorType": {
            "type": "string",
            "description": "Type/category of error that occurred. Common values: \"Validation\", \"Duplicate\", \"Parsing\", \"Unknown\""
          },
          "errorCode": {
            "type": "string",
            "description": "Stable, language-neutral code for the error (see CsvImportErrorCodes)."
          }
        }
      },
      "CsvImportResult": {
        "type": "object",
        "properties": {
          "importBatchId": {
            "type": "string",
            "description": "Unique identifier for this import batch. All attendees imported in this batch share this ID for tracking purposes.",
            "format": "uuid"
          },
          "totalRows": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Total number of rows processed from the CSV file (excluding header).",
            "format": "int32"
          },
          "successCount": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Number of attendees successfully imported.",
            "format": "int32"
          },
          "failureCount": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Number of rows that failed to import due to validation or other errors.",
            "format": "int32"
          },
          "skippedCount": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Number of duplicate rows skipped because \"Skip duplicates\" was on.",
            "format": "int32"
          },
          "errors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CsvImportError"
            },
            "description": "List of errors encountered during the import process. Each error includes the row number, email, and error details."
          },
          "processingTime": {
            "pattern": "^-?(\\d+\\.)?\\d{2}:\\d{2}:\\d{2}(\\.\\d{1,7})?$",
            "type": "string",
            "description": "Total time taken to process the import."
          },
          "importedAt": {
            "type": "string",
            "description": "UTC timestamp when the import was completed.",
            "format": "date-time"
          },
          "hasErrors": {
            "type": "boolean",
            "description": "Value indicating whether any errors occurred during the import."
          },
          "successRate": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "description": "Success rate as a percentage (0-100). Returns 0 if no rows were processed.",
            "format": "double"
          }
        }
      },
      "DashboardStats": {
        "type": "object",
        "properties": {
          "totalCredentials": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "totalViews": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "totalShares": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "totalDownloads": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "verificationRate": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "type": [
              "number",
              "string"
            ],
            "format": "double"
          },
          "sharesByPlatform": {
            "type": "object",
            "additionalProperties": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": [
                "integer",
                "string"
              ],
              "format": "int32"
            },
            "description": "Account-wide share counts per channel (e.g. `{\"linkedin\":5,\"twitter\":2}`), summed from every daily summary's `SharesByPlatform`. Only shares made through the tracked share endpoints (`api/v1/share/certificate/{id}/{platform}`) are counted; links the recipient forwards another way never reach the server. Shares logged without a platform are grouped under `\"unknown\"`."
          }
        }
      },
      "DocumentCheckResult": {
        "required": [
          "match"
        ],
        "type": "object",
        "properties": {
          "match": {
            "type": "string",
            "description": "One of DocumentMatch."
          },
          "credentialId": {
            "type": [
              "null",
              "string"
            ]
          }
        },
        "description": "The result of looking a fingerprint up."
      },
      "EmailDeliveryDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "certificateId": {
            "type": "string",
            "format": "uuid"
          },
          "recipientEmail": {
            "type": "string"
          },
          "templateName": {
            "type": "string"
          },
          "status": {
            "$ref": "#/components/schemas/EmailDeliveryStatus"
          },
          "sentAt": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "openedAt": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "clickedAt": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "failedAt": {
            "type": [
              "null",
              "string"
            ],
            "format": "date-time"
          },
          "errorMessage": {
            "type": [
              "null",
              "string"
            ]
          },
          "retryCount": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "EmailDeliveryStatus": {
        "type": "integer"
      },
      "EmailPreviewRequest": {
        "required": [
          "settings",
          "language"
        ],
        "type": "object",
        "properties": {
          "settings": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The settings to render (may be unsaved); null uses the defaults.",
                "$ref": "#/components/schemas/CertificateEmailSettings"
              }
            ]
          },
          "language": {
            "type": [
              "null",
              "string"
            ],
            "description": "\"en\" or \"es\"; null is English."
          }
        },
        "description": "Body of the Branding › Emails preview and test-email calls."
      },
      "EmailPreviewResult": {
        "required": [
          "subject",
          "html"
        ],
        "type": "object",
        "properties": {
          "subject": {
            "type": "string",
            "description": "The email subject."
          },
          "html": {
            "type": "string",
            "description": "The email body, as HTML."
          }
        },
        "description": "The rendered certificate email, answered by `POST /api/v1/branding/email/preview`."
      },
      "EventAnalytics": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string",
            "format": "date-time"
          },
          "views": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "shares": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "downloads": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          },
          "verifications": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          }
        }
      },
      "EventDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique identifier of the event.",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "Event name."
          },
          "description": {
            "type": [
              "null",
              "string"
            ],
            "description": "Event description."
          },
          "eventDate": {
            "type": "string",
            "description": "Date and time when the event occurred or will occur.",
            "format": "date-time"
          },
          "location": {
            "type": [
              "null",
              "string"
            ],
            "description": "Event location."
          },
          "templateId": {
            "type": [
              "null",
              "string"
            ],
            "description": "Template ID used for this event's certificates.",
            "format": "uuid"
          },
          "templateName": {
            "type": [
              "null",
              "string"
            ]
          },
          "isActive": {
            "type": "boolean",
            "description": "Value indicating whether this event is active."
          },
          "certificateValidityMonths": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "How many months certificates issued for this event stay valid, counted from their issue date. Null means they never expire. Years are sent as months (years × 12).",
            "format": "int32"
          },
          "hideFromSearch": {
            "type": "boolean",
            "description": "Whether the account owner hid the public verify pages of this event's credentials from search engines. Changed only through `PUT api/v1/events/{id}/search-visibility`, never by a general event edit."
          },
          "createdAt": {
            "type": "string",
            "description": "Date and time when the event was created.",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "description": "Date and time when the event was last updated.",
            "format": "date-time"
          },
          "attendeeCount": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Number of attendees registered for this event. This is a computed statistic loaded separately.",
            "format": "int32"
          },
          "certificateCount": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Number of certificates issued for this event. This is a computed statistic loaded separately.",
            "format": "int32"
          }
        }
      },
      "ExistingCertificateRef": {
        "required": [
          "id",
          "certificateNumber",
          "verificationCode"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The certificate id (also in the response's Location header).",
            "format": "uuid"
          },
          "certificateNumber": {
            "type": "string",
            "description": "Its human-readable number."
          },
          "verificationCode": {
            "type": [
              "null",
              "string"
            ],
            "description": "Its credential ID, when it has one."
          }
        },
        "description": "The certificate an `ALREADY_ISSUED` conflict refers to."
      },
      "GenerateCertificateDto": {
        "type": "object",
        "properties": {
          "templateId": {
            "type": "string",
            "description": "Template ID to use for rendering the certificate. The template must exist and be owned by the authenticated user.",
            "format": "uuid"
          },
          "eventId": {
            "type": "string",
            "description": "Event ID this certificate is for. The event must exist and be owned by the authenticated user.",
            "format": "uuid"
          },
          "attendeeId": {
            "type": "string",
            "description": "Attendee ID this certificate is issued to. The attendee must exist, be owned by the authenticated user, and must belong to the specified event.",
            "format": "uuid"
          },
          "issuerName": {
            "type": [
              "null",
              "string"
            ],
            "description": "Optional issuer name override. If provided, overrides the default issuer name from settings or template. Maps to {{issuerName}} placeholder in templates."
          },
          "issuerTitle": {
            "type": [
              "null",
              "string"
            ],
            "description": "Optional issuer title override. If provided, overrides the default issuer title. Example: \"Training Director\", \"CEO\", \"Program Manager\" Maps to {{issuerTitle}} placeholder in templates."
          },
          "organizationName": {
            "type": [
              "null",
              "string"
            ],
            "description": "Optional organization name override. If provided, overrides the default organization name. Maps to {{organizationName}} placeholder in templates."
          },
          "customFields": {
            "examples": [
              null
            ],
            "type": [
              "null",
              "object"
            ],
            "additionalProperties": {
              "type": "string"
            },
            "description": "Optional custom fields for template placeholders. These key-value pairs are merged into the template data and can be referenced as {{customFields.key}} in templates."
          },
          "language": {
            "type": [
              "null",
              "string"
            ],
            "description": "Certificate language for library templates (\"en\"/\"es\"); null uses the template's default."
          },
          "expirationDate": {
            "type": [
              "null",
              "string"
            ],
            "description": "Optional expiry for this one certificate, overriding the event's `CertificateValidityMonths` default. Null means \"use the default\" (which may itself be \"never expires\"). Must be later than the issue time.",
            "format": "date-time"
          },
          "sendEmail": {
            "type": [
              "null",
              "boolean"
            ],
            "description": "Whether to email the recipient their certificate once it is ready. Null (left out of the request) uses the organizer's \"Send automatically on issue\" setting in Settings › Branding › Emails, which is on by default; false holds the email back (for example, until a ceremony) and it can still be sent later with the Send action."
          }
        }
      },
      "IFormFile": {
        "type": "string",
        "format": "binary"
      },
      "IssueBadgeDto": {
        "type": "object",
        "properties": {
          "attendeeIds": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "uuid"
            }
          },
          "eventId": {
            "type": [
              "null",
              "string"
            ],
            "format": "uuid"
          },
          "evidenceUrl": {
            "maxLength": 500,
            "minLength": 0,
            "type": [
              "null",
              "string"
            ],
            "description": "Optional evidence link, at most 500 characters (the BadgeAssertions column, review H7)."
          }
        }
      },
      "JsonElement": { },
      "LoginRequest": {
        "required": [
          "email",
          "password"
        ],
        "type": "object",
        "properties": {
          "email": {
            "type": "string"
          },
          "password": {
            "type": "string"
          },
          "twoFactorCode": {
            "type": [
              "null",
              "string"
            ]
          },
          "recoveryCode": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "MoveRegistrationQuestionDto": {
        "type": "object",
        "properties": {
          "direction": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "-1 moves the question up one position, +1 moves it down one position.",
            "format": "int32"
          }
        },
        "description": "Body of the move question endpoint."
      },
      "PageOrientation": {
        "type": "integer"
      },
      "PaginationMeta": {
        "required": [
          "page",
          "pageSize",
          "total"
        ],
        "type": "object",
        "properties": {
          "page": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The 1-based page number returned.",
            "format": "int32"
          },
          "pageSize": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The number of items requested per page.",
            "format": "int32"
          },
          "total": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "The total number of items across all pages.",
            "format": "int32"
          }
        },
        "description": "Pagination metadata attached to a paginated ApiResponse&lt;T&gt;."
      },
      "RefreshRequest": {
        "required": [
          "refreshToken"
        ],
        "type": "object",
        "properties": {
          "refreshToken": {
            "type": "string"
          }
        }
      },
      "RegistrationClosedReason": {
        "type": "integer"
      },
      "RegistrationQuestionDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Question id (used in the organizer endpoints; never sent to the public form).",
            "format": "uuid"
          },
          "key": {
            "type": "string",
            "description": "Stable key (`q1`, `q2`, ...) that answers are stored under."
          },
          "type": {
            "description": "The kind of answer.",
            "$ref": "#/components/schemas/RegistrationQuestionType"
          },
          "labelEn": {
            "type": "string",
            "description": "English label."
          },
          "labelEs": {
            "type": [
              "null",
              "string"
            ],
            "description": "Spanish label, if set."
          },
          "required": {
            "type": "boolean",
            "description": "Whether the visitor must answer."
          },
          "options": {
            "type": "array",
            "items": {
              "description": "One option of a choice question, as the organizer edits it.",
              "$ref": "#/components/schemas/RegistrationQuestionOptionDto"
            },
            "description": "Options of a Dropdown or Multiple choice question (empty otherwise)."
          },
          "sortOrder": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Position on the form.",
            "format": "int32"
          },
          "isHidden": {
            "type": "boolean",
            "description": "Whether the question is hidden from the public form."
          },
          "hasAnswers": {
            "type": "boolean",
            "description": "Whether at least one attendee answered it (then its type is locked and it cannot be deleted)."
          }
        },
        "description": "The organizer's view of one custom registration question."
      },
      "RegistrationQuestionOptionDto": {
        "type": "object",
        "properties": {
          "value": {
            "type": [
              "null",
              "string"
            ],
            "description": "The option's stable value. Leave empty for a NEW option (the server generates one). For an existing option send back the value you were given: it must already exist on the question."
          },
          "en": {
            "type": "string",
            "description": "English label (required, up to 100 characters)."
          },
          "es": {
            "type": [
              "null",
              "string"
            ],
            "description": "Spanish label (optional, up to 100 characters); empty falls back to English."
          }
        },
        "description": "One option of a choice question, as the organizer edits it."
      },
      "RegistrationQuestionType": {
        "enum": [
          "Dropdown",
          "MultiChoice",
          "ShortText",
          "LongText",
          "YesNo"
        ]
      },
      "RegistrationSettingsDto": {
        "type": "object",
        "properties": {
          "eventId": {
            "type": "string",
            "description": "The event these settings belong to.",
            "format": "uuid"
          },
          "enabled": {
            "type": "boolean",
            "description": "Whether the form accepts registrations (before the other closing rules apply)."
          },
          "code": {
            "type": [
              "null",
              "string"
            ],
            "description": "The public form code, or null if registration was never enabled."
          },
          "publicUrl": {
            "type": [
              "null",
              "string"
            ]
          },
          "embedHtml": {
            "type": [
              "null",
              "string"
            ],
            "description": "A ready-to-paste `&lt;iframe&gt;` snippet embedding the form, or null without a code."
          },
          "limit": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Maximum number of attendees accepted through the form; null means no limit.",
            "format": "int32"
          },
          "closesAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "UTC instant after which the form closes; null means it closes when the event day ends.",
            "format": "date-time"
          },
          "registrationCount": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Number of attendees the event has so far. This counts every attendee of the event (added by hand, imported or registered through the form): attendees carry no source marker, and the limit is about seats, which every attendee occupies.",
            "format": "int32"
          },
          "isOpen": {
            "type": "boolean",
            "description": "Whether a visitor opening the link right now would see the form."
          },
          "closedReason": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "Why the form is closed, or null when it is open.",
                "$ref": "#/components/schemas/RegistrationClosedReason"
              }
            ]
          }
        },
        "description": "The organizer's view of an event's public sign-up form: the switch, the link, the optional limit and close date, and how many attendees the event has so far."
      },
      "RevokeRequest": {
        "required": [
          "reason"
        ],
        "type": "object",
        "properties": {
          "reason": {
            "type": "string",
            "description": "The reason for revoking the certificate, at most 500 characters (the RevocationReason column). Checked here by model validation, before the certificate is touched: an over-long reason used to reach SQL Server, fail with a 500 and leave the certificate valid (review H7)."
          }
        },
        "description": "Request body for certificate revocation."
      },
      "SaveRegistrationQuestionDto": {
        "type": "object",
        "properties": {
          "type": {
            "description": "The kind of answer. Cannot change once the question has answers.",
            "$ref": "#/components/schemas/RegistrationQuestionType"
          },
          "labelEn": {
            "type": "string",
            "description": "English label (required, up to 200 characters)."
          },
          "labelEs": {
            "type": [
              "null",
              "string"
            ],
            "description": "Spanish label (optional, up to 200 characters)."
          },
          "required": {
            "type": "boolean",
            "description": "Whether the visitor must answer (for Yes/No: the box must be ticked)."
          },
          "options": {
            "type": "array",
            "items": {
              "description": "One option of a choice question, as the organizer edits it.",
              "$ref": "#/components/schemas/RegistrationQuestionOptionDto"
            },
            "description": "The options of a Dropdown or Multiple choice question, in display order (1 to 25; ignored for the other types). The values of options that already have answers must be sent back."
          }
        },
        "description": "Body of the create and update question endpoints."
      },
      "SetRegistrationQuestionHiddenDto": {
        "type": "object",
        "properties": {
          "hidden": {
            "type": "boolean",
            "description": "True to hide the question from the public form, false to show it again."
          }
        },
        "description": "Body of the hide / un-hide question endpoint."
      },
      "SetSearchVisibilityRequest": {
        "required": [
          "hidden"
        ],
        "type": "object",
        "properties": {
          "hidden": {
            "type": "boolean",
            "description": "True to hide the event's public verify pages from search engines."
          }
        },
        "description": "Body of `PUT api/v1/events/{id}/search-visibility`."
      },
      "SystemTemplatePageDto": {
        "type": "object",
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SystemTemplateSummaryDto"
            },
            "description": "The cards of this page, in design-number order."
          },
          "page": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "1-based page number actually returned (a request past the end is clamped to the last page).",
            "format": "int32"
          },
          "pageSize": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Cards per page used for this response.",
            "format": "int32"
          },
          "totalCount": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Designs matching the filters across all pages.",
            "format": "int32"
          },
          "totalPages": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "format": "int32"
          }
        }
      },
      "SystemTemplateSummaryDto": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "description": "Stable key, e.g. \"61-classic-seal\"."
          },
          "number": {
            "type": "string",
            "description": "Two-digit design number, e.g. \"61\"."
          },
          "name": {
            "type": "string",
            "description": "Design name, e.g. \"Classic Seal\"."
          },
          "category": {
            "$ref": "#/components/schemas/TemplateCategory"
          },
          "orientation": {
            "description": "Designed orientation.",
            "$ref": "#/components/schemas/PageOrientation"
          },
          "background": {
            "description": "Background tone, shown as a pill on the card.",
            "$ref": "#/components/schemas/TemplateBackground"
          },
          "badgeShape": {
            "description": "Outline of the matching badge.",
            "$ref": "#/components/schemas/BadgeShape"
          },
          "isAvailable": {
            "type": "boolean",
            "description": "True once both the certificate and badge markup exist as embedded resources."
          },
          "isEditable": {
            "type": "boolean"
          }
        }
      },
      "TemplateBackground": {
        "type": "integer"
      },
      "TemplateCategory": {
        "type": "integer"
      },
      "TemplateDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique identifier for the template.",
            "format": "uuid"
          },
          "userId": {
            "type": "string",
            "description": "User ID who owns this template."
          },
          "name": {
            "type": "string",
            "description": "Template name."
          },
          "description": {
            "type": [
              "null",
              "string"
            ],
            "description": "Template description."
          },
          "htmlTemplate": {
            "type": "string",
            "description": "HTML template content with merge fields."
          },
          "cssStyles": {
            "type": "string",
            "description": "CSS styles for the template."
          },
          "pageSize": {
            "type": "string",
            "description": "Page size (e.g., \"A4\", \"Letter\", \"A5\")."
          },
          "orientation": {
            "type": "string",
            "description": "Page orientation (e.g., \"Portrait\", \"Landscape\")."
          },
          "backgroundImageUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "URL to the background image for the certificate."
          },
          "createdAt": {
            "type": "string",
            "description": "Date and time when the template was created.",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "description": "Date and time when the template was last updated.",
            "format": "date-time"
          },
          "isActive": {
            "type": "boolean",
            "description": "Value indicating whether this template is active."
          },
          "usageCount": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Number of certificates using this template.",
            "format": "int32"
          },
          "lastUsedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "Date and time when this template was last used to generate a certificate.",
            "format": "date-time"
          },
          "category": {
            "type": [
              "null",
              "string"
            ],
            "description": "Library category name (a TemplateCategory value) for library copies; null for custom templates."
          },
          "systemTemplateSlug": {
            "type": [
              "null",
              "string"
            ],
            "description": "Slug of the library design this template was copied from (\"01-meridian\"); null for custom templates."
          },
          "badgeShape": {
            "type": [
              "null",
              "string"
            ],
            "description": "Badge outline (a BadgeShape name) for library copies."
          },
          "language": {
            "type": [
              "null",
              "string"
            ],
            "description": "Default certificate language (\"en\"/\"es\") for library copies."
          },
          "editorKind": {
            "type": "string",
            "description": "\"LegacyHtml\", \"LegacyLibrary\" or \"Visual\"."
          },
          "designId": {
            "type": [
              "null",
              "string"
            ],
            "description": "The design a Visual template renders.",
            "format": "uuid"
          },
          "design": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "For Visual templates (single-template reads): the published design, else the draft.",
                "$ref": "#/components/schemas/JsonElement"
              }
            ]
          }
        }
      },
      "TemplatePreviewResult": {
        "required": [
          "template",
          "placeholders"
        ],
        "type": "object",
        "properties": {
          "template": {
            "description": "The template.",
            "$ref": "#/components/schemas/TemplateDto"
          },
          "placeholders": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "The merge fields (such as `{{recipient_name}}`) the template expects."
          }
        },
        "description": "The answer of `POST /api/v1/templates/{id}/preview`: the template and the merge fields it uses."
      },
      "TestEmailResult": {
        "required": [
          "sentTo"
        ],
        "type": "object",
        "properties": {
          "sentTo": {
            "type": "string",
            "description": "The address the test email was sent to (your own)."
          }
        },
        "description": "The answer of `POST /api/v1/branding/email/test`."
      },
      "TokenResponse": {
        "required": [
          "accessToken",
          "refreshToken",
          "expiresAt"
        ],
        "type": "object",
        "properties": {
          "accessToken": {
            "type": "string"
          },
          "refreshToken": {
            "type": "string"
          },
          "expiresAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "UpdateAttendeeDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique identifier of the attendee to update.",
            "format": "uuid"
          },
          "eventId": {
            "type": "string",
            "description": "Event ID this attendee is associated with. Note: Changing the event association may be restricted by business rules.",
            "format": "uuid"
          },
          "firstName": {
            "type": "string",
            "description": "Attendee's first name."
          },
          "lastName": {
            "type": "string",
            "description": "Attendee's last name."
          },
          "email": {
            "type": "string",
            "description": "Attendee's email address."
          },
          "organization": {
            "type": [
              "null",
              "string"
            ],
            "description": "Attendee's organization or company name."
          },
          "additionalInfo": {
            "type": [
              "null",
              "string"
            ],
            "description": "Additional information about the attendee in JSON format. Used for custom fields and merge data."
          }
        }
      },
      "UpdateBrandingRequest": {
        "required": [
          "logoUrl",
          "primaryColor",
          "secondaryColor",
          "customDomain",
          "customFromEmail",
          "customFromName",
          "customFooterText",
          "hidePoweredBy"
        ],
        "type": "object",
        "properties": {
          "logoUrl": {
            "type": [
              "null",
              "string"
            ]
          },
          "primaryColor": {
            "type": [
              "null",
              "string"
            ]
          },
          "secondaryColor": {
            "type": [
              "null",
              "string"
            ]
          },
          "customDomain": {
            "type": [
              "null",
              "string"
            ]
          },
          "customFromEmail": {
            "type": [
              "null",
              "string"
            ]
          },
          "customFromName": {
            "type": [
              "null",
              "string"
            ]
          },
          "customFooterText": {
            "type": [
              "null",
              "string"
            ]
          },
          "hidePoweredBy": {
            "type": "boolean"
          },
          "issuerName": {
            "type": [
              "null",
              "string"
            ]
          },
          "signer1Name": {
            "type": [
              "null",
              "string"
            ]
          },
          "signer1Title": {
            "type": [
              "null",
              "string"
            ]
          },
          "signer2Name": {
            "type": [
              "null",
              "string"
            ]
          },
          "signer2Title": {
            "type": [
              "null",
              "string"
            ]
          },
          "securityLineEnabled": {
            "type": [
              "null",
              "boolean"
            ]
          },
          "hideFromSearch": {
            "type": [
              "null",
              "boolean"
            ]
          }
        },
        "description": "Body of `PUT api/v1/branding`. The limits match the BrandingSettings columns: without them an over-long value reached SQL Server and came back as a 500 (review H7); model validation now answers 400 naming the field."
      },
      "UpdateEventDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique identifier of the event to update.",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "Event name."
          },
          "description": {
            "type": [
              "null",
              "string"
            ],
            "description": "Event description."
          },
          "eventDate": {
            "type": "string",
            "description": "Date and time when the event occurred or will occur.",
            "format": "date-time"
          },
          "location": {
            "type": [
              "null",
              "string"
            ],
            "description": "Event location."
          },
          "templateId": {
            "type": [
              "null",
              "string"
            ],
            "description": "Template ID to use for this event's certificates. This is optional; can be set to null to remove template association.",
            "format": "uuid"
          },
          "isActive": {
            "type": "boolean",
            "description": "Value indicating whether this event is active."
          },
          "certificateValidityMonths": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "How many months certificates issued for this event stay valid, counted from their issue date. Null means they never expire. Years are sent as months (years × 12).",
            "format": "int32"
          }
        }
      },
      "UpdateRegistrationSettingsDto": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "Switches the public form on or off. Enabling it the first time generates the code."
          },
          "limit": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "Optional maximum number of attendees (1 to 100,000); null removes the limit.",
            "format": "int32"
          },
          "closesAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "Optional close instant. Treated as UTC (an unspecified kind is assumed to be UTC); null removes the close date.",
            "format": "date-time"
          }
        },
        "description": "Body of `PUT api/v1/events/{id}/registration`."
      },
      "UpdateTemplateDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Unique identifier for the template to update.",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "description": "Template name."
          },
          "description": {
            "type": [
              "null",
              "string"
            ],
            "description": "Template description."
          },
          "htmlTemplate": {
            "type": "string",
            "description": "HTML template content with merge fields."
          },
          "cssStyles": {
            "type": "string",
            "description": "CSS styles for the template."
          },
          "pageSize": {
            "type": "string",
            "description": "Page size (e.g., \"A4\", \"Letter\", \"A5\"). Defaults to \"A4\"."
          },
          "orientation": {
            "type": "string"
          },
          "backgroundImageUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "URL to the background image for the certificate."
          },
          "isActive": {
            "type": "boolean",
            "description": "Value indicating whether this template is active."
          },
          "design": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "For Visual templates: a design document to publish as the next version. Refused for legacy templates.",
                "$ref": "#/components/schemas/JsonElement"
              }
            ]
          }
        }
      },
      "UpdateWebhookRequest": {
        "required": [
          "url",
          "events"
        ],
        "type": "object",
        "properties": {
          "url": {
            "type": "string"
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "UserProfileResponse": {
        "required": [
          "id",
          "email",
          "userName"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "userName": {
            "type": [
              "null",
              "string"
            ]
          }
        }
      },
      "UseSystemTemplateDto": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string",
            "description": "Catalog slug, filled from the route by the controller."
          },
          "name": {
            "type": [
              "null",
              "string"
            ],
            "description": "Name for the new template copy; defaults to the design's own name when blank."
          },
          "pageSize": {
            "type": "string",
            "description": "\"A4\" or \"Letter\"; default \"A4\"."
          },
          "language": {
            "type": "string",
            "description": "\"en\" or \"es\"; default \"en\"."
          }
        },
        "description": "Request body for \"Use this template\" (`POST api/v1/system-templates/{slug}/use`)."
      },
      "VerifiedCertificate": {
        "required": [
          "status",
          "statusDate",
          "revokedAt",
          "recipientName",
          "eventName",
          "eventDate",
          "issueDate",
          "certificateNumber",
          "expirationDate",
          "revocationReason",
          "revocationReasonCode",
          "previewImageUrl",
          "branding",
          "credentialId",
          "credentialTitle",
          "issuerName",
          "verifyUrl",
          "badgeImageUrl",
          "badgeSvgUrl",
          "pdfUrl",
          "linkedInUrl",
          "shareTwitterUrl",
          "shareFacebookUrl",
          "documentFingerprint"
        ],
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "Whether the certificate is good right now: `valid`, `expired` or `revoked`. Revoked wins over expired."
          },
          "statusDate": {
            "type": [
              "null",
              "string"
            ],
            "description": "The date that explains a status that is not valid: when it was revoked, or when it lapsed. Null for a valid certificate.",
            "format": "date-time"
          },
          "revokedAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When it was revoked. Null unless the status is `revoked`.",
            "format": "date-time"
          },
          "recipientName": {
            "type": "string",
            "description": "The recipient's full name."
          },
          "eventName": {
            "type": [
              "null",
              "string"
            ],
            "description": "The event (course, conference) it was issued for."
          },
          "eventDate": {
            "type": [
              "null",
              "string"
            ],
            "description": "The date of that event.",
            "format": "date-time"
          },
          "issueDate": {
            "type": "string",
            "description": "When the certificate was issued.",
            "format": "date-time"
          },
          "certificateNumber": {
            "type": "string",
            "description": "The certificate number, for example CERT-2026-000123."
          },
          "expirationDate": {
            "type": [
              "null",
              "string"
            ],
            "description": "When it stops being valid. Null when it never expires.",
            "format": "date-time"
          },
          "revocationReason": {
            "type": [
              "null",
              "string"
            ],
            "description": "The reason given when it was revoked. Null unless revoked."
          },
          "revocationReasonCode": {
            "type": [
              "null",
              "string"
            ],
            "description": "A stable code for a reason the system wrote itself, so clients can translate it. Null for a reason the issuer typed."
          },
          "previewImageUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "Path of a preview image of the certificate, when one exists."
          },
          "branding": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The issuer's logo and colours, when set.",
                "$ref": "#/components/schemas/VerifiedCertificateBranding"
              }
            ]
          },
          "credentialId": {
            "type": [
              "null",
              "string"
            ],
            "description": "The public credential ID, for example CFD-7X4K-92QM. Null for older certificates."
          },
          "credentialTitle": {
            "type": [
              "null",
              "string"
            ],
            "description": "The title of the credential (the event name)."
          },
          "issuerName": {
            "type": [
              "null",
              "string"
            ],
            "description": "Who issued it."
          },
          "verifyUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "The public page where anyone can check this certificate."
          },
          "badgeImageUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "Path of the badge image (PNG). Null for a revoked certificate."
          },
          "badgeSvgUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "Path of the badge image (SVG). Null for a revoked certificate."
          },
          "pdfUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "Path of the certificate PDF. Null for a revoked certificate."
          },
          "linkedInUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "A ready-made link that adds the certificate to a LinkedIn profile. Null unless the status is valid."
          },
          "shareTwitterUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "Path of a share link for X (Twitter). Null unless the status is valid."
          },
          "shareFacebookUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "Path of a share link for Facebook. Null unless the status is valid."
          },
          "documentFingerprint": {
            "oneOf": [
              {
                "type": "null"
              },
              {
                "description": "The SHA-256 fingerprint of the current PDF, present only when the account records fingerprints.",
                "$ref": "#/components/schemas/VerifiedCertificateFingerprint"
              }
            ]
          }
        },
        "description": "The public record of one certificate, answered by `GET /api/v1/verify/{code}`."
      },
      "VerifiedCertificateBranding": {
        "required": [
          "logoUrl",
          "logoTone",
          "primaryColor",
          "secondaryColor",
          "footerText",
          "hidePoweredBy"
        ],
        "type": "object",
        "properties": {
          "logoUrl": {
            "type": [
              "null",
              "string"
            ],
            "description": "Path of the issuer's logo."
          },
          "logoTone": {
            "type": [
              "null",
              "string"
            ],
            "description": "`light` when the logo is white lettering that needs a dark background, otherwise `dark` or null."
          },
          "primaryColor": {
            "type": [
              "null",
              "string"
            ],
            "description": "The issuer's main colour, as a hex code."
          },
          "secondaryColor": {
            "type": [
              "null",
              "string"
            ],
            "description": "The issuer's second colour, as a hex code."
          },
          "footerText": {
            "type": [
              "null",
              "string"
            ],
            "description": "A custom footer line."
          },
          "hidePoweredBy": {
            "type": "boolean",
            "description": "True when the issuer hides the \"Powered by Certifyd\" line."
          }
        },
        "description": "The issuer's appearance on the public verify page."
      },
      "VerifiedCertificateFingerprint": {
        "required": [
          "algorithm",
          "sha256"
        ],
        "type": "object",
        "properties": {
          "algorithm": {
            "type": "string",
            "description": "Always `SHA-256`."
          },
          "sha256": {
            "type": "string",
            "description": "The 64-character hexadecimal SHA-256 of the PDF file."
          }
        },
        "description": "The fingerprint of a certificate's current PDF."
      },
      "WebhookDeliveryDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Delivery id (also sent as the `X-Certifyd-Delivery` header).",
            "format": "uuid"
          },
          "eventType": {
            "type": "string",
            "description": "Event name."
          },
          "responseCode": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "null",
              "integer",
              "string"
            ],
            "description": "HTTP status of the last attempt, or null if it never got a response.",
            "format": "int32"
          },
          "success": {
            "type": "boolean",
            "description": "True once an attempt got a 2xx."
          },
          "attemptCount": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Attempts made so far.",
            "format": "int32"
          },
          "createdAt": {
            "type": "string",
            "description": "When the delivery was recorded (UTC).",
            "format": "date-time"
          }
        },
        "description": "One row of a subscription's delivery log."
      },
      "WebhookSubscriptionDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Subscription id.",
            "format": "uuid"
          },
          "url": {
            "type": "string",
            "description": "The https endpoint deliveries are POSTed to."
          },
          "events": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Event names this subscription listens for."
          },
          "isActive": {
            "type": "boolean",
            "description": "False once disabled (for example after too many consecutive failures)."
          },
          "lastTriggeredAt": {
            "type": [
              "null",
              "string"
            ],
            "description": "When an event last matched this subscription.",
            "format": "date-time"
          },
          "consecutiveFailureCount": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "type": [
              "integer",
              "string"
            ],
            "description": "Failed attempts since the last success.",
            "format": "int32"
          },
          "createdAt": {
            "type": "string",
            "description": "Creation time (UTC).",
            "format": "date-time"
          }
        },
        "description": "A webhook subscription as returned by list/get. Deliberately has no secret."
      }
    },
    "securitySchemes": {
      "ApiKey": {
        "type": "http",
        "description": "An API key from Settings, then API keys in the app. It starts with `crtfd_live_` and can only do what the scopes it was created with allow. Sent as `Authorization: Bearer crtfd_live_...`.",
        "scheme": "bearer",
        "bearerFormat": "crtfd_live_..."
      },
      "BearerJwt": {
        "type": "http",
        "description": "The `accessToken` returned by `POST /api/v1/auth/login` (or `/auth/refresh`). A signed-in session is needed for operations that API keys cannot do, such as managing API keys, branding and credits.",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      }
    }
  },
  "tags": [
    {
      "name": "Authentication",
      "description": "Sign in with an email and password, refresh a session and read the signed-in profile. Integrations should use an API key instead."
    },
    {
      "name": "Certificates",
      "description": "Issue a certificate for an attendee, then read, download, send, regenerate, revoke or delete it."
    },
    {
      "name": "Bulk issuing",
      "description": "Issue certificates for every attendee of an event, immediately or as a background job you can follow and cancel."
    },
    {
      "name": "Events",
      "description": "The occasions certificates are issued for: a course, a conference, a training session."
    },
    {
      "name": "Registration",
      "description": "The sign-up page of an event and the questions it asks."
    },
    {
      "name": "Attendees",
      "description": "The people who took part. Add them one by one, or import many at once."
    },
    {
      "name": "Templates",
      "description": "Your certificate designs: list, create, change, duplicate and preview."
    },
    {
      "name": "Template library",
      "description": "Ready-made designs from the Certifyd library. Use one to add it to your own templates."
    },
    {
      "name": "Badges",
      "description": "Digital badges and the assertions issued from them."
    },
    {
      "name": "Verification",
      "description": "Public checks: look up a certificate by its credential ID or by the SHA-256 fingerprint of its PDF. No credentials needed."
    },
    {
      "name": "Email",
      "description": "Send certificates by email and see what was delivered."
    },
    {
      "name": "Webhooks",
      "description": "Subscribe to certificate events so your systems hear about them as they happen."
    },
    {
      "name": "Analytics",
      "description": "Dashboard figures and per-event statistics."
    },
    {
      "name": "Credits",
      "description": "How many certificate credits you have used and have left this month."
    },
    {
      "name": "API keys",
      "description": "Create and revoke API keys. Needs a signed-in session: a key cannot manage keys."
    },
    {
      "name": "Branding",
      "description": "Your logo, signatures and email appearance. Needs a signed-in session."
    }
  ],
  "webhooks": {
    "certificate.issued": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "A certificate was issued",
        "description": "Sent when a certificate is generated, one at a time or in bulk. Regenerating a certificate sends it again.\n\nSubscribe an HTTPS endpoint with `POST /api/v1/webhooks` (or in the app under *Settings, then Webhooks*) and\nchoose which events it receives. The endpoint must use HTTPS and resolve to a public address. The signing\nsecret is returned **once**, when the subscription is created.\n\nEvery delivery is a `POST` with a JSON body and these headers:\n\n| Header | Value |\n|---|---|\n| `X-Certifyd-Event` | The event name, for example `certificate.issued`. |\n| `X-Certifyd-Delivery` | The delivery id. It stays the same across retries: use it as your idempotency key. |\n| `X-Certifyd-Timestamp` | Unix time in seconds when this attempt was sent. |\n| `X-Certifyd-Signature` | `sha256=` followed by the hex HMAC-SHA256 of `\"{timestamp}.{raw body}\"`, keyed with your subscription secret. |\n\nVerify the signature on the raw request body, reject timestamps that are more than a few minutes old, and answer\nwith any `2xx` status. A delivery times out after 10 seconds. A failed delivery is tried up to 5 times in total,\nafter 1 minute, 5 minutes, 30 minutes and 2 hours, and a subscription that fails 10 times in a row is switched\noff (turn it back on with `POST /api/v1/webhooks/{id}/enable`).\n\nTo recompute a signature on your side:\n\n```bash\nprintf '%s.%s' \"$TIMESTAMP\" \"$RAW_BODY\" | openssl dgst -sha256 -hmac \"$WEBHOOK_SECRET\"\n```",
        "operationId": "webhook_certificate_issued",
        "parameters": [
          {
            "name": "X-Certifyd-Event",
            "in": "header",
            "description": "The event name.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "certificate.issued"
          },
          {
            "name": "X-Certifyd-Delivery",
            "in": "header",
            "description": "The delivery id. It is the same on every retry of this delivery, so it works as your idempotency key.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "7c0e5b0e-0000-4000-8000-000000000000"
          },
          {
            "name": "X-Certifyd-Timestamp",
            "in": "header",
            "description": "Unix time in seconds when this attempt was sent.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "1790000000"
          },
          {
            "name": "X-Certifyd-Signature",
            "in": "header",
            "description": "`sha256=` followed by the hex HMAC-SHA256 of `{timestamp}.{raw body}`, keyed with your subscription secret.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "sha256=3f1c0000000000000000000000000000000000000000000000000000000000e9"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "eventType": {
                    "type": "string",
                    "description": "The event name, the same as the X-Certifyd-Event header."
                  },
                  "timestamp": {
                    "type": "string",
                    "description": "When the event happened, as an ISO 8601 date and time in UTC.",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "certificateId": {
                        "type": "string",
                        "description": "The certificate id.",
                        "format": "uuid"
                      },
                      "certificateNumber": {
                        "type": "string",
                        "description": "The human-readable certificate number."
                      },
                      "eventId": {
                        "type": "string",
                        "description": "The event it was issued for.",
                        "format": "uuid"
                      },
                      "attendeeId": {
                        "type": "string",
                        "description": "The attendee it was issued to.",
                        "format": "uuid"
                      },
                      "templateId": {
                        "type": "string",
                        "description": "The template it was issued from.",
                        "format": "uuid"
                      },
                      "status": {
                        "type": "string",
                        "description": "The certificate status: Generated, Sent, Viewed, Active, Expired or Revoked."
                      },
                      "issueDate": {
                        "type": "string",
                        "description": "When it was issued.",
                        "format": "date-time"
                      },
                      "verificationCode": {
                        "type": "string",
                        "description": "The public credential ID anyone can verify, for example CFD-7X4K-92QM."
                      },
                      "revokedAt": {
                        "type": [
                          "null",
                          "string"
                        ],
                        "description": "When it was revoked, or null.",
                        "format": "date-time"
                      },
                      "revocationReason": {
                        "type": [
                          "null",
                          "string"
                        ],
                        "description": "Why it was revoked, or null."
                      }
                    }
                  }
                }
              },
              "example": {
                "eventType": "certificate.issued",
                "timestamp": "2026-10-05T15:02:00Z",
                "data": {
                  "certificateId": "3f2c9a52-6b0e-4c1d-9a37-0e5d8f7b1c24",
                  "certificateNumber": "CERT-2026-000123",
                  "eventId": "a1b2c3d4-0000-4000-8000-000000000001",
                  "attendeeId": "a1b2c3d4-0000-4000-8000-000000000002",
                  "templateId": "a1b2c3d4-0000-4000-8000-000000000003",
                  "status": "Generated",
                  "issueDate": "2026-10-05T14:30:00Z",
                  "verificationCode": "CFD-7X4K-92QM",
                  "revokedAt": null,
                  "revocationReason": null
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Any 2xx answer tells Certifyd the delivery arrived. Anything else, or no answer within 10 seconds, is retried."
          }
        }
      }
    },
    "certificate.sent": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "A certificate was emailed",
        "description": "Sent when a certificate is emailed to its recipient.\n\nSubscribe an HTTPS endpoint with `POST /api/v1/webhooks` (or in the app under *Settings, then Webhooks*) and\nchoose which events it receives. The endpoint must use HTTPS and resolve to a public address. The signing\nsecret is returned **once**, when the subscription is created.\n\nEvery delivery is a `POST` with a JSON body and these headers:\n\n| Header | Value |\n|---|---|\n| `X-Certifyd-Event` | The event name, for example `certificate.issued`. |\n| `X-Certifyd-Delivery` | The delivery id. It stays the same across retries: use it as your idempotency key. |\n| `X-Certifyd-Timestamp` | Unix time in seconds when this attempt was sent. |\n| `X-Certifyd-Signature` | `sha256=` followed by the hex HMAC-SHA256 of `\"{timestamp}.{raw body}\"`, keyed with your subscription secret. |\n\nVerify the signature on the raw request body, reject timestamps that are more than a few minutes old, and answer\nwith any `2xx` status. A delivery times out after 10 seconds. A failed delivery is tried up to 5 times in total,\nafter 1 minute, 5 minutes, 30 minutes and 2 hours, and a subscription that fails 10 times in a row is switched\noff (turn it back on with `POST /api/v1/webhooks/{id}/enable`).\n\nTo recompute a signature on your side:\n\n```bash\nprintf '%s.%s' \"$TIMESTAMP\" \"$RAW_BODY\" | openssl dgst -sha256 -hmac \"$WEBHOOK_SECRET\"\n```",
        "operationId": "webhook_certificate_sent",
        "parameters": [
          {
            "name": "X-Certifyd-Event",
            "in": "header",
            "description": "The event name.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "certificate.sent"
          },
          {
            "name": "X-Certifyd-Delivery",
            "in": "header",
            "description": "The delivery id. It is the same on every retry of this delivery, so it works as your idempotency key.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "7c0e5b0e-0000-4000-8000-000000000000"
          },
          {
            "name": "X-Certifyd-Timestamp",
            "in": "header",
            "description": "Unix time in seconds when this attempt was sent.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "1790000000"
          },
          {
            "name": "X-Certifyd-Signature",
            "in": "header",
            "description": "`sha256=` followed by the hex HMAC-SHA256 of `{timestamp}.{raw body}`, keyed with your subscription secret.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "sha256=3f1c0000000000000000000000000000000000000000000000000000000000e9"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "eventType": {
                    "type": "string",
                    "description": "The event name, the same as the X-Certifyd-Event header."
                  },
                  "timestamp": {
                    "type": "string",
                    "description": "When the event happened, as an ISO 8601 date and time in UTC.",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "certificateId": {
                        "type": "string",
                        "description": "The certificate id.",
                        "format": "uuid"
                      },
                      "certificateNumber": {
                        "type": "string",
                        "description": "The human-readable certificate number."
                      },
                      "eventId": {
                        "type": "string",
                        "description": "The event it was issued for.",
                        "format": "uuid"
                      },
                      "attendeeId": {
                        "type": "string",
                        "description": "The attendee it was issued to.",
                        "format": "uuid"
                      },
                      "templateId": {
                        "type": "string",
                        "description": "The template it was issued from.",
                        "format": "uuid"
                      },
                      "status": {
                        "type": "string",
                        "description": "The certificate status: Generated, Sent, Viewed, Active, Expired or Revoked."
                      },
                      "issueDate": {
                        "type": "string",
                        "description": "When it was issued.",
                        "format": "date-time"
                      },
                      "verificationCode": {
                        "type": "string",
                        "description": "The public credential ID anyone can verify, for example CFD-7X4K-92QM."
                      },
                      "revokedAt": {
                        "type": [
                          "null",
                          "string"
                        ],
                        "description": "When it was revoked, or null.",
                        "format": "date-time"
                      },
                      "revocationReason": {
                        "type": [
                          "null",
                          "string"
                        ],
                        "description": "Why it was revoked, or null."
                      }
                    }
                  }
                }
              },
              "example": {
                "eventType": "certificate.sent",
                "timestamp": "2026-10-05T15:02:00Z",
                "data": {
                  "certificateId": "3f2c9a52-6b0e-4c1d-9a37-0e5d8f7b1c24",
                  "certificateNumber": "CERT-2026-000123",
                  "eventId": "a1b2c3d4-0000-4000-8000-000000000001",
                  "attendeeId": "a1b2c3d4-0000-4000-8000-000000000002",
                  "templateId": "a1b2c3d4-0000-4000-8000-000000000003",
                  "status": "Sent",
                  "issueDate": "2026-10-05T14:30:00Z",
                  "verificationCode": "CFD-7X4K-92QM",
                  "revokedAt": null,
                  "revocationReason": null
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Any 2xx answer tells Certifyd the delivery arrived. Anything else, or no answer within 10 seconds, is retried."
          }
        }
      }
    },
    "certificate.viewed": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "The recipient opened the certificate",
        "description": "Sent the first time the recipient opens their portal link.\n\nSubscribe an HTTPS endpoint with `POST /api/v1/webhooks` (or in the app under *Settings, then Webhooks*) and\nchoose which events it receives. The endpoint must use HTTPS and resolve to a public address. The signing\nsecret is returned **once**, when the subscription is created.\n\nEvery delivery is a `POST` with a JSON body and these headers:\n\n| Header | Value |\n|---|---|\n| `X-Certifyd-Event` | The event name, for example `certificate.issued`. |\n| `X-Certifyd-Delivery` | The delivery id. It stays the same across retries: use it as your idempotency key. |\n| `X-Certifyd-Timestamp` | Unix time in seconds when this attempt was sent. |\n| `X-Certifyd-Signature` | `sha256=` followed by the hex HMAC-SHA256 of `\"{timestamp}.{raw body}\"`, keyed with your subscription secret. |\n\nVerify the signature on the raw request body, reject timestamps that are more than a few minutes old, and answer\nwith any `2xx` status. A delivery times out after 10 seconds. A failed delivery is tried up to 5 times in total,\nafter 1 minute, 5 minutes, 30 minutes and 2 hours, and a subscription that fails 10 times in a row is switched\noff (turn it back on with `POST /api/v1/webhooks/{id}/enable`).\n\nTo recompute a signature on your side:\n\n```bash\nprintf '%s.%s' \"$TIMESTAMP\" \"$RAW_BODY\" | openssl dgst -sha256 -hmac \"$WEBHOOK_SECRET\"\n```",
        "operationId": "webhook_certificate_viewed",
        "parameters": [
          {
            "name": "X-Certifyd-Event",
            "in": "header",
            "description": "The event name.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "certificate.viewed"
          },
          {
            "name": "X-Certifyd-Delivery",
            "in": "header",
            "description": "The delivery id. It is the same on every retry of this delivery, so it works as your idempotency key.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "7c0e5b0e-0000-4000-8000-000000000000"
          },
          {
            "name": "X-Certifyd-Timestamp",
            "in": "header",
            "description": "Unix time in seconds when this attempt was sent.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "1790000000"
          },
          {
            "name": "X-Certifyd-Signature",
            "in": "header",
            "description": "`sha256=` followed by the hex HMAC-SHA256 of `{timestamp}.{raw body}`, keyed with your subscription secret.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "sha256=3f1c0000000000000000000000000000000000000000000000000000000000e9"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "eventType": {
                    "type": "string",
                    "description": "The event name, the same as the X-Certifyd-Event header."
                  },
                  "timestamp": {
                    "type": "string",
                    "description": "When the event happened, as an ISO 8601 date and time in UTC.",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "certificateId": {
                        "type": "string",
                        "description": "The certificate id.",
                        "format": "uuid"
                      },
                      "recipientId": {
                        "type": "string",
                        "description": "The recipient the portal link belongs to.",
                        "format": "uuid"
                      },
                      "occurredAt": {
                        "type": "string",
                        "description": "When the recipient opened or downloaded it.",
                        "format": "date-time"
                      },
                      "viewCount": {
                        "type": "integer",
                        "description": "How many times the portal link has been opened."
                      },
                      "downloadCount": {
                        "type": "integer",
                        "description": "How many times the PDF has been downloaded."
                      }
                    }
                  }
                }
              },
              "example": {
                "eventType": "certificate.viewed",
                "timestamp": "2026-10-05T15:02:00Z",
                "data": {
                  "certificateId": "3f2c9a52-6b0e-4c1d-9a37-0e5d8f7b1c24",
                  "recipientId": "a1b2c3d4-0000-4000-8000-000000000004",
                  "occurredAt": "2026-10-05T15:02:00Z",
                  "viewCount": 1,
                  "downloadCount": 0
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Any 2xx answer tells Certifyd the delivery arrived. Anything else, or no answer within 10 seconds, is retried."
          }
        }
      }
    },
    "certificate.downloaded": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "The recipient downloaded the certificate",
        "description": "Sent every time the recipient downloads the certificate PDF from the portal.\n\nSubscribe an HTTPS endpoint with `POST /api/v1/webhooks` (or in the app under *Settings, then Webhooks*) and\nchoose which events it receives. The endpoint must use HTTPS and resolve to a public address. The signing\nsecret is returned **once**, when the subscription is created.\n\nEvery delivery is a `POST` with a JSON body and these headers:\n\n| Header | Value |\n|---|---|\n| `X-Certifyd-Event` | The event name, for example `certificate.issued`. |\n| `X-Certifyd-Delivery` | The delivery id. It stays the same across retries: use it as your idempotency key. |\n| `X-Certifyd-Timestamp` | Unix time in seconds when this attempt was sent. |\n| `X-Certifyd-Signature` | `sha256=` followed by the hex HMAC-SHA256 of `\"{timestamp}.{raw body}\"`, keyed with your subscription secret. |\n\nVerify the signature on the raw request body, reject timestamps that are more than a few minutes old, and answer\nwith any `2xx` status. A delivery times out after 10 seconds. A failed delivery is tried up to 5 times in total,\nafter 1 minute, 5 minutes, 30 minutes and 2 hours, and a subscription that fails 10 times in a row is switched\noff (turn it back on with `POST /api/v1/webhooks/{id}/enable`).\n\nTo recompute a signature on your side:\n\n```bash\nprintf '%s.%s' \"$TIMESTAMP\" \"$RAW_BODY\" | openssl dgst -sha256 -hmac \"$WEBHOOK_SECRET\"\n```",
        "operationId": "webhook_certificate_downloaded",
        "parameters": [
          {
            "name": "X-Certifyd-Event",
            "in": "header",
            "description": "The event name.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "certificate.downloaded"
          },
          {
            "name": "X-Certifyd-Delivery",
            "in": "header",
            "description": "The delivery id. It is the same on every retry of this delivery, so it works as your idempotency key.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "7c0e5b0e-0000-4000-8000-000000000000"
          },
          {
            "name": "X-Certifyd-Timestamp",
            "in": "header",
            "description": "Unix time in seconds when this attempt was sent.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "1790000000"
          },
          {
            "name": "X-Certifyd-Signature",
            "in": "header",
            "description": "`sha256=` followed by the hex HMAC-SHA256 of `{timestamp}.{raw body}`, keyed with your subscription secret.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "sha256=3f1c0000000000000000000000000000000000000000000000000000000000e9"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "eventType": {
                    "type": "string",
                    "description": "The event name, the same as the X-Certifyd-Event header."
                  },
                  "timestamp": {
                    "type": "string",
                    "description": "When the event happened, as an ISO 8601 date and time in UTC.",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "certificateId": {
                        "type": "string",
                        "description": "The certificate id.",
                        "format": "uuid"
                      },
                      "recipientId": {
                        "type": "string",
                        "description": "The recipient the portal link belongs to.",
                        "format": "uuid"
                      },
                      "occurredAt": {
                        "type": "string",
                        "description": "When the recipient opened or downloaded it.",
                        "format": "date-time"
                      },
                      "viewCount": {
                        "type": "integer",
                        "description": "How many times the portal link has been opened."
                      },
                      "downloadCount": {
                        "type": "integer",
                        "description": "How many times the PDF has been downloaded."
                      }
                    }
                  }
                }
              },
              "example": {
                "eventType": "certificate.downloaded",
                "timestamp": "2026-10-05T15:02:00Z",
                "data": {
                  "certificateId": "3f2c9a52-6b0e-4c1d-9a37-0e5d8f7b1c24",
                  "recipientId": "a1b2c3d4-0000-4000-8000-000000000004",
                  "occurredAt": "2026-10-05T15:02:00Z",
                  "viewCount": 1,
                  "downloadCount": 1
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Any 2xx answer tells Certifyd the delivery arrived. Anything else, or no answer within 10 seconds, is retried."
          }
        }
      }
    },
    "certificate.revoked": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "A certificate was revoked",
        "description": "Sent when you revoke a certificate (a deleted, already issued certificate is revoked too).\n\nSubscribe an HTTPS endpoint with `POST /api/v1/webhooks` (or in the app under *Settings, then Webhooks*) and\nchoose which events it receives. The endpoint must use HTTPS and resolve to a public address. The signing\nsecret is returned **once**, when the subscription is created.\n\nEvery delivery is a `POST` with a JSON body and these headers:\n\n| Header | Value |\n|---|---|\n| `X-Certifyd-Event` | The event name, for example `certificate.issued`. |\n| `X-Certifyd-Delivery` | The delivery id. It stays the same across retries: use it as your idempotency key. |\n| `X-Certifyd-Timestamp` | Unix time in seconds when this attempt was sent. |\n| `X-Certifyd-Signature` | `sha256=` followed by the hex HMAC-SHA256 of `\"{timestamp}.{raw body}\"`, keyed with your subscription secret. |\n\nVerify the signature on the raw request body, reject timestamps that are more than a few minutes old, and answer\nwith any `2xx` status. A delivery times out after 10 seconds. A failed delivery is tried up to 5 times in total,\nafter 1 minute, 5 minutes, 30 minutes and 2 hours, and a subscription that fails 10 times in a row is switched\noff (turn it back on with `POST /api/v1/webhooks/{id}/enable`).\n\nTo recompute a signature on your side:\n\n```bash\nprintf '%s.%s' \"$TIMESTAMP\" \"$RAW_BODY\" | openssl dgst -sha256 -hmac \"$WEBHOOK_SECRET\"\n```",
        "operationId": "webhook_certificate_revoked",
        "parameters": [
          {
            "name": "X-Certifyd-Event",
            "in": "header",
            "description": "The event name.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "certificate.revoked"
          },
          {
            "name": "X-Certifyd-Delivery",
            "in": "header",
            "description": "The delivery id. It is the same on every retry of this delivery, so it works as your idempotency key.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "7c0e5b0e-0000-4000-8000-000000000000"
          },
          {
            "name": "X-Certifyd-Timestamp",
            "in": "header",
            "description": "Unix time in seconds when this attempt was sent.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "1790000000"
          },
          {
            "name": "X-Certifyd-Signature",
            "in": "header",
            "description": "`sha256=` followed by the hex HMAC-SHA256 of `{timestamp}.{raw body}`, keyed with your subscription secret.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "sha256=3f1c0000000000000000000000000000000000000000000000000000000000e9"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "eventType": {
                    "type": "string",
                    "description": "The event name, the same as the X-Certifyd-Event header."
                  },
                  "timestamp": {
                    "type": "string",
                    "description": "When the event happened, as an ISO 8601 date and time in UTC.",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "certificateId": {
                        "type": "string",
                        "description": "The certificate id.",
                        "format": "uuid"
                      },
                      "certificateNumber": {
                        "type": "string",
                        "description": "The human-readable certificate number."
                      },
                      "eventId": {
                        "type": "string",
                        "description": "The event it was issued for.",
                        "format": "uuid"
                      },
                      "attendeeId": {
                        "type": "string",
                        "description": "The attendee it was issued to.",
                        "format": "uuid"
                      },
                      "templateId": {
                        "type": "string",
                        "description": "The template it was issued from.",
                        "format": "uuid"
                      },
                      "status": {
                        "type": "string",
                        "description": "The certificate status: Generated, Sent, Viewed, Active, Expired or Revoked."
                      },
                      "issueDate": {
                        "type": "string",
                        "description": "When it was issued.",
                        "format": "date-time"
                      },
                      "verificationCode": {
                        "type": "string",
                        "description": "The public credential ID anyone can verify, for example CFD-7X4K-92QM."
                      },
                      "revokedAt": {
                        "type": [
                          "null",
                          "string"
                        ],
                        "description": "When it was revoked, or null.",
                        "format": "date-time"
                      },
                      "revocationReason": {
                        "type": [
                          "null",
                          "string"
                        ],
                        "description": "Why it was revoked, or null."
                      }
                    }
                  }
                }
              },
              "example": {
                "eventType": "certificate.revoked",
                "timestamp": "2026-10-05T15:02:00Z",
                "data": {
                  "certificateId": "3f2c9a52-6b0e-4c1d-9a37-0e5d8f7b1c24",
                  "certificateNumber": "CERT-2026-000123",
                  "eventId": "a1b2c3d4-0000-4000-8000-000000000001",
                  "attendeeId": "a1b2c3d4-0000-4000-8000-000000000002",
                  "templateId": "a1b2c3d4-0000-4000-8000-000000000003",
                  "status": "Revoked",
                  "issueDate": "2026-10-05T14:30:00Z",
                  "verificationCode": "CFD-7X4K-92QM",
                  "revokedAt": "2026-10-06T09:00:00Z",
                  "revocationReason": "Issued by mistake"
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Any 2xx answer tells Certifyd the delivery arrived. Anything else, or no answer within 10 seconds, is retried."
          }
        }
      }
    },
    "webhook.test": {
      "post": {
        "tags": [
          "Webhooks"
        ],
        "summary": "A test delivery",
        "description": "Sent when you call `POST /api/v1/webhooks/{id}/test` or press the test button in the app, so you can check your endpoint and your signature code.\n\nSubscribe an HTTPS endpoint with `POST /api/v1/webhooks` (or in the app under *Settings, then Webhooks*) and\nchoose which events it receives. The endpoint must use HTTPS and resolve to a public address. The signing\nsecret is returned **once**, when the subscription is created.\n\nEvery delivery is a `POST` with a JSON body and these headers:\n\n| Header | Value |\n|---|---|\n| `X-Certifyd-Event` | The event name, for example `certificate.issued`. |\n| `X-Certifyd-Delivery` | The delivery id. It stays the same across retries: use it as your idempotency key. |\n| `X-Certifyd-Timestamp` | Unix time in seconds when this attempt was sent. |\n| `X-Certifyd-Signature` | `sha256=` followed by the hex HMAC-SHA256 of `\"{timestamp}.{raw body}\"`, keyed with your subscription secret. |\n\nVerify the signature on the raw request body, reject timestamps that are more than a few minutes old, and answer\nwith any `2xx` status. A delivery times out after 10 seconds. A failed delivery is tried up to 5 times in total,\nafter 1 minute, 5 minutes, 30 minutes and 2 hours, and a subscription that fails 10 times in a row is switched\noff (turn it back on with `POST /api/v1/webhooks/{id}/enable`).\n\nTo recompute a signature on your side:\n\n```bash\nprintf '%s.%s' \"$TIMESTAMP\" \"$RAW_BODY\" | openssl dgst -sha256 -hmac \"$WEBHOOK_SECRET\"\n```",
        "operationId": "webhook_webhook_test",
        "parameters": [
          {
            "name": "X-Certifyd-Event",
            "in": "header",
            "description": "The event name.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "webhook.test"
          },
          {
            "name": "X-Certifyd-Delivery",
            "in": "header",
            "description": "The delivery id. It is the same on every retry of this delivery, so it works as your idempotency key.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "7c0e5b0e-0000-4000-8000-000000000000"
          },
          {
            "name": "X-Certifyd-Timestamp",
            "in": "header",
            "description": "Unix time in seconds when this attempt was sent.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "1790000000"
          },
          {
            "name": "X-Certifyd-Signature",
            "in": "header",
            "description": "`sha256=` followed by the hex HMAC-SHA256 of `{timestamp}.{raw body}`, keyed with your subscription secret.",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "sha256=3f1c0000000000000000000000000000000000000000000000000000000000e9"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "eventType": {
                    "type": "string",
                    "description": "The event name, the same as the X-Certifyd-Event header."
                  },
                  "timestamp": {
                    "type": "string",
                    "description": "When the event happened, as an ISO 8601 date and time in UTC.",
                    "format": "date-time"
                  },
                  "data": {
                    "type": "object",
                    "properties": {
                      "message": {
                        "type": "string",
                        "description": "A short note saying this is a test."
                      }
                    }
                  }
                }
              },
              "example": {
                "eventType": "webhook.test",
                "timestamp": "2026-10-05T15:02:00Z",
                "data": {
                  "message": "This is a test webhook delivery from Certifyd."
                }
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "Any 2xx answer tells Certifyd the delivery arrived. Anything else, or no answer within 10 seconds, is retried."
          }
        }
      }
    }
  }
}