{
  "openapi": "3.1.0",
  "info": {
    "title": "Eventwerk API v1",
    "version": "1.0.0",
    "description": "Öffentliche Programmierschnittstelle von Eventwerk. Alle Zeitpunkte sind UTC und im Format RFC 3339 angegeben. Die IANA-Zeitzone eines Events steht getrennt im Feld timezone. Authentifizierung über einen API-Schlüssel im Kopf Authorization: Bearer ew_...."
  },
  "servers": [
    {
      "url": "/"
    }
  ],
  "security": [
    {
      "apiKey": []
    }
  ],
  "paths": {
    "/api/v1/openapi.json": {
      "get": {
        "summary": "OpenAPI-Beschreibung",
        "description": "Liefert diese Beschreibung ohne Anmeldung.",
        "security": [],
        "responses": {
          "200": {
            "description": "OpenAPI-Dokument",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/me": {
      "get": {
        "summary": "Eigene Organisation und Rolle",
        "description": "Ein API-Schlüssel handelt immer als Eigentümer seiner Organisation.",
        "responses": {
          "200": {
            "description": "Organisation des Schlüssels",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Me"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PlanRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/events": {
      "get": {
        "summary": "Events auflisten",
        "description": "Liefert die Events der Organisation in der Reihenfolge created_at und id. Der Cursor kodiert den letzten Schlüssel, keinen Offset.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          }
        ],
        "responses": {
          "200": {
            "description": "Seite der Eventliste",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventPage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PlanRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "summary": "Event anlegen",
        "description": "Legt ein Event an. Mit dem Kopf Idempotency-Key wird die Antwort gespeichert: derselbe Schlüssel mit demselben Rumpf liefert dieselbe Antwort, ein anderer Rumpf ergibt 409 idempotency_conflict. Ohne Kopf verhält sich der Aufruf wie bisher. Ein über den Schlüssel gesetztes status published veröffentlicht sofort. Über die Oberfläche angelegte Events warten dagegen auf die bestätigte E-Mail-Adresse.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "Undurchsichtiger Schlüssel für eine wiederholbare Anlage, höchstens 255 Zeichen.",
            "schema": {
              "type": "string",
              "maxLength": 255
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EventCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Angelegtes Event",
            "headers": {
              "Location": {
                "description": "Pfad zum angelegten Event",
                "schema": {
                  "type": "string"
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Event"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PlanRequired"
          },
          "409": {
            "description": "Idempotency-Key bereits mit anderem Rumpf verwendet oder noch in Arbeit",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "konflikt": {
                    "value": {
                      "code": "idempotency_conflict",
                      "message": "Der Idempotency-Key wurde bereits mit einem anderen Rumpf verwendet."
                    }
                  }
                }
              }
            }
          },
          "413": {
            "$ref": "#/components/responses/TooLarge"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/events/{eventID}": {
      "parameters": [
        {
          "$ref": "#/components/parameters/EventID"
        }
      ],
      "get": {
        "summary": "Event lesen",
        "responses": {
          "200": {
            "description": "Event",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Event"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PlanRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "patch": {
        "summary": "Event ändern",
        "description": "Mindestens ein Feld muss übergeben werden. end_at und capacity dürfen null sein.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EventUpdate"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Geändertes Event",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Event"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PlanRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      },
      "delete": {
        "summary": "Event löschen",
        "description": "Ein Entwurf oder ein Event ohne Anmeldungen wird gelöscht. Ein veröffentlichtes Event mit mindestens einer Anmeldung ergibt 409 event_has_registrations.",
        "responses": {
          "200": {
            "description": "Event gelöscht",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Message"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PlanRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Veröffentlichtes Event mit Anmeldungen",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "examples": {
                  "anmeldungen": {
                    "value": {
                      "code": "event_has_registrations",
                      "message": "Das Event ist veröffentlicht und hat bereits Anmeldungen. Es kann deshalb nicht gelöscht werden.",
                      "details": {
                        "count": 3
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/events/{eventID}/tickets": {
      "get": {
        "summary": "Ticketarten auflisten",
        "parameters": [
          {
            "$ref": "#/components/parameters/EventID"
          }
        ],
        "responses": {
          "200": {
            "description": "Ticketarten des Events",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TicketList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PlanRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/events/{eventID}/registrations": {
      "get": {
        "summary": "Anmeldungen auflisten",
        "description": "Liefert die Anmeldungen eines Events in der Reihenfolge created_at und id. updated_since gleicht Änderungen seit einem Zeitpunkt ab. Der Cursor kodiert den letzten Schlüssel, keinen Offset.",
        "parameters": [
          {
            "$ref": "#/components/parameters/EventID"
          },
          {
            "$ref": "#/components/parameters/Limit"
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/UpdatedSince"
          },
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filtert genau einen Status.",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "confirmed",
                "waitlisted",
                "cancelled",
                "declined"
              ]
            }
          },
          {
            "name": "query",
            "in": "query",
            "required": false,
            "description": "Sucht ohne Beachtung der Großschreibung in Name und E-Mail-Adresse.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Seite der Anmeldeliste",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RegistrationPage"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PlanRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/events/{eventID}/registrations.csv": {
      "get": {
        "summary": "Anmeldungen als CSV exportieren",
        "description": "Liefert die Gästeliste als CSV mit Semikolon als Trennzeichen. Ohne limit werden alle Zeilen ausgegeben. Ein limit über 5000 ergibt 400 invalid_limit.",
        "parameters": [
          {
            "$ref": "#/components/parameters/EventID"
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Höchstzahl der ausgegebenen Anmeldungen. Ohne Angabe werden alle Zeilen exportiert.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 5000
            }
          }
        ],
        "responses": {
          "200": {
            "description": "CSV-Export",
            "content": {
              "text/csv": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PlanRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/events/{eventID}/stats": {
      "get": {
        "summary": "Kennzahlen eines Events",
        "parameters": [
          {
            "$ref": "#/components/parameters/EventID"
          },
          {
            "name": "days",
            "in": "query",
            "required": false,
            "description": "Zeitraum der Aufrufstatistik in Tagen.",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 365,
              "default": 30
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Kennzahlen",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EventStats"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PlanRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/api/v1/membership/tiers": {
      "get": {
        "summary": "Mitgliedschaftsstufen auflisten",
        "description": "Liefert alle Stufen der Organisation, auch nicht öffentliche, sortiert nach sort_order und Name.",
        "responses": {
          "200": {
            "description": "Stufen der Organisation",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MembershipTierList"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PlanRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/membership/tiers/{tierID}/members": {
      "parameters": [
        {
          "name": "tierID",
          "in": "path",
          "required": true,
          "description": "Kennung der Mitgliedschaftsstufe",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "get": {
        "summary": "Mitglieder einer Stufe auflisten",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "required": false,
            "description": "Filter auf pending, active, cancelled oder expired.",
            "schema": {
              "type": "string",
              "enum": [
                "pending",
                "active",
                "cancelled",
                "expired"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Mitglieder der Stufe",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MembershipList"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PlanRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      },
      "post": {
        "summary": "Mitglied aufnehmen",
        "description": "Verlangt die Stufe eine Freigabe, entsteht status pending, sonst active. Eine bereits eingetragene Adresse ergibt 409 membership_exists.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/MembershipMemberCreate"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Angelegte Mitgliedschaft",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Membership"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/BadRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PlanRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Adresse ist in der Stufe bereits eingetragen. Code: membership_exists.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    },
    "/api/v1/membership/members/{membershipID}/approve": {
      "parameters": [
        {
          "name": "membershipID",
          "in": "path",
          "required": true,
          "description": "Kennung der Mitgliedschaft",
          "schema": {
            "type": "string",
            "format": "uuid"
          }
        }
      ],
      "post": {
        "summary": "Mitgliedschaft freigeben",
        "description": "Setzt eine offene Mitgliedschaft auf active und activated_at. Eine bereits aktive Mitgliedschaft wird unverändert zurückgegeben.",
        "responses": {
          "200": {
            "description": "Freigegebene Mitgliedschaft",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Membership"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/PlanRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "description": "Mitgliedschaft ist nicht offen. Code: membership_not_pending.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/InternalError"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "ew_...",
        "description": "API-Schlüssel aus der Eventwerk-Oberfläche. Der Kopf lautet Authorization: Bearer ew_...."
      }
    },
    "parameters": {
      "EventID": {
        "name": "eventID",
        "in": "path",
        "required": true,
        "description": "Kennung des Events",
        "schema": {
          "type": "string",
          "format": "uuid"
        }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "required": false,
        "description": "Seitengröße. Werte über 200 werden auf 200 begrenzt.",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 200,
          "default": 50
        }
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "required": false,
        "description": "Undurchsichtiger Cursor aus next_cursor der vorigen Antwort.",
        "schema": {
          "type": "string"
        }
      },
      "UpdatedSince": {
        "name": "updated_since",
        "in": "query",
        "required": false,
        "description": "Nur Datensätze, deren Änderungszeitpunkt später liegt. Zeitpunkt in UTC im Format RFC 3339.",
        "schema": {
          "type": "string",
          "format": "date-time"
        }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Ungültige Anfrage. Häufige Codes: invalid_json, unknown_field, invalid_limit, invalid_cursor, invalid_updated_since, invalid_status, invalid_idempotency_key, invalid_title, invalid_start_at, invalid_end_at, invalid_timezone, invalid_location_type, invalid_visibility, invalid_capacity, invalid_registration_mode, access_code_required, invalid_category, invalid_tags, no_changes.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "Unauthorized": {
        "description": "API-Schlüssel fehlt, ist unbekannt oder wurde widerrufen. Code: invalid_api_key.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "PlanRequired": {
        "description": "Die öffentliche API steht nur im Plus-Plan zur Verfügung. Code: plan_required.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "NotFound": {
        "description": "Das Event wurde nicht gefunden. Code: event_not_found.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "TooLarge": {
        "description": "Der Rumpf überschreitet 1 MB. Code: request_too_large.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Mehr als 200 Anfragen je Minute und Schlüssel auf dieser Instanz. Code: rate_limited. Der Kopf Retry-After nennt die Wartezeit in Sekunden.",
        "headers": {
          "Retry-After": {
            "description": "Wartezeit in Sekunden",
            "schema": {
              "type": "integer"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "InternalError": {
        "description": "Interner Fehler. Code: internal_error.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "code",
          "message"
        ],
        "properties": {
          "code": {
            "type": "string",
            "description": "Stabiler Fehlercode"
          },
          "message": {
            "type": "string",
            "description": "Deutsche Meldung für Menschen"
          },
          "details": {
            "type": [
              "object",
              "null"
            ],
            "description": "Zusätzliche Angaben, etwa field bei unknown_field oder count bei event_has_registrations",
            "additionalProperties": true
          }
        }
      },
      "Message": {
        "type": "object",
        "required": [
          "message"
        ],
        "properties": {
          "message": {
            "type": "string"
          }
        }
      },
      "Organization": {
        "type": "object",
        "required": [
          "id",
          "name",
          "slug",
          "plan"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "slug": {
            "type": "string"
          },
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "plus"
            ]
          }
        }
      },
      "Me": {
        "type": "object",
        "required": [
          "organization",
          "role"
        ],
        "properties": {
          "organization": {
            "$ref": "#/components/schemas/Organization"
          },
          "role": {
            "type": "string",
            "enum": [
              "owner"
            ]
          }
        }
      },
      "Event": {
        "type": "object",
        "required": [
          "id",
          "org_id",
          "slug",
          "title",
          "description",
          "cover_url",
          "start_at",
          "end_at",
          "timezone",
          "location_type",
          "location_name",
          "location_address",
          "online_url",
          "visibility",
          "status",
          "capacity",
          "registration_mode",
          "access_code",
          "category",
          "tags",
          "show_cohosts",
          "membership_tier_id",
          "latitude",
          "longitude",
          "series_id",
          "series_index",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "org_id": {
            "type": "string",
            "format": "uuid"
          },
          "slug": {
            "type": "string"
          },
          "title": {
            "type": "string",
            "maxLength": 200
          },
          "description": {
            "type": "string"
          },
          "cover_url": {
            "type": "string"
          },
          "start_at": {
            "type": "string",
            "format": "date-time",
            "description": "Beginn in UTC (RFC 3339)"
          },
          "end_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Ende in UTC (RFC 3339)"
          },
          "timezone": {
            "type": "string",
            "description": "IANA-Zeitzone des Events, zum Beispiel Europe/Vienna. start_at und end_at bleiben UTC."
          },
          "location_type": {
            "type": "string",
            "enum": [
              "offline",
              "online",
              "hybrid"
            ]
          },
          "location_name": {
            "type": "string"
          },
          "location_address": {
            "type": "string"
          },
          "online_url": {
            "type": "string"
          },
          "visibility": {
            "type": "string",
            "enum": [
              "public",
              "private"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "published"
            ]
          },
          "capacity": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          },
          "registration_mode": {
            "type": "string",
            "enum": [
              "auto",
              "approval",
              "code"
            ]
          },
          "access_code": {
            "type": "string"
          },
          "category": {
            "type": "string",
            "enum": [
              "",
              "konferenz",
              "workshop",
              "meetup",
              "webinar",
              "networking",
              "sonstiges"
            ]
          },
          "tags": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "string",
              "maxLength": 40
            }
          },
          "show_cohosts": {
            "type": "boolean"
          },
          "membership_tier_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Ist eine Stufe gesetzt, ist das Event nur für aktive Mitglieder dieser Stufe buchbar."
          },
          "latitude": {
            "type": [
              "number",
              "null"
            ]
          },
          "longitude": {
            "type": [
              "number",
              "null"
            ]
          },
          "series_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid"
          },
          "series_index": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Anlagezeitpunkt in UTC (RFC 3339)"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time",
            "description": "Änderungszeitpunkt in UTC (RFC 3339)"
          }
        }
      },
      "EventWithRegistrationCount": {
        "allOf": [
          {
            "$ref": "#/components/schemas/Event"
          },
          {
            "type": "object",
            "required": [
              "registration_count"
            ],
            "properties": {
              "registration_count": {
                "type": "integer",
                "minimum": 0
              }
            }
          }
        ]
      },
      "EventPage": {
        "type": "object",
        "required": [
          "items",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/EventWithRegistrationCount"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Undurchsichtiger Cursor für die nächste Seite oder null"
          }
        }
      },
      "EventCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "title",
          "start_at"
        ],
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 200,
            "description": "Pflichtfeld"
          },
          "description": {
            "type": "string",
            "default": ""
          },
          "cover_url": {
            "type": "string",
            "default": ""
          },
          "start_at": {
            "type": "string",
            "format": "date-time",
            "description": "Pflichtfeld. Beginn in UTC (RFC 3339)."
          },
          "end_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Ende in UTC (RFC 3339)"
          },
          "timezone": {
            "type": "string",
            "default": "Europe/Vienna",
            "description": "IANA-Zeitzone. start_at und end_at bleiben UTC."
          },
          "location_type": {
            "type": "string",
            "enum": [
              "offline",
              "online",
              "hybrid"
            ],
            "default": "offline"
          },
          "location_name": {
            "type": "string",
            "default": ""
          },
          "location_address": {
            "type": "string",
            "default": ""
          },
          "online_url": {
            "type": "string",
            "default": ""
          },
          "visibility": {
            "type": "string",
            "enum": [
              "public",
              "private"
            ],
            "default": "private"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "published"
            ],
            "default": "draft",
            "description": "Mit published wird das Event über den API-Schlüssel sofort veröffentlicht."
          },
          "capacity": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          },
          "registration_mode": {
            "type": "string",
            "enum": [
              "auto",
              "approval",
              "code"
            ],
            "default": "auto"
          },
          "access_code": {
            "type": "string",
            "default": "",
            "description": "Pflicht bei registration_mode code"
          },
          "category": {
            "type": "string",
            "enum": [
              "",
              "konferenz",
              "workshop",
              "meetup",
              "webinar",
              "networking",
              "sonstiges"
            ],
            "default": ""
          },
          "tags": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "string",
              "maxLength": 40
            },
            "default": []
          }
        }
      },
      "EventUpdate": {
        "type": "object",
        "additionalProperties": false,
        "minProperties": 1,
        "description": "Alle Felder sind optional. Mindestens eines muss gesetzt sein, sonst folgt 400 no_changes.",
        "properties": {
          "title": {
            "type": "string",
            "maxLength": 200
          },
          "description": {
            "type": "string"
          },
          "cover_url": {
            "type": "string"
          },
          "start_at": {
            "type": "string",
            "format": "date-time",
            "description": "Beginn in UTC (RFC 3339)"
          },
          "end_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Ende in UTC (RFC 3339). null entfernt das Ende."
          },
          "timezone": {
            "type": "string",
            "description": "IANA-Zeitzone. start_at und end_at bleiben UTC."
          },
          "location_type": {
            "type": "string",
            "enum": [
              "offline",
              "online",
              "hybrid"
            ]
          },
          "location_name": {
            "type": "string"
          },
          "location_address": {
            "type": "string"
          },
          "online_url": {
            "type": "string"
          },
          "visibility": {
            "type": "string",
            "enum": [
              "public",
              "private"
            ]
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "published"
            ]
          },
          "capacity": {
            "type": [
              "integer",
              "null"
            ],
            "minimum": 0
          },
          "registration_mode": {
            "type": "string",
            "enum": [
              "auto",
              "approval",
              "code"
            ]
          },
          "access_code": {
            "type": "string"
          },
          "category": {
            "type": "string",
            "enum": [
              "",
              "konferenz",
              "workshop",
              "meetup",
              "webinar",
              "networking",
              "sonstiges"
            ]
          },
          "tags": {
            "type": "array",
            "maxItems": 10,
            "items": {
              "type": "string",
              "maxLength": 40
            }
          },
          "show_cohosts": {
            "type": "boolean"
          },
          "membership_tier_id": {
            "type": [
              "string",
              "null"
            ],
            "format": "uuid",
            "description": "Kennung einer Stufe der eigenen Organisation. Ist sie gesetzt, ist das Event nur für aktive Mitglieder dieser Stufe buchbar."
          }
        }
      },
      "Ticket": {
        "type": "object",
        "required": [
          "id",
          "event_id",
          "name",
          "description",
          "price_cents",
          "currency",
          "quantity",
          "min_per_order",
          "max_per_order",
          "hidden",
          "is_donation",
          "active",
          "sort_order",
          "created_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "event_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "description": {
            "type": "string"
          },
          "price_cents": {
            "type": "integer",
            "format": "int64",
            "description": "Preis in Cent, nie als Gleitkommazahl"
          },
          "currency": {
            "type": "string",
            "minLength": 3,
            "maxLength": 3
          },
          "quantity": {
            "type": [
              "integer",
              "null"
            ]
          },
          "min_per_order": {
            "type": "integer"
          },
          "max_per_order": {
            "type": "integer"
          },
          "hidden": {
            "type": "boolean"
          },
          "is_donation": {
            "type": "boolean"
          },
          "active": {
            "type": "boolean"
          },
          "sort_order": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Anlagezeitpunkt in UTC (RFC 3339)"
          }
        }
      },
      "TicketList": {
        "type": "object",
        "required": [
          "tickets"
        ],
        "properties": {
          "tickets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Ticket"
            }
          }
        }
      },
      "Registration": {
        "type": "object",
        "required": [
          "id",
          "name",
          "email",
          "status",
          "ticket_type_id",
          "price_paid_cents",
          "checked_in_at",
          "created_at",
          "attendance_response",
          "attendance_responded_at",
          "referral_code",
          "referral_name"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "confirmed",
              "waitlisted",
              "cancelled",
              "declined"
            ]
          },
          "ticket_type_id": {
            "type": "string",
            "format": "uuid"
          },
          "price_paid_cents": {
            "type": "integer",
            "format": "int64",
            "description": "Gezahlter Preis in Cent"
          },
          "checked_in_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Check-in-Zeitpunkt in UTC (RFC 3339)"
          },
          "created_at": {
            "type": "string",
            "format": "date-time",
            "description": "Anmeldezeitpunkt in UTC (RFC 3339)"
          },
          "attendance_response": {
            "type": "string",
            "enum": [
              "unknown",
              "yes",
              "no"
            ]
          },
          "attendance_responded_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "Antwortzeitpunkt in UTC (RFC 3339)"
          },
          "referral_code": {
            "type": "string"
          },
          "referral_name": {
            "type": "string"
          }
        }
      },
      "RegistrationPage": {
        "type": "object",
        "required": [
          "items",
          "has_more",
          "next_cursor"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Registration"
            }
          },
          "has_more": {
            "type": "boolean"
          },
          "next_cursor": {
            "type": [
              "string",
              "null"
            ],
            "description": "Undurchsichtiger Cursor für die nächste Seite oder null"
          }
        }
      },
      "EventStats": {
        "type": "object",
        "required": [
          "views_total",
          "views_by_day",
          "registrations",
          "confirmed",
          "pending",
          "waitlisted",
          "cancelled",
          "checked_in",
          "attendance_yes",
          "attendance_no",
          "attendance_unknown",
          "conversion_rate",
          "checkin_rate"
        ],
        "properties": {
          "views_total": {
            "type": "integer"
          },
          "views_by_day": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/DailyViews"
            }
          },
          "registrations": {
            "type": "integer"
          },
          "confirmed": {
            "type": "integer"
          },
          "pending": {
            "type": "integer"
          },
          "waitlisted": {
            "type": "integer"
          },
          "cancelled": {
            "type": "integer"
          },
          "checked_in": {
            "type": "integer"
          },
          "attendance_yes": {
            "type": "integer"
          },
          "attendance_no": {
            "type": "integer"
          },
          "attendance_unknown": {
            "type": "integer"
          },
          "conversion_rate": {
            "type": "number"
          },
          "checkin_rate": {
            "type": "number"
          }
        }
      },
      "DailyViews": {
        "type": "object",
        "required": [
          "day",
          "views"
        ],
        "properties": {
          "day": {
            "type": "string",
            "format": "date-time",
            "description": "Kalendertag um Mitternacht in UTC (RFC 3339)"
          },
          "views": {
            "type": "integer"
          }
        }
      },
      "MembershipTier": {
        "type": "object",
        "required": [
          "id",
          "org_id",
          "name",
          "description",
          "price_cents",
          "interval",
          "requires_approval",
          "is_public",
          "sort_order",
          "created_at",
          "updated_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "org_id": {
            "type": "string",
            "format": "uuid"
          },
          "name": {
            "type": "string",
            "maxLength": 200
          },
          "description": {
            "type": "string",
            "maxLength": 2000
          },
          "price_cents": {
            "type": "integer",
            "format": "int64",
            "minimum": 0,
            "description": "Preis in Cent, nie als Gleitkommazahl. 0 bedeutet kostenlos."
          },
          "interval": {
            "type": "string",
            "enum": [
              "none",
              "month",
              "year"
            ]
          },
          "requires_approval": {
            "type": "boolean"
          },
          "is_public": {
            "type": "boolean"
          },
          "sort_order": {
            "type": "integer"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "updated_at": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "MembershipTierList": {
        "type": "object",
        "required": [
          "tiers"
        ],
        "properties": {
          "tiers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MembershipTier"
            }
          }
        }
      },
      "Membership": {
        "type": "object",
        "required": [
          "id",
          "tier_id",
          "tier_name",
          "org_id",
          "email",
          "name",
          "status",
          "created_at",
          "activated_at",
          "expires_at"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "tier_id": {
            "type": "string",
            "format": "uuid"
          },
          "tier_name": {
            "type": "string"
          },
          "org_id": {
            "type": "string",
            "format": "uuid"
          },
          "email": {
            "type": "string",
            "format": "email"
          },
          "name": {
            "type": "string"
          },
          "status": {
            "type": "string",
            "enum": [
              "pending",
              "active",
              "cancelled",
              "expired"
            ]
          },
          "calendar_url": {
            "type": "string",
            "description": "Persönlicher Kalender-Abo-Link"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "activated_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "expires_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          }
        }
      },
      "MembershipList": {
        "type": "object",
        "required": [
          "members"
        ],
        "properties": {
          "members": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Membership"
            }
          }
        }
      },
      "MembershipMemberCreate": {
        "type": "object",
        "additionalProperties": false,
        "required": [
          "email"
        ],
        "properties": {
          "email": {
            "type": "string",
            "format": "email"
          },
          "name": {
            "type": "string",
            "maxLength": 200,
            "description": "Optionaler Anzeigename"
          }
        }
      }
    }
  }
}
