{
  "openapi": "3.0.3",
  "info": {
    "title": "Trankets API",
    "version": "1.0.0",
    "description": "The Trankets External API gives programmatic access to your growth platform data - events, attendees, products, purchases, and waitlists.\n\n**Authentication**: All requests must include your API key in the `X-API-Key` header. Never pass the key in the URL. Keys are created in your Trankets dashboard under **Settings → API Keys**.\n\n**Scopes**: Keys have either `read` or `read_write` scope. Endpoints that create, update, or delete resources require a `read_write` key; a `read`-only key receives `403 Forbidden` on those routes.\n\n**Rate limit**: 100 requests per minute per API key. Exceeding this returns `429 Too Many Requests`.",
    "contact": {
      "url": "https://trankets.com/support"
    }
  },
  "servers": [
    {
      "url": "https://service.trankets.com/api/v1/external"
    }
  ],
  "security": [
    {
      "ApiKeyAuth": []
    }
  ],
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-Key"
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          }
        },
        "required": ["error"]
      },
      "Pagination": {
        "type": "object",
        "properties": {
          "page": {
            "type": "integer"
          },
          "limit": {
            "type": "integer"
          },
          "total": {
            "type": "integer"
          },
          "pages": {
            "type": "integer"
          }
        },
        "required": ["page", "limit", "total", "pages"]
      },
      "EventCategory": {
        "type": "object",
        "description": "System-level event category (e.g. Music, Tech, Sports).",
        "properties": {
          "id": { "type": "integer" },
          "name": { "type": "string" },
          "slug": { "type": "string" },
          "icon": { "type": "string" },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" }
        }
      },
      "EventTag": {
        "type": "object",
        "description": "System-level event tag.",
        "properties": {
          "id": { "type": "integer" },
          "name": { "type": "string" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "ExternalEvent": {
        "type": "object",
        "description": "Filtered event shape returned by the external API. Omits email templates, internal stats, and private config.",
        "properties": {
          "id": { "type": "integer" },
          "user_id": { "type": "integer" },
          "name": { "type": "string" },
          "description": { "type": "string" },
          "location": { "type": "string" },
          "country": { "type": "string" },
          "state": { "type": "string" },
          "city": { "type": "string" },
          "address": { "type": "string" },
          "event_category_id": { "type": "integer", "nullable": true },
          "event_category": {
            "$ref": "#/components/schemas/EventCategory",
            "nullable": true
          },
          "tags": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/EventTag" }
          },
          "start_date": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "end_date": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "theme_color": { "type": "string" },
          "is_active": { "type": "boolean" },
          "total_attendees": { "type": "integer" },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" }
        }
      },
      "AttendeeCategory": {
        "type": "object",
        "description": "Slim attendee category shape (event-scoped).",
        "properties": {
          "id": { "type": "integer" },
          "name": { "type": "string" }
        }
      },
      "AttendeeTag": {
        "type": "object",
        "description": "Lightweight tag on an attendee.",
        "properties": {
          "id": { "type": "integer" },
          "name": { "type": "string" },
          "color": {
            "type": "string",
            "description": "Hex colour string, e.g. #64748b"
          }
        }
      },
      "ExternalTicket": {
        "type": "object",
        "description": "Slim ticket shape attached to an attendee.",
        "properties": {
          "id": { "type": "integer" },
          "name": { "type": "string" },
          "price": { "type": "number", "format": "float" },
          "currency": { "type": "string" }
        }
      },
      "ExternalAttendee": {
        "type": "object",
        "description": "Attendee shape returned by the external API. `code` and `qr_code` are intentionally excluded to protect check-in monetization - those fields are only available through the internal API and verified check-in flow.",
        "properties": {
          "id": { "type": "integer" },
          "event_id": { "type": "integer" },
          "category_id": { "type": "integer", "nullable": true },
          "first_name": { "type": "string" },
          "last_name": { "type": "string" },
          "email": { "type": "string", "format": "email" },
          "phone_number": { "type": "string" },
          "invite_sent": { "type": "boolean" },
          "invite_sent_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "attendance_status": {
            "type": "string",
            "enum": ["pending", "confirmed", "declined", "attended"],
            "description": "Current attendance status."
          },
          "confirmed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "verified_count": {
            "type": "integer",
            "description": "How many times this attendee has been verified at the door."
          },
          "last_verified_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "category": {
            "$ref": "#/components/schemas/AttendeeCategory",
            "nullable": true
          },
          "tags": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/AttendeeTag" }
          },
          "ticket_id": { "type": "integer", "nullable": true },
          "ticket": {
            "$ref": "#/components/schemas/ExternalTicket",
            "nullable": true
          },
          "payment_status": { "type": "string" },
          "table_number": { "type": "string", "nullable": true },
          "seat_number": { "type": "string", "nullable": true },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" }
        }
      },
      "CreateAttendeeRequest": {
        "type": "object",
        "required": ["event_id", "first_name", "last_name", "email"],
        "properties": {
          "event_id": {
            "type": "integer",
            "description": "ID of the event to add the attendee to."
          },
          "category_id": { "type": "integer", "nullable": true },
          "first_name": { "type": "string" },
          "last_name": { "type": "string" },
          "email": { "type": "string", "format": "email" },
          "phone_number": { "type": "string" },
          "tag_ids": { "type": "array", "items": { "type": "integer" } },
          "ticket_id": { "type": "integer", "nullable": true },
          "send_confirmation_email": {
            "type": "boolean",
            "description": "When true, a confirmation email is sent asynchronously to the attendee."
          },
          "form_data": {
            "type": "object",
            "additionalProperties": true,
            "description": "Arbitrary custom form field key-value pairs."
          }
        }
      },
      "UpdateAttendeeRequest": {
        "type": "object",
        "properties": {
          "category_id": { "type": "integer", "nullable": true },
          "first_name": { "type": "string" },
          "last_name": { "type": "string" },
          "email": { "type": "string", "format": "email" },
          "phone_number": { "type": "string" },
          "tag_ids": { "type": "array", "items": { "type": "integer" } },
          "attendance_status": { "type": "string" },
          "table_number": { "type": "string", "nullable": true },
          "seat_number": { "type": "string", "nullable": true }
        }
      },
      "EventScopedCategory": {
        "type": "object",
        "description": "Attendee-grouping category scoped to a specific event.",
        "properties": {
          "id": { "type": "integer" },
          "user_id": { "type": "integer" },
          "event_id": { "type": "integer" },
          "name": { "type": "string" },
          "description": { "type": "string" },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" }
        }
      },
      "EventScopedTag": {
        "type": "object",
        "description": "Attendee tag scoped to a specific event.",
        "properties": {
          "id": { "type": "integer" },
          "user_id": { "type": "integer" },
          "event_id": { "type": "integer" },
          "name": { "type": "string" },
          "color": { "type": "string" },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" }
        }
      },
      "ProductCategoryDTO": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "name": { "type": "string" },
          "slug": { "type": "string" }
        }
      },
      "ProductTagDTO": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "name": { "type": "string" }
        }
      },
      "ExternalFile": {
        "type": "object",
        "description": "Metadata for a file attached to a product. Download URLs are not included.",
        "properties": {
          "id": { "type": "integer" },
          "file_name": { "type": "string" },
          "file_size": {
            "type": "integer",
            "format": "int64",
            "description": "Size in bytes."
          },
          "file_type": { "type": "string" },
          "display_order": { "type": "integer" }
        }
      },
      "ExternalProduct": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "uid": { "type": "string" },
          "name": { "type": "string" },
          "short_description": { "type": "string" },
          "description": { "type": "string" },
          "price": { "type": "number", "format": "float" },
          "compare_at_price": {
            "type": "number",
            "format": "float",
            "nullable": true
          },
          "currency": { "type": "string" },
          "category_id": { "type": "integer" },
          "category": {
            "$ref": "#/components/schemas/ProductCategoryDTO",
            "nullable": true
          },
          "tags": {
            "type": "array",
            "items": { "$ref": "#/components/schemas/ProductTagDTO" }
          },
          "cover_image_url": { "type": "string" },
          "thumbnail_url": { "type": "string" },
          "is_active": { "type": "boolean" },
          "is_listed": { "type": "boolean" },
          "total_sales": { "type": "integer" },
          "file_count": { "type": "integer" },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "ISO 8601 string (Go format 2006-01-02T15:04:05Z)."
          },
          "updated_at": { "type": "string", "format": "date-time" }
        }
      },
      "ExternalPurchase": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "product_uid": { "type": "string" },
          "product_name": { "type": "string" },
          "buyer_email": { "type": "string", "format": "email" },
          "buyer_name": { "type": "string" },
          "amount": { "type": "number", "format": "float" },
          "currency": { "type": "string" },
          "status": {
            "type": "string",
            "enum": ["pending", "completed", "refunded"]
          },
          "download_count": { "type": "integer" },
          "download_limit": { "type": "integer", "nullable": true },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "description": "Omitted when not yet completed."
          },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "ProductStats": {
        "type": "object",
        "properties": {
          "total_products": { "type": "integer" },
          "active_products": { "type": "integer" },
          "total_sales": { "type": "integer" },
          "total_revenue": {
            "type": "number",
            "format": "float",
            "description": "Sum of seller_amount for completed purchases."
          },
          "completed_purchases": { "type": "integer" },
          "pending_purchases": { "type": "integer" }
        }
      },
      "ExternalWaitlist": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "slug": { "type": "string" },
          "name": {
            "type": "string",
            "description": "Internal name for reference."
          },
          "title": { "type": "string", "description": "Public-facing title." },
          "description": {
            "type": "string",
            "description": "Rich text description."
          },
          "theme_color": { "type": "string" },
          "background_type": { "type": "string" },
          "background_image_url": { "type": "string" },
          "background_overlay": { "type": "boolean" },
          "form_schema": {
            "description": "Custom form fields configuration. Returns an empty array when no custom schema is set.",
            "oneOf": [{ "type": "array", "items": {} }, { "type": "object" }]
          },
          "status": {
            "type": "string",
            "enum": ["active", "paused", "closed"]
          },
          "is_public": { "type": "boolean" },
          "total_signups": { "type": "integer" },
          "public_url": { "type": "string", "format": "uri" },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" }
        }
      },
      "ExternalRegistration": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "email": { "type": "string", "format": "email" },
          "first_name": { "type": "string" },
          "last_name": { "type": "string" },
          "source": {
            "type": "string",
            "description": "Always `waitlist:<slug>` for registrations created via this endpoint."
          },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "CreateRegistrationRequest": {
        "type": "object",
        "required": ["email", "first_name", "last_name"],
        "properties": {
          "email": { "type": "string", "format": "email" },
          "first_name": { "type": "string" },
          "last_name": { "type": "string" },
          "phone_number": { "type": "string" },
          "form_data": {
            "type": "object",
            "additionalProperties": true,
            "description": "Responses to custom form fields defined in the waitlist's form_schema."
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid API key.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      },
      "Forbidden": {
        "description": "API key exists but lacks permission - either no API access, `read`-only key on a write endpoint, or suspended account.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      },
      "NotFound": {
        "description": "Resource not found.",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      },
      "TooManyRequests": {
        "description": "Rate limit exceeded (100 req/min per key).",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/Error" }
          }
        }
      }
    }
  },
  "paths": {
    "/events": {
      "get": {
        "tags": ["Events"],
        "summary": "List events",
        "description": "Returns a paginated list of events belonging to the authenticated API key owner.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": { "type": "integer", "default": 1 }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "default": 50 }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of events.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "events": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/ExternalEvent" }
                    },
                    "pagination": { "$ref": "#/components/schemas/Pagination" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/events/{id}": {
      "get": {
        "tags": ["Events"],
        "summary": "Get event by ID",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "integer" }
          }
        ],
        "responses": {
          "200": {
            "description": "Single event.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "event": { "$ref": "#/components/schemas/ExternalEvent" }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid event ID.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/categories": {
      "get": {
        "tags": ["Events"],
        "summary": "List attendee categories for an event",
        "description": "Returns all attendee-grouping categories scoped to the given event. `event_id` is required and must be an event owned by the API key owner.",
        "parameters": [
          {
            "name": "event_id",
            "in": "query",
            "required": true,
            "description": "Numeric ID or UID of the event.",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Array of categories (bare array - no envelope).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/EventScopedCategory"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid event ID.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/tags": {
      "get": {
        "tags": ["Events"],
        "summary": "List attendee tags for an event",
        "description": "Returns all attendee tags scoped to the given event. `event_id` is required.",
        "parameters": [
          {
            "name": "event_id",
            "in": "query",
            "required": true,
            "description": "Numeric ID or UID of the event.",
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Array of tags (bare array - no envelope).",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": { "$ref": "#/components/schemas/EventScopedTag" }
                }
              }
            }
          },
          "400": {
            "description": "Invalid event ID.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/system/categories": {
      "get": {
        "tags": ["Metadata"],
        "summary": "List system event categories",
        "description": "Returns the global list of system-level event categories (Music, Tech, Sports, etc.).",
        "responses": {
          "200": {
            "description": "System categories wrapped in a `data` key.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/EventCategory" }
                    }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/system/tags": {
      "get": {
        "tags": ["Metadata"],
        "summary": "List system event tags",
        "description": "Returns the global list of system-level event tags.",
        "responses": {
          "200": {
            "description": "System tags wrapped in a `data` key.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/EventTag" }
                    }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/attendees": {
      "get": {
        "tags": ["Attendees"],
        "summary": "List attendees for an event",
        "description": "Returns a paginated, filterable list of attendees for a given event. `event_id` is required.",
        "parameters": [
          {
            "name": "event_id",
            "in": "query",
            "required": true,
            "description": "Numeric ID or UID of the event.",
            "schema": { "type": "string" }
          },
          {
            "name": "page",
            "in": "query",
            "schema": { "type": "integer", "default": 1 }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "default": 50 }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Filter by attendance status (pending, confirmed, declined, attended).",
            "schema": { "type": "string" }
          },
          {
            "name": "verified",
            "in": "query",
            "description": "Filter by verification state: `true` or `false`.",
            "schema": { "type": "string", "enum": ["true", "false"] }
          },
          {
            "name": "search",
            "in": "query",
            "description": "Free-text search across name and email.",
            "schema": { "type": "string" }
          },
          {
            "name": "sort",
            "in": "query",
            "description": "Sort expression, e.g. `created_at asc`.",
            "schema": { "type": "string" }
          },
          {
            "name": "ticket_id",
            "in": "query",
            "description": "Filter to attendees with this ticket ID.",
            "schema": { "type": "integer" }
          },
          {
            "name": "category_id",
            "in": "query",
            "description": "Filter to attendees in this category.",
            "schema": { "type": "integer" }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of attendees.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "attendees": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ExternalAttendee"
                      }
                    },
                    "pagination": { "$ref": "#/components/schemas/Pagination" }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing or invalid event_id.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      },
      "post": {
        "tags": ["Attendees"],
        "summary": "Create attendee",
        "description": "Adds a new attendee to an event. **Requires `read_write` scope** - a read-only key receives `403`. When `send_confirmation_email` is `true`, the email is dispatched asynchronously and the response includes `confirmation_email_sent: true`.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CreateAttendeeRequest" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Attendee created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "attendee": {
                      "$ref": "#/components/schemas/ExternalAttendee"
                    },
                    "confirmation_email_sent": {
                      "type": "boolean",
                      "description": "Only present when `send_confirmation_email` was true in the request."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Validation error.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/attendees/{id}": {
      "get": {
        "tags": ["Attendees"],
        "summary": "Get attendee by ID",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "integer" }
          }
        ],
        "responses": {
          "200": {
            "description": "Single attendee.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "attendee": {
                      "$ref": "#/components/schemas/ExternalAttendee"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid attendee ID.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      },
      "put": {
        "tags": ["Attendees"],
        "summary": "Update attendee",
        "description": "Updates an attendee's fields. **Requires `read_write` scope** - a read-only key receives `403`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "integer" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/UpdateAttendeeRequest" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated attendee.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "attendee": {
                      "$ref": "#/components/schemas/ExternalAttendee"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid ID or validation error.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "500": {
            "description": "Failed to update attendee.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      },
      "delete": {
        "tags": ["Attendees"],
        "summary": "Delete attendee",
        "description": "Permanently deletes an attendee record. **Requires `read_write` scope** - a read-only key receives `403`.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "integer" }
          }
        ],
        "responses": {
          "200": {
            "description": "Attendee deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "message": {
                      "type": "string",
                      "example": "Attendee deleted successfully"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid attendee ID.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "500": {
            "description": "Failed to delete attendee.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/products": {
      "get": {
        "tags": ["Products"],
        "summary": "List products",
        "description": "Returns a paginated list of products belonging to the authenticated API key owner.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": { "type": "integer", "default": 1 }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "default": 50 }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Filter by product status: `active`, `inactive`, or `all` (default `all`).",
            "schema": {
              "type": "string",
              "enum": ["active", "inactive", "all"],
              "default": "all"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated list of products.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "products": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ExternalProduct"
                      }
                    },
                    "pagination": { "$ref": "#/components/schemas/Pagination" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/products/stats": {
      "get": {
        "tags": ["Products"],
        "summary": "Get product stats",
        "description": "Returns aggregated statistics for all products owned by the API key owner.",
        "responses": {
          "200": {
            "description": "Product statistics.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "stats": { "$ref": "#/components/schemas/ProductStats" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/products/categories": {
      "get": {
        "tags": ["Products"],
        "summary": "List product categories",
        "description": "Returns all available product categories (global, not user-scoped).",
        "responses": {
          "200": {
            "description": "Product categories.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "categories": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ProductCategoryDTO"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/products/tags": {
      "get": {
        "tags": ["Products"],
        "summary": "List product tags",
        "description": "Returns all available product tags (global, not user-scoped).",
        "responses": {
          "200": {
            "description": "Product tags.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "tags": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/ProductTagDTO" }
                    }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/products/{uid}": {
      "get": {
        "tags": ["Products"],
        "summary": "Get product by UID",
        "description": "Returns a single product by its UID, along with file metadata in a separate `files` array.",
        "parameters": [
          {
            "name": "uid",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "description": "Product with files.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "product": {
                      "$ref": "#/components/schemas/ExternalProduct"
                    },
                    "files": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/ExternalFile" },
                      "description": "File metadata for the product's attachments. Does not include download URLs."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing product UID.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/products/{uid}/purchases": {
      "get": {
        "tags": ["Purchases"],
        "summary": "List purchases for a product",
        "description": "Returns a paginated list of purchases for the specified product.",
        "parameters": [
          {
            "name": "uid",
            "in": "path",
            "required": true,
            "schema": { "type": "string" }
          },
          {
            "name": "page",
            "in": "query",
            "schema": { "type": "integer", "default": 1 }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "default": 50 }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Filter by purchase status: `pending`, `completed`, `refunded`, or `all` (default `all`).",
            "schema": {
              "type": "string",
              "enum": ["pending", "completed", "refunded", "all"],
              "default": "all"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated purchases.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "purchases": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ExternalPurchase"
                      }
                    },
                    "pagination": { "$ref": "#/components/schemas/Pagination" }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Missing product UID.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/purchases": {
      "get": {
        "tags": ["Purchases"],
        "summary": "List all purchases",
        "description": "Returns a paginated list of all product purchases across all products owned by the API key owner.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": { "type": "integer", "default": 1 }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "default": 50 }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Filter by purchase status: `pending`, `completed`, `refunded`, or `all` (default `all`).",
            "schema": {
              "type": "string",
              "enum": ["pending", "completed", "refunded", "all"],
              "default": "all"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated purchases.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "purchases": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ExternalPurchase"
                      }
                    },
                    "pagination": { "$ref": "#/components/schemas/Pagination" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/waitlists": {
      "get": {
        "tags": ["Waitlists"],
        "summary": "List waitlists",
        "description": "Returns a paginated list of waitlists belonging to the authenticated API key owner.",
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": { "type": "integer", "default": 1 }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "default": 50 }
          },
          {
            "name": "status",
            "in": "query",
            "description": "Filter by waitlist status: `active`, `paused`, or `closed`.",
            "schema": {
              "type": "string",
              "enum": ["active", "paused", "closed"]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated waitlists.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "waitlists": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ExternalWaitlist"
                      }
                    },
                    "pagination": { "$ref": "#/components/schemas/Pagination" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/waitlists/{id}": {
      "get": {
        "tags": ["Waitlists"],
        "summary": "Get waitlist by ID",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "integer" }
          }
        ],
        "responses": {
          "200": {
            "description": "Single waitlist.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "waitlist": {
                      "$ref": "#/components/schemas/ExternalWaitlist"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid waitlist ID.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    },
    "/waitlists/{id}/registrations": {
      "get": {
        "tags": ["Waitlists"],
        "summary": "List registrations for a waitlist",
        "description": "Returns a paginated list of registrations (contacts) for the given waitlist.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "integer" }
          },
          {
            "name": "page",
            "in": "query",
            "schema": { "type": "integer", "default": 1 }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": { "type": "integer", "default": 50 }
          }
        ],
        "responses": {
          "200": {
            "description": "Paginated registrations.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "registrations": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ExternalRegistration"
                      }
                    },
                    "pagination": { "$ref": "#/components/schemas/Pagination" }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid waitlist ID.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      },
      "post": {
        "tags": ["Waitlists"],
        "summary": "Create a waitlist registration",
        "description": "Registers a contact to the given waitlist. **Requires `read_write` scope** - a read-only key receives `403`.\n\nThe waitlist must be `active` and `is_public: true`. Returns `200` (not `201`) with `already_registered: true` when the email is already in the waitlist. Returns `403` if the waitlist signup limit is reached, if the waitlist is not accepting registrations, or if the email has unsubscribed.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": { "type": "integer" }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateRegistrationRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Registration created.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "message": { "type": "string" },
                    "contact": {
                      "$ref": "#/components/schemas/ExternalRegistration"
                    },
                    "waitlist": {
                      "type": "string",
                      "description": "Slug of the waitlist."
                    }
                  }
                }
              }
            }
          },
          "200": {
            "description": "Email already registered - idempotent success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "success": { "type": "boolean" },
                    "message": { "type": "string" },
                    "already_registered": { "type": "boolean" },
                    "contact": {
                      "$ref": "#/components/schemas/ExternalRegistration"
                    },
                    "waitlist": { "type": "string" }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid waitlist ID or validation error.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": {
            "description": "Read-only key / waitlist not accepting registrations / signup limit reached / email unsubscribed.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "error": { "type": "string" },
                    "status": { "type": "string" },
                    "message": { "type": "string" },
                    "limit": { "type": "integer" },
                    "current": { "type": "integer" }
                  }
                }
              }
            }
          },
          "404": { "$ref": "#/components/responses/NotFound" },
          "429": { "$ref": "#/components/responses/TooManyRequests" }
        }
      }
    }
  }
}
