{"openapi":"3.1.0","info":{"title":"EventX Public API v2","version":"v2","description":"Public API for EventX organisers — Event CRUD, attendee management (including bulk upsert), attendee tickets, attendee addons, promo codes, and per-session check-ins (datetime-range query).\n\n⚠️ **Beta API** — EventX reserves the right to update this API and introduce breaking changes without prior notice.\n\n---\n\n## Authentication — Organizer API Token (Org / Event Scoped, Not User)\n\nAll `Event`, `Attendee`, and `/mcp` endpoints authenticate with an **organizer API token** — not a user JWT.\n\n### Token Format\n\nSend the token on every request as:\n\n```\nAuthorization: Bearer evtx_live_<secret>\n```\n\n- Secret shape: `evtx_live_` (10-char prefix) followed by a 43-char base64url secret (32 random bytes) — e.g. `evtx_live_9k3…cB1`.\n- `x-api-key: evtx_live_…` is also accepted as an alternative header.\n- The token must begin with the `evtx_` prefix or it is rejected with `401`.\n\n### Provisioning\n\nTokens are created in the **Organiser Portal → API Tokens** (`portalRoutes.userPortalApiTokens`).\n\nThe raw secret is **shown only once** at creation. Subsequent reads return only `prefix` and `last4` for display.\n\n### Scope\n\n- **`scope='org'`** → `token.organization_id === event.organization_id`; grants access to **all events in the organization**.\n- **`scope='event'`** → `token.event_id === eventId`; grants access to **only that single event**. An event-scoped token **cannot** call the org-only `GET /v2/events` list endpoint.\n\nScope enforcement is **pure token-level** — it never consults `userId`, `user_org_assignment`, or attendee `roleTags` (`event-auth.ts` is bypassed for these routes).\n\n### Permissions\n\n- **`read`** → GET requests only.\n- **`write`** → implies `read`; allows POST / PUT / PATCH / DELETE (checked via `isWriteMethod`).\n\n### Validation & Errors\n\nA token is rejected when `is_enabled=false`, `deleted_at` is set, or `expires_at` is in the past. Failure modes:\n\n- `401` `ERR_PUBLIC_API_UNAUTHORIZED` — missing, malformed, expired, or revoked token.\n- `403` `ERR_PUBLIC_API_FORBIDDEN` — token valid but wrong org/event scope, or `read`-only token used on a write method.\n\nLegacy `POST /auth` JWT exchange (`user:{userId}:{secret}` → `Bearer JWT` 1h) remains for backward compat on tickets/addons, but `Event`/`Attendee` prefer direct `organizer_api_token` Bearer.\n\n## Authentication (Legacy JWT)\n\nLegacy `/v2/*` (tickets/addons) also accept Bearer JWT via `POST /auth`.\n\n## Error Response Envelope\n\nAll errors return a consistent JSON envelope:\n\n```json\n{ \"code\": \"ERR_PUBLIC_API_<NAME>\", \"message\": \"Human-readable error\", \"status\": 400, \"meta\": { \"errors\": {} } }\n```\n\nThe `meta` field is optional and contains additional context (e.g., validation field errors).\n\n### Error Codes\n\n| Code | HTTP Status | Meaning |\n|------|-------------|---------|\n| `ERR_PUBLIC_API_VALIDATION_ERROR` | 400 | Request body or path parameters failed validation |\n| `ERR_PUBLIC_API_INVALID_STATUS_TRANSITION` | 400 | The requested addon status transition is not allowed |\n| `ERR_PUBLIC_API_UNAUTHORIZED` | 401 | Missing, invalid, or revoked JWT |\n| `ERR_PUBLIC_API_FORBIDDEN` | 403 | User is not authorized for this event (not an organiser or org member) |\n| `ERR_PUBLIC_API_NOT_FOUND` | 404 | Resource not found within the given scope |\n| `ERR_PUBLIC_API_PAYLOAD_TOO_LARGE` | 413 | Request body exceeds the 1MB limit |\n| `ERR_PUBLIC_API_RATE_LIMITED` | 429 | Rate limit exceeded (see Rate Limiting below) |\n| `ERR_PUBLIC_API_QUOTA_EXCEEDED` | 429 | Monthly API quota exceeded (100/mo default, per subscription) |\n| `ERR_PUBLIC_API_INTERNAL_ERROR` | 500 | Unexpected server error |\n\n## Monthly API Quota\n\n- **Default:** 100 calls per calendar month (UTC `YYYY-MM`) per active subscription\n- **Anchor:** `subscription.id` when an active subscription exists, otherwise `organization.id`; renewal mid-month resets usage\n- **Counting:** only `2xx` responses increment; 4xx/5xx do not count (accepted drift via `waitUntil`)\n- **On exceed:** HTTP 429 with `ERR_PUBLIC_API_QUOTA_EXCEEDED`, `Retry-After` (seconds to next month), and `meta { quota, used, period, resetsAt }`\n\n## Rate Limiting\n\n- **Limit:** 300 requests per 60-second window, per client IP\n- **Key:** `CF-Connecting-IP` header (provided by Cloudflare)\n- **Applied to:** `POST /auth` and all `/v2/*` endpoints\n- **On exceed:** HTTP 429 with `ERR_PUBLIC_API_RATE_LIMITED` and a `Retry-After` header\n\n## Request Body Size\n\n- Maximum request body size: **1MB**\n- Exceeding this returns HTTP 413 with `ERR_PUBLIC_API_PAYLOAD_TOO_LARGE`\n\n## MCP Server (AI Agent Access)\n\nAn [Model Context Protocol](https://modelcontextprotocol.io) server is exposed at `/mcp` for AI agents to query and manage event data.\n\n- **Endpoint:** `{server}/mcp`\n- **Auth:** send an **organizer API token** on every request as `Authorization: Bearer evtx_live_…` (Org- or Event-scoped; `read` suffices for read tools, `write` required for promo-code writes).\n- **Scope:** **Org/Event scoped (not user-scoped)**. An Org-scoped token can access every event in the organization; an Event-scoped token only the single event it was issued for. Read tools: list events, list/get attendees, tickets, addons, promo codes, check-in sessions/check-ins. Write tools: create/patch/delete promo codes and check-ins require `write` permission and explicit human confirmation (`confirm:true`).\n- **Install guide:** see `docs/mcp-guide.md` in the repository for connecting Claude Desktop, Cursor, and other MCP clients."},"servers":[{"url":"https://api.eventx.io","description":"EventX Public API server"}],"tags":[{"name":"auth","description":"Token exchange"},{"name":"events","description":"Event CRUD — organizer_api_token org/event scoped, rich examples"},{"name":"attendees","description":"Event attendee management — externalId scoped by event, email optional, customQrString writable, checkInToken read-only, bulk upsert"},{"name":"attendee tickets","description":"Attendee ticket assignment"},{"name":"attendee addons","description":"Attendee addon selection"},{"name":"promo codes","description":"Promo code CRUD — discount for ticket classes, org/event scoped, write needs confirm via MCP"},{"name":"custom fields","description":"Custom field definitions per event (question_library) — soft-deleted (trash, recoverable via PATCH deletedAt:null), org/event scoped, write needs confirm via MCP"},{"name":"check-ins","description":"Per-session attendee check-in taps (physical_event_attendance) — CRUD with checkedAt datetime-range filter, per-session overview counts, attendee timeline; sessions read-only"}],"components":{"securitySchemes":{"organizerApiToken":{"type":"http","scheme":"bearer","description":"Organizer API token — Authorization: Bearer evtx_live_… (Org/Event scoped, read/write)."}},"schemas":{},"parameters":{}},"paths":{"/auth":{"post":{"tags":["auth"],"summary":"Exchange API key for a JWT","description":"Accepts an API key (`user:{userId}:{secret}`) and returns a short-lived JWT for use as a Bearer token on subsequent API requests.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"apiToken":{"type":"string","minLength":1,"description":"API key: user:<uuid>:<secret>","example":"user:00000000-0000-0000-0000-000000000000:your-api-secret-here-36-chars-long"}},"required":["apiToken"]}}}},"responses":{"200":{"description":"JWT issued successfully","content":{"application/json":{"schema":{"type":"object","properties":{"accessToken":{"type":"string","description":"Bearer JWT","example":"eyJhbGciOiJIUzI1NiJ9..."},"userId":{"type":"string","description":"Authenticated user ID","example":"2f310b4a-d3b3-47ca-b73e-e042df5e9e41"},"expiresIn":{"type":"string","description":"Token expiry duration","example":"1h"}},"required":["accessToken","userId","expiresIn"]}}}},"400":{"description":"Invalid token format","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}},"401":{"description":"Token not found, disabled, or expired","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}}}}},"/v2/events":{"get":{"tags":["events"],"summary":"List events","description":"List events accessible to the organizer_api_token. Token scoped at Org (all events in org) or Event (single). Pure token-level, not user. Supports pagination and filtering. Requires Bearer evtx_live_... with read permission.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"example":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0,"default":0,"example":0},"required":false,"name":"offset","in":"query"},{"schema":{"type":"string","example":"Tech"},"required":false,"name":"nameContains","in":"query"},{"schema":{"type":["boolean","null"],"example":true},"required":false,"name":"isPublished","in":"query"}],"responses":{"200":{"description":"Paginated events","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Event ID, UUID","example":"e63be3e1-7b4c-42b1-9369-aa390032f0ee"},"organizationId":{"type":["string","null"],"format":"uuid","description":"Organization ID, derived from token scope","example":"org_123"},"name":{"type":["string","null"],"description":"Event name","example":"Tech Summit 2026"},"descriptionHtml":{"type":["string","null"],"description":"Description HTML","example":"<p>Welcome</p>"},"timezone":{"type":["string","null"],"description":"IANA timezone","example":"Asia/Hong_Kong"},"startsAt":{"type":["string","null"],"description":"Start time ISO with timezone","example":"2026-03-01T09:00:00+08:00"},"endsAt":{"type":["string","null"],"description":"End time ISO","example":"2026-03-02T18:00:00+08:00"},"isPublished":{"type":["boolean","null"],"example":true},"createdAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"},"updatedAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"}},"required":["id","organizationId","name","descriptionHtml","timezone","startsAt","endsAt","isPublished","createdAt","updatedAt"],"description":"Event row — organizer_api_token scoped at Org or Event, not user. See info.description for full token scope explanation."},"example":[{"id":"e63be3e1-7b4c-42b1-9369-aa390032f0ee","organizationId":"org_123","name":"Tech Summit 2026","descriptionHtml":"<p>Welcome</p>","timezone":"Asia/Hong_Kong","startsAt":"2026-03-01T09:00:00+08:00","endsAt":"2026-03-02T18:00:00+08:00","isPublished":true,"createdAt":"2026-01-15T02:00:00.000Z","updatedAt":"2026-01-15T02:00:00.000Z"}]},"total":{"type":"number","example":100},"limit":{"type":"number","example":50},"offset":{"type":"number","example":0}},"required":["data","total","limit","offset"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden — wrong org/event scope","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}},"post":{"tags":["events"],"summary":"Create event","description":"Create event — requires organizer_api_token with scope org and write permission. Pure token-level, not user.","security":[{"organizerApiToken":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"description":"Event name, required","example":"Tech Summit 2026"},"timezone":{"type":"string","minLength":1,"description":"IANA timezone, required","example":"Asia/Hong_Kong"},"startsAt":{"type":"string","description":"ISO start, required","example":"2026-03-01T09:00:00+08:00"},"endsAt":{"type":"string","description":"ISO end, required","example":"2026-03-02T18:00:00+08:00"},"descriptionHtml":{"type":"string","example":"<p>Welcome</p>"},"isPublished":{"type":"boolean","example":true}},"required":["name","timezone","startsAt","endsAt"],"description":"Create event — requires organizer_api_token with scope org (any event in org) or event (this event only). Pure token-level, not user.","example":{"name":"Tech Summit 2026","timezone":"Asia/Hong_Kong","startsAt":"2026-03-01T09:00:00+08:00","endsAt":"2026-03-02T18:00:00+08:00","descriptionHtml":"<p>Welcome</p>","isPublished":true}}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Event ID, UUID","example":"e63be3e1-7b4c-42b1-9369-aa390032f0ee"},"organizationId":{"type":["string","null"],"format":"uuid","description":"Organization ID, derived from token scope","example":"org_123"},"name":{"type":["string","null"],"description":"Event name","example":"Tech Summit 2026"},"descriptionHtml":{"type":["string","null"],"description":"Description HTML","example":"<p>Welcome</p>"},"timezone":{"type":["string","null"],"description":"IANA timezone","example":"Asia/Hong_Kong"},"startsAt":{"type":["string","null"],"description":"Start time ISO with timezone","example":"2026-03-01T09:00:00+08:00"},"endsAt":{"type":["string","null"],"description":"End time ISO","example":"2026-03-02T18:00:00+08:00"},"isPublished":{"type":["boolean","null"],"example":true},"createdAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"},"updatedAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"}},"required":["id","organizationId","name","descriptionHtml","timezone","startsAt","endsAt","isPublished","createdAt","updatedAt"],"description":"Event row — organizer_api_token scoped at Org or Event, not user. See info.description for full token scope explanation."}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}}},"/v2/events/{eventId}":{"get":{"tags":["events"],"summary":"Get event","description":"Get single event — token must be scoped to its org or that exact event.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","example":"e63be3e1-7b4c-42b1-9369-aa390032f0ee"},"required":true,"name":"eventId","in":"path"}],"responses":{"200":{"description":"Event","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Event ID, UUID","example":"e63be3e1-7b4c-42b1-9369-aa390032f0ee"},"organizationId":{"type":["string","null"],"format":"uuid","description":"Organization ID, derived from token scope","example":"org_123"},"name":{"type":["string","null"],"description":"Event name","example":"Tech Summit 2026"},"descriptionHtml":{"type":["string","null"],"description":"Description HTML","example":"<p>Welcome</p>"},"timezone":{"type":["string","null"],"description":"IANA timezone","example":"Asia/Hong_Kong"},"startsAt":{"type":["string","null"],"description":"Start time ISO with timezone","example":"2026-03-01T09:00:00+08:00"},"endsAt":{"type":["string","null"],"description":"End time ISO","example":"2026-03-02T18:00:00+08:00"},"isPublished":{"type":["boolean","null"],"example":true},"createdAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"},"updatedAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"}},"required":["id","organizationId","name","descriptionHtml","timezone","startsAt","endsAt","isPublished","createdAt","updatedAt"],"description":"Event row — organizer_api_token scoped at Org or Event, not user. See info.description for full token scope explanation."}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}},"patch":{"tags":["events"],"summary":"Update event","description":"Partial update — requires write permission and matching org/event scope.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","example":"e63be3e1-7b4c-42b1-9369-aa390032f0ee"},"required":true,"name":"eventId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200,"example":"Tech Summit 2026 — Updated"},"timezone":{"type":"string","example":"Asia/Hong_Kong"},"startsAt":{"type":"string","example":"2026-03-01T09:00:00+08:00"},"endsAt":{"type":"string","example":"2026-03-02T18:00:00+08:00"},"descriptionHtml":{"type":"string","example":"<p>Updated</p>"},"isPublished":{"type":"boolean","example":true}},"description":"Partial update — at least one field required"}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Event ID, UUID","example":"e63be3e1-7b4c-42b1-9369-aa390032f0ee"},"organizationId":{"type":["string","null"],"format":"uuid","description":"Organization ID, derived from token scope","example":"org_123"},"name":{"type":["string","null"],"description":"Event name","example":"Tech Summit 2026"},"descriptionHtml":{"type":["string","null"],"description":"Description HTML","example":"<p>Welcome</p>"},"timezone":{"type":["string","null"],"description":"IANA timezone","example":"Asia/Hong_Kong"},"startsAt":{"type":["string","null"],"description":"Start time ISO with timezone","example":"2026-03-01T09:00:00+08:00"},"endsAt":{"type":["string","null"],"description":"End time ISO","example":"2026-03-02T18:00:00+08:00"},"isPublished":{"type":["boolean","null"],"example":true},"createdAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"},"updatedAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"}},"required":["id","organizationId","name","descriptionHtml","timezone","startsAt","endsAt","isPublished","createdAt","updatedAt"],"description":"Event row — organizer_api_token scoped at Org or Event, not user. See info.description for full token scope explanation."}}}},"400":{"description":"Validation","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}},"delete":{"tags":["events"],"summary":"Delete event","description":"Soft delete — requires write and org scope.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","example":"e63be3e1-7b4c-42b1-9369-aa390032f0ee"},"required":true,"name":"eventId","in":"path"}],"responses":{"204":{"description":"Deleted"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}}},"/v2/events/{eventId}/attendees":{"get":{"tags":["attendees"],"summary":"List attendees in an event","description":"Returns paginated attendees for an event. Requires organizer_api_token with scope org (any event in org) or event (this event only). Pure token-level, not user. Supports limit/offset and optional externalId/email/addOnId filter (addOnId returns only attendees who have selected that addon — exists in attendee_add_on). Token read permission suffices.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event UUID"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Limit 1-100, default 50","example":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0,"default":0,"description":"Offset","example":0},"required":false,"name":"offset","in":"query"},{"schema":{"type":"string","description":"Filter by externalId (provider ID)","example":"ext_123"},"required":false,"name":"externalId","in":"query"},{"schema":{"type":"string","description":"Filter by email","example":"alex@acme.com"},"required":false,"name":"email","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Filter by addon ID — returns only attendees who have selected this addon (exists in attendee_add_on for this event)","example":"473ed8f7-deae-4bee-bb32-eafd860aebe4"},"required":false,"name":"addOnId","in":"query"},{"schema":{"type":"string","description":"Free-text search across firstName, lastName, email, name, organization, externalId (case-insensitive substring)","example":"alex"},"required":false,"name":"q","in":"query"},{"schema":{"type":"string","enum":["pending","confirmed","cancelled"],"description":"Filter by registration status","example":"confirmed"},"required":false,"name":"registrationStatus","in":"query"},{"schema":{"type":"string","enum":["waitlisted","approved","rejected"],"description":"Filter by approval status","example":"approved"},"required":false,"name":"approvalStatus","in":"query"},{"schema":{"type":"string","enum":["none","suspended"],"description":"Filter by suspend status","example":"none"},"required":false,"name":"suspendStatus","in":"query"},{"schema":{"type":"string","description":"Comma-separated role tags — matches attendees with ANY of the listed tags (OR)","example":"exhibitor,speaker"},"required":false,"name":"roleTags","in":"query"},{"schema":{"type":"string","description":"Custom field key to search within","example":"dietary_restrictions"},"required":false,"name":"customFieldKey","in":"query"},{"schema":{"type":"string","description":"Substring to match within the custom field value (case-insensitive)","example":"vegan"},"required":false,"name":"customFieldContains","in":"query"},{"schema":{"type":"string","enum":["createdAt","name","email"],"description":"Sort field (default: createdAt)","example":"createdAt"},"required":false,"name":"sortBy","in":"query"},{"schema":{"type":"string","enum":["asc","desc"],"description":"Sort direction (default: desc)","example":"desc"},"required":false,"name":"sortDirection","in":"query"}],"responses":{"200":{"description":"Paginated list","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Attendee ID, UUID","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"eventId":{"type":"string","format":"uuid","description":"Event ID","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"externalId":{"type":["string","null"],"description":"Provider external ID, scoped by event (UNIQUE(event_id, externalId))","example":"ext_123"},"email":{"type":["string","null"],"description":"Contact email (maps to invitation_email, optional)","example":"alex@acme.com"},"customQrString":{"type":["string","null"],"description":"Custom QR string (maps to customCheckInToken, writable)","example":"WELCOME-2026-QR-A1B2C3"},"checkInToken":{"type":["string","null"],"description":"System check-in token, read-only (from check_in_token, ignored on write)","example":"ci_9f3k2a1b"},"name":{"type":["string","null"],"example":"Alex Chen"},"firstName":{"type":["string","null"],"example":"Alex"},"lastName":{"type":["string","null"],"example":"Chen"},"jobTitle":{"type":["string","null"],"example":"Engineer"},"organization":{"type":["string","null"],"example":"Acme Inc"},"city":{"type":["string","null"],"example":"Hong Kong"},"country":{"type":["string","null"],"example":"HK"},"areaCode":{"type":["string","null"],"example":"852"},"contactNo":{"type":["string","null"],"example":"98765432"},"roleTags":{"example":["visitor"]},"customFields":{"example":{}},"approvalStatus":{"type":["string","null"],"example":"waitlisted"},"registrationStatus":{"type":["string","null"],"example":"pending"},"suspendStatus":{"type":"string","example":"none"},"createdAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.429Z"},"updatedAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.429Z"},"addOns":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Add-on row ID","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"addOnId":{"type":["string","null"],"description":"FK to ticket_class_add_on","example":"473ed8f7-deae-4bee-bb32-eafd860aebe4"},"qty":{"type":"integer","description":"Quantity","example":1},"selectionStatus":{"type":["string","null"],"description":"Selection status (regular/waitlisted/confirmed)","example":"waitlisted"},"confirmedAt":{"type":["string","null"],"description":"Confirmation timestamp","example":null},"createdAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.518Z"},"updatedAt":{"type":["string","null"],"example":"2026-08-17T09:36:09.620Z"}},"required":["id","addOnId","qty","selectionStatus","confirmedAt","createdAt","updatedAt"],"description":"Attendee add-on record"},"description":"Associated add-on records"}},"required":["id","eventId","externalId","email","customQrString","checkInToken","name","firstName","lastName","jobTitle","organization","city","country","areaCode","contactNo","approvalStatus","registrationStatus","suspendStatus","createdAt","updatedAt","addOns"],"description":"Attendee row — scoped by organizer_api_token (Org or Event, not user). Token permissions: read allows GET, write allows all. See info.description for full token scope."}},"total":{"type":"number","example":3},"limit":{"type":"number","example":50},"offset":{"type":"number","example":0}},"required":["data","total","limit","offset"]}}}},"400":{"description":"Validation error (invalid sortBy, registrationStatus, approvalStatus, suspendStatus, sortDirection)","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"401":{"description":"Unauthorized — missing or invalid organizer token","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden — token scope mismatch or read required","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}},"post":{"tags":["attendees"],"summary":"Create an attendee","description":"Creates attendee — supply externalId at creation to enable later provider-based update (ext_123). Email optional (no unique per event). Custom QR string writable (customCheckInToken), checkInToken read-only ignored. Requires organizer_api_token write, org/event scoped.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event UUID"},"required":true,"name":"eventId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"externalId":{"type":"string","maxLength":100,"description":"Provider external ID, scoped by event, optional but recommended for upsert","example":"ext_123"},"email":{"type":"string","maxLength":254,"format":"email","description":"Contact email (maps to invitation_email, optional, no unique)","example":"alex@acme.com"},"customQrString":{"type":"string","maxLength":500,"description":"Custom QR string (maps to customCheckInToken, writable)","example":"WELCOME-2026-QR-A1B2C3"},"name":{"type":"string","maxLength":500,"example":"Alex Chen"},"firstName":{"type":"string","maxLength":500,"example":"Alex"},"lastName":{"type":"string","maxLength":500,"example":"Chen"},"jobTitle":{"type":"string","maxLength":500,"example":"Engineer"},"organization":{"type":"string","maxLength":500,"example":"Acme Inc"},"city":{"type":"string","maxLength":50,"example":"Hong Kong"},"country":{"type":"string","maxLength":50,"example":"HK"},"areaCode":{"type":"string","maxLength":50,"example":"852"},"contactNo":{"type":"string","maxLength":50,"example":"98765432"},"customFields":{"type":"object","additionalProperties":{},"description":"Custom fields keyed by custom-field id (GET /custom-fields). Values are wrapped as {value: string|string[]|boolean} (or {value, mediaValues} for fileUpload). Flat string/boolean/array values are auto-wrapped server-side. Unknown field ids → 400.","example":{"7180eec9-e955-462a-bfd2-ed75d37169d2":{"value":"Short answer"},"d62e4be4-3104-42ec-921f-53e7f2332c3b":{"value":"opt1"},"2e0ce9d8-b388-4226-9a89-864a9348232b":{"value":["opt1","opt2"]},"4e206a98-a988-4f1f-8f4a-14190a7d3065":{"value":true}}},"roleTags":{"type":"array","items":{"type":"string"},"description":"Visible role tags (visitor/organizer/host/speaker/exhibitor/booth-rep). Defaults to [\"visitor\"] when omitted — tag-less attendees are hidden from the portal people list.","example":["visitor"]}},"description":"Create attendee — supply externalId at creation to enable later update by provider ID (ext_123). Email optional. Custom QR string writable; checkInToken read-only ignored. customFields must use custom-field ids from GET /custom-fields, wrapped as {value: ...}.","example":{"externalId":"ext_123","email":"alex@acme.com","customQrString":"WELCOME-2026-QR-A1B2C3","name":"Alex Chen"}}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Attendee ID, UUID","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"eventId":{"type":"string","format":"uuid","description":"Event ID","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"externalId":{"type":["string","null"],"description":"Provider external ID, scoped by event (UNIQUE(event_id, externalId))","example":"ext_123"},"email":{"type":["string","null"],"description":"Contact email (maps to invitation_email, optional)","example":"alex@acme.com"},"customQrString":{"type":["string","null"],"description":"Custom QR string (maps to customCheckInToken, writable)","example":"WELCOME-2026-QR-A1B2C3"},"checkInToken":{"type":["string","null"],"description":"System check-in token, read-only (from check_in_token, ignored on write)","example":"ci_9f3k2a1b"},"name":{"type":["string","null"],"example":"Alex Chen"},"firstName":{"type":["string","null"],"example":"Alex"},"lastName":{"type":["string","null"],"example":"Chen"},"jobTitle":{"type":["string","null"],"example":"Engineer"},"organization":{"type":["string","null"],"example":"Acme Inc"},"city":{"type":["string","null"],"example":"Hong Kong"},"country":{"type":["string","null"],"example":"HK"},"areaCode":{"type":["string","null"],"example":"852"},"contactNo":{"type":["string","null"],"example":"98765432"},"roleTags":{"example":["visitor"]},"customFields":{"example":{}},"approvalStatus":{"type":["string","null"],"example":"waitlisted"},"registrationStatus":{"type":["string","null"],"example":"pending"},"suspendStatus":{"type":"string","example":"none"},"createdAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.429Z"},"updatedAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.429Z"},"addOns":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Add-on row ID","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"addOnId":{"type":["string","null"],"description":"FK to ticket_class_add_on","example":"473ed8f7-deae-4bee-bb32-eafd860aebe4"},"qty":{"type":"integer","description":"Quantity","example":1},"selectionStatus":{"type":["string","null"],"description":"Selection status (regular/waitlisted/confirmed)","example":"waitlisted"},"confirmedAt":{"type":["string","null"],"description":"Confirmation timestamp","example":null},"createdAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.518Z"},"updatedAt":{"type":["string","null"],"example":"2026-08-17T09:36:09.620Z"}},"required":["id","addOnId","qty","selectionStatus","confirmedAt","createdAt","updatedAt"],"description":"Attendee add-on record"},"description":"Associated add-on records"}},"required":["id","eventId","externalId","email","customQrString","checkInToken","name","firstName","lastName","jobTitle","organization","city","country","areaCode","contactNo","approvalStatus","registrationStatus","suspendStatus","createdAt","updatedAt","addOns"],"description":"Attendee row — scoped by organizer_api_token (Org or Event, not user). Token permissions: read allows GET, write allows all. See info.description for full token scope."}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}}},"/v2/events/{eventId}/attendees/{attendeeId}":{"get":{"tags":["attendees"],"summary":"Get attendee by ID","description":"Get single attendee by UUID — token must be scoped to its event/org.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event UUID"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Attendee UUID"},"required":true,"name":"attendeeId","in":"path"}],"responses":{"200":{"description":"Attendee","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Attendee ID, UUID","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"eventId":{"type":"string","format":"uuid","description":"Event ID","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"externalId":{"type":["string","null"],"description":"Provider external ID, scoped by event (UNIQUE(event_id, externalId))","example":"ext_123"},"email":{"type":["string","null"],"description":"Contact email (maps to invitation_email, optional)","example":"alex@acme.com"},"customQrString":{"type":["string","null"],"description":"Custom QR string (maps to customCheckInToken, writable)","example":"WELCOME-2026-QR-A1B2C3"},"checkInToken":{"type":["string","null"],"description":"System check-in token, read-only (from check_in_token, ignored on write)","example":"ci_9f3k2a1b"},"name":{"type":["string","null"],"example":"Alex Chen"},"firstName":{"type":["string","null"],"example":"Alex"},"lastName":{"type":["string","null"],"example":"Chen"},"jobTitle":{"type":["string","null"],"example":"Engineer"},"organization":{"type":["string","null"],"example":"Acme Inc"},"city":{"type":["string","null"],"example":"Hong Kong"},"country":{"type":["string","null"],"example":"HK"},"areaCode":{"type":["string","null"],"example":"852"},"contactNo":{"type":["string","null"],"example":"98765432"},"roleTags":{"example":["visitor"]},"customFields":{"example":{}},"approvalStatus":{"type":["string","null"],"example":"waitlisted"},"registrationStatus":{"type":["string","null"],"example":"pending"},"suspendStatus":{"type":"string","example":"none"},"createdAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.429Z"},"updatedAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.429Z"},"addOns":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Add-on row ID","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"addOnId":{"type":["string","null"],"description":"FK to ticket_class_add_on","example":"473ed8f7-deae-4bee-bb32-eafd860aebe4"},"qty":{"type":"integer","description":"Quantity","example":1},"selectionStatus":{"type":["string","null"],"description":"Selection status (regular/waitlisted/confirmed)","example":"waitlisted"},"confirmedAt":{"type":["string","null"],"description":"Confirmation timestamp","example":null},"createdAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.518Z"},"updatedAt":{"type":["string","null"],"example":"2026-08-17T09:36:09.620Z"}},"required":["id","addOnId","qty","selectionStatus","confirmedAt","createdAt","updatedAt"],"description":"Attendee add-on record"},"description":"Associated add-on records"}},"required":["id","eventId","externalId","email","customQrString","checkInToken","name","firstName","lastName","jobTitle","organization","city","country","areaCode","contactNo","approvalStatus","registrationStatus","suspendStatus","createdAt","updatedAt","addOns"],"description":"Attendee row — scoped by organizer_api_token (Org or Event, not user). Token permissions: read allows GET, write allows all. See info.description for full token scope."}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}},"patch":{"tags":["attendees"],"summary":"Update attendee","description":"Update attendee — identified by either attendeeId (this route) or externalId (via the by-external route); one of the two is required. Supply externalId/customQrString/email etc. checkInToken ignored (read-only). Requires write permission.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event UUID"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Attendee UUID"},"required":true,"name":"attendeeId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"externalId":{"type":"string","maxLength":100,"example":"ext_123"},"email":{"type":"string","maxLength":254,"format":"email","example":"alex_new@acme.com"},"customQrString":{"type":"string","maxLength":500,"example":"UPDATED-QR-999"},"name":{"type":"string","maxLength":500,"example":"Alex Updated"},"firstName":{"type":"string","maxLength":500,"example":"Alex"},"lastName":{"type":"string","maxLength":500,"example":"Chen"},"jobTitle":{"type":"string","maxLength":500,"example":"Engineer"},"organization":{"type":"string","maxLength":500,"example":"Acme Inc"},"city":{"type":"string","maxLength":50,"example":"Hong Kong"},"country":{"type":"string","maxLength":50,"example":"HK"},"areaCode":{"type":"string","maxLength":50,"example":"852"},"contactNo":{"type":"string","maxLength":50,"example":"98765432"},"customFields":{"type":"object","additionalProperties":{},"description":"Custom fields merge — provided keys are merged into existing, wrapped as {value}. Unknown field ids → 400. Use null value to delete a key.","example":{"7180eec9-e955-462a-bfd2-ed75d37169d2":{"value":"Patched"}}},"roleTags":{"type":"array","items":{"type":"string"},"description":"Replaces visible role tags. Defaults to [\"visitor\"] on create when omitted; omitted on PATCH leaves tags unchanged.","example":["visitor"]}},"description":"Partial update — at least one field, checkInToken ignored (read-only). customFields are merged, not replaced."}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Attendee ID, UUID","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"eventId":{"type":"string","format":"uuid","description":"Event ID","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"externalId":{"type":["string","null"],"description":"Provider external ID, scoped by event (UNIQUE(event_id, externalId))","example":"ext_123"},"email":{"type":["string","null"],"description":"Contact email (maps to invitation_email, optional)","example":"alex@acme.com"},"customQrString":{"type":["string","null"],"description":"Custom QR string (maps to customCheckInToken, writable)","example":"WELCOME-2026-QR-A1B2C3"},"checkInToken":{"type":["string","null"],"description":"System check-in token, read-only (from check_in_token, ignored on write)","example":"ci_9f3k2a1b"},"name":{"type":["string","null"],"example":"Alex Chen"},"firstName":{"type":["string","null"],"example":"Alex"},"lastName":{"type":["string","null"],"example":"Chen"},"jobTitle":{"type":["string","null"],"example":"Engineer"},"organization":{"type":["string","null"],"example":"Acme Inc"},"city":{"type":["string","null"],"example":"Hong Kong"},"country":{"type":["string","null"],"example":"HK"},"areaCode":{"type":["string","null"],"example":"852"},"contactNo":{"type":["string","null"],"example":"98765432"},"roleTags":{"example":["visitor"]},"customFields":{"example":{}},"approvalStatus":{"type":["string","null"],"example":"waitlisted"},"registrationStatus":{"type":["string","null"],"example":"pending"},"suspendStatus":{"type":"string","example":"none"},"createdAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.429Z"},"updatedAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.429Z"},"addOns":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Add-on row ID","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"addOnId":{"type":["string","null"],"description":"FK to ticket_class_add_on","example":"473ed8f7-deae-4bee-bb32-eafd860aebe4"},"qty":{"type":"integer","description":"Quantity","example":1},"selectionStatus":{"type":["string","null"],"description":"Selection status (regular/waitlisted/confirmed)","example":"waitlisted"},"confirmedAt":{"type":["string","null"],"description":"Confirmation timestamp","example":null},"createdAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.518Z"},"updatedAt":{"type":["string","null"],"example":"2026-08-17T09:36:09.620Z"}},"required":["id","addOnId","qty","selectionStatus","confirmedAt","createdAt","updatedAt"],"description":"Attendee add-on record"},"description":"Associated add-on records"}},"required":["id","eventId","externalId","email","customQrString","checkInToken","name","firstName","lastName","jobTitle","organization","city","country","areaCode","contactNo","approvalStatus","registrationStatus","suspendStatus","createdAt","updatedAt","addOns"],"description":"Attendee row — scoped by organizer_api_token (Org or Event, not user). Token permissions: read allows GET, write allows all. See info.description for full token scope."}}}},"400":{"description":"Validation","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}}},"/v2/events/{eventId}/attendees/by-external/{externalId}":{"get":{"tags":["attendees"],"summary":"Get attendee by externalId","description":"Get attendee by provider externalId scoped by event — use after creating with externalId (ext_123). Token must be scoped to event/org.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event UUID"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"string","description":"Provider external ID, scoped by event","example":"ext_123"},"required":true,"name":"externalId","in":"path"}],"responses":{"200":{"description":"Attendee","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Attendee ID, UUID","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"eventId":{"type":"string","format":"uuid","description":"Event ID","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"externalId":{"type":["string","null"],"description":"Provider external ID, scoped by event (UNIQUE(event_id, externalId))","example":"ext_123"},"email":{"type":["string","null"],"description":"Contact email (maps to invitation_email, optional)","example":"alex@acme.com"},"customQrString":{"type":["string","null"],"description":"Custom QR string (maps to customCheckInToken, writable)","example":"WELCOME-2026-QR-A1B2C3"},"checkInToken":{"type":["string","null"],"description":"System check-in token, read-only (from check_in_token, ignored on write)","example":"ci_9f3k2a1b"},"name":{"type":["string","null"],"example":"Alex Chen"},"firstName":{"type":["string","null"],"example":"Alex"},"lastName":{"type":["string","null"],"example":"Chen"},"jobTitle":{"type":["string","null"],"example":"Engineer"},"organization":{"type":["string","null"],"example":"Acme Inc"},"city":{"type":["string","null"],"example":"Hong Kong"},"country":{"type":["string","null"],"example":"HK"},"areaCode":{"type":["string","null"],"example":"852"},"contactNo":{"type":["string","null"],"example":"98765432"},"roleTags":{"example":["visitor"]},"customFields":{"example":{}},"approvalStatus":{"type":["string","null"],"example":"waitlisted"},"registrationStatus":{"type":["string","null"],"example":"pending"},"suspendStatus":{"type":"string","example":"none"},"createdAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.429Z"},"updatedAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.429Z"},"addOns":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Add-on row ID","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"addOnId":{"type":["string","null"],"description":"FK to ticket_class_add_on","example":"473ed8f7-deae-4bee-bb32-eafd860aebe4"},"qty":{"type":"integer","description":"Quantity","example":1},"selectionStatus":{"type":["string","null"],"description":"Selection status (regular/waitlisted/confirmed)","example":"waitlisted"},"confirmedAt":{"type":["string","null"],"description":"Confirmation timestamp","example":null},"createdAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.518Z"},"updatedAt":{"type":["string","null"],"example":"2026-08-17T09:36:09.620Z"}},"required":["id","addOnId","qty","selectionStatus","confirmedAt","createdAt","updatedAt"],"description":"Attendee add-on record"},"description":"Associated add-on records"}},"required":["id","eventId","externalId","email","customQrString","checkInToken","name","firstName","lastName","jobTitle","organization","city","country","areaCode","contactNo","approvalStatus","registrationStatus","suspendStatus","createdAt","updatedAt","addOns"],"description":"Attendee row — scoped by organizer_api_token (Org or Event, not user). Token permissions: read allows GET, write allows all. See info.description for full token scope."}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}},"patch":{"tags":["attendees"],"summary":"Update attendee by externalId","description":"Update attendee by externalId — identified by either externalId (this route) or attendeeId (via the by-attendee-id route); one of the two is required. Idempotent. Supply customQrString/email etc. checkInToken ignored. Use after creation with externalId.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event UUID"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"string","description":"Provider external ID, scoped by event","example":"ext_123"},"required":true,"name":"externalId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"externalId":{"type":"string","maxLength":100,"example":"ext_123"},"email":{"type":"string","maxLength":254,"format":"email","example":"alex_new@acme.com"},"customQrString":{"type":"string","maxLength":500,"example":"UPDATED-QR-999"},"name":{"type":"string","maxLength":500,"example":"Alex Updated"},"firstName":{"type":"string","maxLength":500,"example":"Alex"},"lastName":{"type":"string","maxLength":500,"example":"Chen"},"jobTitle":{"type":"string","maxLength":500,"example":"Engineer"},"organization":{"type":"string","maxLength":500,"example":"Acme Inc"},"city":{"type":"string","maxLength":50,"example":"Hong Kong"},"country":{"type":"string","maxLength":50,"example":"HK"},"areaCode":{"type":"string","maxLength":50,"example":"852"},"contactNo":{"type":"string","maxLength":50,"example":"98765432"},"customFields":{"type":"object","additionalProperties":{},"description":"Custom fields merge — provided keys are merged into existing, wrapped as {value}. Unknown field ids → 400. Use null value to delete a key.","example":{"7180eec9-e955-462a-bfd2-ed75d37169d2":{"value":"Patched"}}},"roleTags":{"type":"array","items":{"type":"string"},"description":"Replaces visible role tags. Defaults to [\"visitor\"] on create when omitted; omitted on PATCH leaves tags unchanged.","example":["visitor"]}},"description":"Partial update — at least one field, checkInToken ignored (read-only). customFields are merged, not replaced."}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Attendee ID, UUID","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"eventId":{"type":"string","format":"uuid","description":"Event ID","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"externalId":{"type":["string","null"],"description":"Provider external ID, scoped by event (UNIQUE(event_id, externalId))","example":"ext_123"},"email":{"type":["string","null"],"description":"Contact email (maps to invitation_email, optional)","example":"alex@acme.com"},"customQrString":{"type":["string","null"],"description":"Custom QR string (maps to customCheckInToken, writable)","example":"WELCOME-2026-QR-A1B2C3"},"checkInToken":{"type":["string","null"],"description":"System check-in token, read-only (from check_in_token, ignored on write)","example":"ci_9f3k2a1b"},"name":{"type":["string","null"],"example":"Alex Chen"},"firstName":{"type":["string","null"],"example":"Alex"},"lastName":{"type":["string","null"],"example":"Chen"},"jobTitle":{"type":["string","null"],"example":"Engineer"},"organization":{"type":["string","null"],"example":"Acme Inc"},"city":{"type":["string","null"],"example":"Hong Kong"},"country":{"type":["string","null"],"example":"HK"},"areaCode":{"type":["string","null"],"example":"852"},"contactNo":{"type":["string","null"],"example":"98765432"},"roleTags":{"example":["visitor"]},"customFields":{"example":{}},"approvalStatus":{"type":["string","null"],"example":"waitlisted"},"registrationStatus":{"type":["string","null"],"example":"pending"},"suspendStatus":{"type":"string","example":"none"},"createdAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.429Z"},"updatedAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.429Z"},"addOns":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Add-on row ID","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"addOnId":{"type":["string","null"],"description":"FK to ticket_class_add_on","example":"473ed8f7-deae-4bee-bb32-eafd860aebe4"},"qty":{"type":"integer","description":"Quantity","example":1},"selectionStatus":{"type":["string","null"],"description":"Selection status (regular/waitlisted/confirmed)","example":"waitlisted"},"confirmedAt":{"type":["string","null"],"description":"Confirmation timestamp","example":null},"createdAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.518Z"},"updatedAt":{"type":["string","null"],"example":"2026-08-17T09:36:09.620Z"}},"required":["id","addOnId","qty","selectionStatus","confirmedAt","createdAt","updatedAt"],"description":"Attendee add-on record"},"description":"Associated add-on records"}},"required":["id","eventId","externalId","email","customQrString","checkInToken","name","firstName","lastName","jobTitle","organization","city","country","areaCode","contactNo","approvalStatus","registrationStatus","suspendStatus","createdAt","updatedAt","addOns"],"description":"Attendee row — scoped by organizer_api_token (Org or Event, not user). Token permissions: read allows GET, write allows all. See info.description for full token scope."}}}},"400":{"description":"Validation","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}}},"/v2/events/{eventId}/attendees/bulk-upsert":{"post":{"tags":["attendees"],"summary":"Bulk upsert attendees","description":"Bulk upsert 1-50 attendees — upsert key externalId if present else email (no unique on email, app-level fallback). Each item independent, partial success allowed. Requires organizer_api_token write, org/event scoped. Fits bodyLimit 1MB and rateLimit 60/60s. Per-item autonomous, idempotent via UNIQUE(event_id, externalId).","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event UUID"},"required":true,"name":"eventId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"attendees":{"type":"array","items":{"type":"object","properties":{"externalId":{"type":"string","maxLength":100,"example":"ext_123"},"email":{"type":"string","maxLength":254,"format":"email","example":"alex@acme.com"},"customQrString":{"type":"string","maxLength":500,"example":"WELCOME-2026-QR-A1B2C3"},"name":{"type":"string","maxLength":500,"example":"Alex Chen"},"firstName":{"type":"string","maxLength":500,"example":"Alex"},"lastName":{"type":"string","maxLength":500,"example":"Chen"},"customFields":{"type":"object","additionalProperties":{},"description":"Wrapped {value: ...} keyed by custom-field id; flat values auto-wrapped.","example":{"7180eec3-e955-462a-bfd2-ed75d37169d2":{"value":"Hi"}}}},"description":"Attendee upsert item — externalId if present else email is key, no unique on email"},"minItems":1,"maxItems":50,"description":"Batch max 50, per-item autonomous, partial success allowed. Fits bodyLimit 1MB and rateLimit 60/60s."}},"required":["attendees"],"description":"Bulk upsert — upsert key externalId if present else email. Each item independent, idempotent. Requires organizer_api_token write, org/event scoped.","example":{"attendees":[{"externalId":"ext_123","email":"alex@acme.com","customQrString":"QR-A","name":"Alex"},{"email":"bob@acme.com","name":"Bob"}]}}}}},"responses":{"200":{"description":"Per-item results","content":{"application/json":{"schema":{"type":"object","properties":{"results":{"type":"array","items":{"type":"object","properties":{"index":{"type":"number","example":0},"attendeeId":{"type":["string","null"],"example":"a1b2c3d4-..."},"externalId":{"type":["string","null"],"example":"ext_123"},"status":{"type":"string","enum":["created","updated","failed"],"example":"created"},"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"],"example":{"code":"ERR_PUBLIC_API_VALIDATION_ERROR","message":"externalId or email required"}}},"required":["index","attendeeId","externalId","status"]}}},"required":["results"]}}}},"400":{"description":"Validation","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}}},"/v2/events/{eventId}/promo-codes":{"get":{"tags":["promo codes"],"summary":"List promo codes","description":"List promo codes for an event — paginated, filterable by codeName substring and isActive. Requires organizer_api_token read, org or event scoped. Excludes soft-deleted rows.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"example":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0,"default":0,"example":0},"required":false,"name":"offset","in":"query"},{"schema":{"type":"string","description":"Filter by codeName substring (case-insensitive)","example":"SUMMER"},"required":false,"name":"q","in":"query"},{"schema":{"type":["boolean","null"],"example":true},"required":false,"name":"isActive","in":"query"}],"responses":{"200":{"description":"Paginated promo codes","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Promo code ID, UUID","example":"b2e7c3a1-4f5e-4b2a-9c1d-1234567890ab"},"eventId":{"type":["string","null"],"format":"uuid","description":"Event ID","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"codeName":{"type":["string","null"],"description":"Human-readable code name, unique per event","example":"SUMMER2026"},"remarks":{"type":["string","null"],"description":"Internal remarks","example":"Early bird"},"discountType":{"type":["string","null"],"description":"subtractByPercentage | subtractByFixedAmount | chargeByFixedAmount","example":"subtractByPercentage"},"discount":{"type":["string","null"],"description":"Fixed amount string, for subtractByFixedAmount/chargeByFixedAmount","example":"100.00"},"discountRate":{"type":["string","null"],"description":"Percentage string 0-100, for subtractByPercentage","example":"20.00"},"quota":{"type":["number","null"],"description":"Max redemptions, null = unlimited","example":100},"isActive":{"type":["boolean","null"],"description":"Whether code is currently active","example":true},"isAppliedToAllTicketClasses":{"type":["boolean","null"],"description":"If true, applies to all ticket classes; otherwise ticketClassIds lists scope","example":true},"ticketClassIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Ticket class IDs this code applies to (empty when isAppliedToAllTicketClasses=true)","example":["550e8400-e29b-41d4-a716-446655440011"]},"startDate":{"type":["string","null"],"description":"Start time ISO","example":"2026-03-01T00:00:00.000Z"},"endDate":{"type":["string","null"],"description":"End time ISO, null = no expiry","example":"2026-04-01T00:00:00.000Z"},"timezone":{"type":["string","null"],"description":"IANA timezone for start/end display","example":"Asia/Hong_Kong"},"createdAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"},"updatedAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"}},"required":["id","eventId","codeName","remarks","discountType","discount","discountRate","quota","isActive","isAppliedToAllTicketClasses","ticketClassIds","startDate","endDate","timezone","createdAt","updatedAt"],"description":"Promo code row — scoped by organizer_api_token (Org or Event, not user). Discount affects ticket pricing; PATCH is human-confirmed via MCP."}},"total":{"type":"number","example":10},"limit":{"type":"number","example":50},"offset":{"type":"number","example":0}},"required":["data","total","limit","offset"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Event not found","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}},"post":{"tags":["promo codes"],"summary":"Create promo code","description":"Create promo code — event-create allowed for event-scoped token (token.eventId must match). Validates discount range, price, and ticketClass ownership. Requires write.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"required":true,"name":"eventId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"codeName":{"type":"string","minLength":1,"maxLength":100,"description":"Unique per event, required","example":"SUMMER2026"},"remarks":{"type":["string","null"],"maxLength":500,"example":"Early bird"},"discountType":{"type":"string","enum":["subtractByPercentage","subtractByFixedAmount","chargeByFixedAmount"],"description":"Discount type, required","example":"subtractByPercentage"},"discount":{"type":["string","null"],"description":"Fixed amount string; required for subtractByFixedAmount/chargeByFixedAmount","example":"50.00"},"discountRate":{"type":["string","null"],"description":"Percentage 0-100 string; for subtractByPercentage","example":"20.00"},"quota":{"type":["integer","null"],"minimum":1,"description":"Max redemptions; null = unlimited","example":100},"isActive":{"type":"boolean","description":"Active flag, defaults to true","example":true},"isAppliedToAllTicketClasses":{"type":"boolean","description":"If true, ticketClassIds is ignored","example":true},"ticketClassIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Ticket class IDs when isAppliedToAllTicketClasses=false","example":["550e8400-e29b-41d4-a716-446655440011"]},"startDate":{"type":"string","description":"ISO start, required","example":"2026-03-01T00:00:00.000Z"},"endDate":{"type":["string","null"],"description":"ISO end, null = no expiry","example":"2026-04-01T00:00:00.000Z"},"timezone":{"type":"string","minLength":1,"description":"IANA timezone, required","example":"Asia/Hong_Kong"}},"required":["codeName","discountType","isAppliedToAllTicketClasses","startDate","timezone"],"description":"Create promo code — requires organizer_api_token write, org or event scoped (event-create allowed). Validates discount range and ticket class scope.","example":{"codeName":"SUMMER2026","discountType":"subtractByPercentage","discountRate":"20.00","quota":100,"isAppliedToAllTicketClasses":true,"startDate":"2026-03-01T00:00:00.000Z","endDate":"2026-04-01T00:00:00.000Z","timezone":"Asia/Hong_Kong"}}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Promo code ID, UUID","example":"b2e7c3a1-4f5e-4b2a-9c1d-1234567890ab"},"eventId":{"type":["string","null"],"format":"uuid","description":"Event ID","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"codeName":{"type":["string","null"],"description":"Human-readable code name, unique per event","example":"SUMMER2026"},"remarks":{"type":["string","null"],"description":"Internal remarks","example":"Early bird"},"discountType":{"type":["string","null"],"description":"subtractByPercentage | subtractByFixedAmount | chargeByFixedAmount","example":"subtractByPercentage"},"discount":{"type":["string","null"],"description":"Fixed amount string, for subtractByFixedAmount/chargeByFixedAmount","example":"100.00"},"discountRate":{"type":["string","null"],"description":"Percentage string 0-100, for subtractByPercentage","example":"20.00"},"quota":{"type":["number","null"],"description":"Max redemptions, null = unlimited","example":100},"isActive":{"type":["boolean","null"],"description":"Whether code is currently active","example":true},"isAppliedToAllTicketClasses":{"type":["boolean","null"],"description":"If true, applies to all ticket classes; otherwise ticketClassIds lists scope","example":true},"ticketClassIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Ticket class IDs this code applies to (empty when isAppliedToAllTicketClasses=true)","example":["550e8400-e29b-41d4-a716-446655440011"]},"startDate":{"type":["string","null"],"description":"Start time ISO","example":"2026-03-01T00:00:00.000Z"},"endDate":{"type":["string","null"],"description":"End time ISO, null = no expiry","example":"2026-04-01T00:00:00.000Z"},"timezone":{"type":["string","null"],"description":"IANA timezone for start/end display","example":"Asia/Hong_Kong"},"createdAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"},"updatedAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"}},"required":["id","eventId","codeName","remarks","discountType","discount","discountRate","quota","isActive","isAppliedToAllTicketClasses","ticketClassIds","startDate","endDate","timezone","createdAt","updatedAt"],"description":"Promo code row — scoped by organizer_api_token (Org or Event, not user). Discount affects ticket pricing; PATCH is human-confirmed via MCP."}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Event or ticket class not found","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}}},"/v2/events/{eventId}/promo-codes/{promoCodeId}":{"get":{"tags":["promo codes"],"summary":"Get promo code","description":"Get single promo code by ID, scoped to event. Requires read.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"string","format":"uuid","example":"b2e7c3a1-4f5e-4b2a-9c1d-1234567890ab"},"required":true,"name":"promoCodeId","in":"path"}],"responses":{"200":{"description":"Promo code","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Promo code ID, UUID","example":"b2e7c3a1-4f5e-4b2a-9c1d-1234567890ab"},"eventId":{"type":["string","null"],"format":"uuid","description":"Event ID","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"codeName":{"type":["string","null"],"description":"Human-readable code name, unique per event","example":"SUMMER2026"},"remarks":{"type":["string","null"],"description":"Internal remarks","example":"Early bird"},"discountType":{"type":["string","null"],"description":"subtractByPercentage | subtractByFixedAmount | chargeByFixedAmount","example":"subtractByPercentage"},"discount":{"type":["string","null"],"description":"Fixed amount string, for subtractByFixedAmount/chargeByFixedAmount","example":"100.00"},"discountRate":{"type":["string","null"],"description":"Percentage string 0-100, for subtractByPercentage","example":"20.00"},"quota":{"type":["number","null"],"description":"Max redemptions, null = unlimited","example":100},"isActive":{"type":["boolean","null"],"description":"Whether code is currently active","example":true},"isAppliedToAllTicketClasses":{"type":["boolean","null"],"description":"If true, applies to all ticket classes; otherwise ticketClassIds lists scope","example":true},"ticketClassIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Ticket class IDs this code applies to (empty when isAppliedToAllTicketClasses=true)","example":["550e8400-e29b-41d4-a716-446655440011"]},"startDate":{"type":["string","null"],"description":"Start time ISO","example":"2026-03-01T00:00:00.000Z"},"endDate":{"type":["string","null"],"description":"End time ISO, null = no expiry","example":"2026-04-01T00:00:00.000Z"},"timezone":{"type":["string","null"],"description":"IANA timezone for start/end display","example":"Asia/Hong_Kong"},"createdAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"},"updatedAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"}},"required":["id","eventId","codeName","remarks","discountType","discount","discountRate","quota","isActive","isAppliedToAllTicketClasses","ticketClassIds","startDate","endDate","timezone","createdAt","updatedAt"],"description":"Promo code row — scoped by organizer_api_token (Org or Event, not user). Discount affects ticket pricing; PATCH is human-confirmed via MCP."}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}},"patch":{"tags":["promo codes"],"summary":"Patch promo code","description":"Partial update — at least one field. codeName rename checks duplicate per event. Validates discount/price/ticket scope. Requires write and human confirmation when via MCP (confirm:true). Best-effort duplicate race.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"string","format":"uuid","example":"b2e7c3a1-4f5e-4b2a-9c1d-1234567890ab"},"required":true,"name":"promoCodeId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"codeName":{"type":"string","minLength":1,"maxLength":100,"example":"SUMMER2026-V2"},"remarks":{"type":["string","null"],"maxLength":500,"example":"Updated remarks"},"discountType":{"type":"string","enum":["subtractByPercentage","subtractByFixedAmount","chargeByFixedAmount"],"example":"subtractByFixedAmount"},"discount":{"type":["string","null"],"example":"50.00"},"discountRate":{"type":["string","null"],"example":"15.00"},"quota":{"type":["integer","null"],"minimum":1,"example":200},"isActive":{"type":"boolean","example":false},"isAppliedToAllTicketClasses":{"type":"boolean","example":false},"ticketClassIds":{"type":"array","items":{"type":"string","format":"uuid"},"example":["550e8400-e29b-41d4-a716-446655440011"]},"startDate":{"type":"string","example":"2026-03-02T00:00:00.000Z"},"endDate":{"type":["string","null"],"example":"2026-04-01T00:00:00.000Z"},"timezone":{"type":"string","example":"Asia/Hong_Kong"}},"description":"Partial update — at least one field required. codeName rename checks duplicate. Requires human confirmation via MCP confirm:true."}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Promo code ID, UUID","example":"b2e7c3a1-4f5e-4b2a-9c1d-1234567890ab"},"eventId":{"type":["string","null"],"format":"uuid","description":"Event ID","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"codeName":{"type":["string","null"],"description":"Human-readable code name, unique per event","example":"SUMMER2026"},"remarks":{"type":["string","null"],"description":"Internal remarks","example":"Early bird"},"discountType":{"type":["string","null"],"description":"subtractByPercentage | subtractByFixedAmount | chargeByFixedAmount","example":"subtractByPercentage"},"discount":{"type":["string","null"],"description":"Fixed amount string, for subtractByFixedAmount/chargeByFixedAmount","example":"100.00"},"discountRate":{"type":["string","null"],"description":"Percentage string 0-100, for subtractByPercentage","example":"20.00"},"quota":{"type":["number","null"],"description":"Max redemptions, null = unlimited","example":100},"isActive":{"type":["boolean","null"],"description":"Whether code is currently active","example":true},"isAppliedToAllTicketClasses":{"type":["boolean","null"],"description":"If true, applies to all ticket classes; otherwise ticketClassIds lists scope","example":true},"ticketClassIds":{"type":"array","items":{"type":"string","format":"uuid"},"description":"Ticket class IDs this code applies to (empty when isAppliedToAllTicketClasses=true)","example":["550e8400-e29b-41d4-a716-446655440011"]},"startDate":{"type":["string","null"],"description":"Start time ISO","example":"2026-03-01T00:00:00.000Z"},"endDate":{"type":["string","null"],"description":"End time ISO, null = no expiry","example":"2026-04-01T00:00:00.000Z"},"timezone":{"type":["string","null"],"description":"IANA timezone for start/end display","example":"Asia/Hong_Kong"},"createdAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"},"updatedAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"}},"required":["id","eventId","codeName","remarks","discountType","discount","discountRate","quota","isActive","isAppliedToAllTicketClasses","ticketClassIds","startDate","endDate","timezone","createdAt","updatedAt"],"description":"Promo code row — scoped by organizer_api_token (Org or Event, not user). Discount affects ticket pricing; PATCH is human-confirmed via MCP."}}}},"400":{"description":"Validation","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}},"delete":{"tags":["promo codes"],"summary":"Delete promo code","description":"Soft delete — sets deletedAt, keeps assignment rows. Requires write.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"string","format":"uuid","example":"b2e7c3a1-4f5e-4b2a-9c1d-1234567890ab"},"required":true,"name":"promoCodeId","in":"path"}],"responses":{"204":{"description":"Deleted"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}}},"/v2/events/{eventId}/custom-fields":{"get":{"tags":["custom fields"],"summary":"List custom fields","description":"List custom field definitions for an event — paginated, filterable by label substring and type. Excludes soft-deleted (trashed) by default; pass includeDeleted=true to include. Requires organizer_api_token read, org or event scoped.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"example":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0,"default":0,"example":0},"required":false,"name":"offset","in":"query"},{"schema":{"type":"string","description":"Filter by label substring (case-insensitive)","example":"Dietary"},"required":false,"name":"q","in":"query"},{"schema":{"type":"string","description":"Filter by type: text, textArea, multipleText, radioGroup, checkboxGroup, dropdown, singleCheckbox, fileUpload, datePicker, timePicker, phoneField","example":"text"},"required":false,"name":"type","in":"query"},{"schema":{"type":["boolean","null"],"description":"Include soft-deleted (trashed) fields","example":false},"required":false,"name":"includeDeleted","in":"query"}],"responses":{"200":{"description":"Paginated custom fields","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Custom field ID, UUID","example":"a1b2c3d4-1234-4abc-9def-1234567890ab"},"eventId":{"type":"string","description":"Event ID","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"label":{"type":"string","description":"Field label","example":"Dietary Requirements"},"type":{"type":"string","description":"text | textArea | multipleText | radioGroup | checkboxGroup | dropdown | singleCheckbox | fileUpload | datePicker | timePicker | phoneField","example":"text"},"isRequired":{"type":"boolean","description":"Whether field is required","example":false},"isEditableByAttendee":{"type":["boolean","null"],"description":"Whether attendee can edit after submission","example":true},"config":{"description":"Type-specific config (e.g. options for radioGroup/dropdown)","example":{"options":[{"id":"opt1","label":"Vegan"}]}},"contentI18n":{"description":"Translations","example":{"en":{"label":"Dietary Requirements"}}},"isDeleted":{"type":"boolean","description":"Soft-deleted (trashed), recoverable via PATCH deletedAt:null","example":false},"deletedAt":{"type":["string","null"],"description":"Soft delete timestamp, null if active","example":null},"createdAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"},"updatedAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"}},"required":["id","eventId","label","type","isRequired","isEditableByAttendee","isDeleted","deletedAt","createdAt","updatedAt"],"description":"Custom field row — per-event definition stored in question_library.questions, soft-deleted (trash, recoverable). Scoped by organizer_api_token (Org or Event, not user)."}},"total":{"type":"number","example":10},"limit":{"type":"number","example":50},"offset":{"type":"number","example":0}},"required":["data","total","limit","offset"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Event not found","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}},"post":{"tags":["custom fields"],"summary":"Create custom field","description":"Create custom field — event-create allowed for event-scoped token (token.eventId must match). Requires write.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"required":true,"name":"eventId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":200,"description":"Field label, required 1-200","example":"Dietary Requirements"},"type":{"type":"string","enum":["text","textArea","multipleText","radioGroup","checkboxGroup","dropdown","singleCheckbox","fileUpload","datePicker","timePicker","phoneField"],"description":"Field type, required","example":"text"},"isRequired":{"type":"boolean","description":"Required flag, defaults false","example":false},"isEditableByAttendee":{"type":"boolean","example":true},"config":{"description":"Type-specific config, e.g. options for choice types","example":{"options":[{"id":"opt1","label":"Vegan"}]}},"contentI18n":{"example":{"en":{"label":"Dietary"}}}},"required":["label","type"],"description":"Create custom field — requires organizer_api_token write, org or event scoped. Type must be one of the supported CustomQuestionType values.","example":{"label":"Dietary Requirements","type":"text","isRequired":false}}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Custom field ID, UUID","example":"a1b2c3d4-1234-4abc-9def-1234567890ab"},"eventId":{"type":"string","description":"Event ID","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"label":{"type":"string","description":"Field label","example":"Dietary Requirements"},"type":{"type":"string","description":"text | textArea | multipleText | radioGroup | checkboxGroup | dropdown | singleCheckbox | fileUpload | datePicker | timePicker | phoneField","example":"text"},"isRequired":{"type":"boolean","description":"Whether field is required","example":false},"isEditableByAttendee":{"type":["boolean","null"],"description":"Whether attendee can edit after submission","example":true},"config":{"description":"Type-specific config (e.g. options for radioGroup/dropdown)","example":{"options":[{"id":"opt1","label":"Vegan"}]}},"contentI18n":{"description":"Translations","example":{"en":{"label":"Dietary Requirements"}}},"isDeleted":{"type":"boolean","description":"Soft-deleted (trashed), recoverable via PATCH deletedAt:null","example":false},"deletedAt":{"type":["string","null"],"description":"Soft delete timestamp, null if active","example":null},"createdAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"},"updatedAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"}},"required":["id","eventId","label","type","isRequired","isEditableByAttendee","isDeleted","deletedAt","createdAt","updatedAt"],"description":"Custom field row — per-event definition stored in question_library.questions, soft-deleted (trash, recoverable). Scoped by organizer_api_token (Org or Event, not user)."}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Event not found","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}}},"/v2/events/{eventId}/custom-fields/{fieldId}":{"get":{"tags":["custom fields"],"summary":"Get custom field","description":"Get single custom field by ID, scoped to event. Soft-deleted (trashed) returns 404 unless includeDeleted. Requires read.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"string","format":"uuid","example":"a1b2c3d4-1234-4abc-9def-1234567890ab"},"required":true,"name":"fieldId","in":"path"},{"schema":{"type":["boolean","null"],"example":false},"required":false,"name":"includeDeleted","in":"query"}],"responses":{"200":{"description":"Custom field","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Custom field ID, UUID","example":"a1b2c3d4-1234-4abc-9def-1234567890ab"},"eventId":{"type":"string","description":"Event ID","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"label":{"type":"string","description":"Field label","example":"Dietary Requirements"},"type":{"type":"string","description":"text | textArea | multipleText | radioGroup | checkboxGroup | dropdown | singleCheckbox | fileUpload | datePicker | timePicker | phoneField","example":"text"},"isRequired":{"type":"boolean","description":"Whether field is required","example":false},"isEditableByAttendee":{"type":["boolean","null"],"description":"Whether attendee can edit after submission","example":true},"config":{"description":"Type-specific config (e.g. options for radioGroup/dropdown)","example":{"options":[{"id":"opt1","label":"Vegan"}]}},"contentI18n":{"description":"Translations","example":{"en":{"label":"Dietary Requirements"}}},"isDeleted":{"type":"boolean","description":"Soft-deleted (trashed), recoverable via PATCH deletedAt:null","example":false},"deletedAt":{"type":["string","null"],"description":"Soft delete timestamp, null if active","example":null},"createdAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"},"updatedAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"}},"required":["id","eventId","label","type","isRequired","isEditableByAttendee","isDeleted","deletedAt","createdAt","updatedAt"],"description":"Custom field row — per-event definition stored in question_library.questions, soft-deleted (trash, recoverable). Scoped by organizer_api_token (Org or Event, not user)."}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}},"patch":{"tags":["custom fields"],"summary":"Patch custom field","description":"Partial update — at least one field. Restore soft-deleted via deletedAt:null. Requires write and human confirmation when via MCP (confirm:true).","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"string","format":"uuid","example":"a1b2c3d4-1234-4abc-9def-1234567890ab"},"required":true,"name":"fieldId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"label":{"type":"string","minLength":1,"maxLength":200,"example":"Dietary — Updated"},"type":{"type":"string","enum":["text","textArea","multipleText","radioGroup","checkboxGroup","dropdown","singleCheckbox","fileUpload","datePicker","timePicker","phoneField"],"example":"textArea"},"isRequired":{"type":"boolean","example":true},"isEditableByAttendee":{"type":"boolean","example":false},"config":{"example":{"options":[{"id":"opt1","label":"Vegan"}]}},"contentI18n":{"example":{"en":{"label":"Dietary"}}},"deletedAt":{"type":["string","null"],"description":"Set null to restore soft-deleted field, ISO string to trash","example":null}},"description":"Partial update — at least one field required. Restore soft-deleted via deletedAt:null. Trash is soft, recoverable."}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","description":"Custom field ID, UUID","example":"a1b2c3d4-1234-4abc-9def-1234567890ab"},"eventId":{"type":"string","description":"Event ID","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"label":{"type":"string","description":"Field label","example":"Dietary Requirements"},"type":{"type":"string","description":"text | textArea | multipleText | radioGroup | checkboxGroup | dropdown | singleCheckbox | fileUpload | datePicker | timePicker | phoneField","example":"text"},"isRequired":{"type":"boolean","description":"Whether field is required","example":false},"isEditableByAttendee":{"type":["boolean","null"],"description":"Whether attendee can edit after submission","example":true},"config":{"description":"Type-specific config (e.g. options for radioGroup/dropdown)","example":{"options":[{"id":"opt1","label":"Vegan"}]}},"contentI18n":{"description":"Translations","example":{"en":{"label":"Dietary Requirements"}}},"isDeleted":{"type":"boolean","description":"Soft-deleted (trashed), recoverable via PATCH deletedAt:null","example":false},"deletedAt":{"type":["string","null"],"description":"Soft delete timestamp, null if active","example":null},"createdAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"},"updatedAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"}},"required":["id","eventId","label","type","isRequired","isEditableByAttendee","isDeleted","deletedAt","createdAt","updatedAt"],"description":"Custom field row — per-event definition stored in question_library.questions, soft-deleted (trash, recoverable). Scoped by organizer_api_token (Org or Event, not user)."}}}},"400":{"description":"Validation","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}},"delete":{"tags":["custom fields"],"summary":"Delete custom field","description":"Soft delete (trash) — sets deletedAt, recoverable via PATCH deletedAt:null. Requires write.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"string","format":"uuid","example":"a1b2c3d4-1234-4abc-9def-1234567890ab"},"required":true,"name":"fieldId","in":"path"}],"responses":{"204":{"description":"Deleted (trashed)"},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}}},"/v2/events/{eventId}/check-in-sessions":{"get":{"tags":["check-ins"],"summary":"List check-in sessions","description":"Lists physical check-in sessions for an event (read-only via v2 — sessions are managed in the portal). Requires organizer_api_token read, org or event scoped.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event UUID"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Limit 1-100, default 50","example":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0,"default":0,"description":"Offset","example":0},"required":false,"name":"offset","in":"query"}],"responses":{"200":{"description":"Paginated sessions","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Physical check-in session ID","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"eventId":{"type":["string","null"],"description":"Event ID","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"name":{"type":["string","null"],"description":"Session name (from contentI18n)","example":"Morning Keynote Scan"},"startsAt":{"type":["string","null"],"example":"2026-03-01T01:00:00.000Z"},"endsAt":{"type":["string","null"],"example":"2026-03-01T03:00:00.000Z"},"isPermitAll":{"type":["boolean","null"],"example":true},"isCheckInAnywayEnabled":{"type":["boolean","null"],"example":false},"createdAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"},"updatedAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"}},"required":["id","eventId","name","startsAt","endsAt","isPermitAll","isCheckInAnywayEnabled","createdAt","updatedAt"],"description":"Physical check-in session (read-only via v2)"}},"total":{"type":"number","example":3},"limit":{"type":"number","example":50},"offset":{"type":"number","example":0}},"required":["data","total","limit","offset"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Event not found","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}}},"/v2/events/{eventId}/check-in-sessions/{sessionId}":{"get":{"tags":["check-ins"],"summary":"Get check-in session","description":"Get a single check-in session by ID, scoped to an event.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event UUID"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Session UUID"},"required":true,"name":"sessionId","in":"path"}],"responses":{"200":{"description":"Session","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Physical check-in session ID","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"eventId":{"type":["string","null"],"description":"Event ID","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"name":{"type":["string","null"],"description":"Session name (from contentI18n)","example":"Morning Keynote Scan"},"startsAt":{"type":["string","null"],"example":"2026-03-01T01:00:00.000Z"},"endsAt":{"type":["string","null"],"example":"2026-03-01T03:00:00.000Z"},"isPermitAll":{"type":["boolean","null"],"example":true},"isCheckInAnywayEnabled":{"type":["boolean","null"],"example":false},"createdAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"},"updatedAt":{"type":["string","null"],"example":"2026-01-15T02:00:00.000Z"}},"required":["id","eventId","name","startsAt","endsAt","isPermitAll","isCheckInAnywayEnabled","createdAt","updatedAt"],"description":"Physical check-in session (read-only via v2)"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}}},"/v2/events/{eventId}/check-ins":{"get":{"tags":["check-ins"],"summary":"List check-ins with datetime range","description":"Paginated check-in taps for an event. Filter by sessionId (one session) or receptionOnly (event-level taps, session null), attendeeId, direction, method, and checkedAt from/to (ISO-8601 inclusive, max 90-day window). sessionId and receptionOnly are mutually exclusive. Requires read.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event UUID"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":50,"description":"Limit 1-100, default 50","example":50},"required":false,"name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0,"default":0,"description":"Offset","example":0},"required":false,"name":"offset","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Filter to one session","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"required":false,"name":"sessionId","in":"query"},{"schema":{"type":["boolean","null"],"description":"When true, only reception (event-level, session null) taps","example":false},"required":false,"name":"receptionOnly","in":"query"},{"schema":{"type":"string","format":"uuid","description":"Filter to one attendee","example":"550e8400-e29b-41d4-a716-446655440002"},"required":false,"name":"attendeeId","in":"query"},{"schema":{"type":"string","enum":["in","out"],"description":"Tap direction","example":"in"},"required":false,"name":"direction","in":"query"},{"schema":{"type":"string","enum":["qr_code","search","walk_in"],"description":"Check-in method","example":"qr_code"},"required":false,"name":"method","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Inclusive lower bound on checkedAt (ISO-8601)","example":"2026-03-01T00:00:00.000Z"},"required":false,"name":"from","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Inclusive upper bound on checkedAt (ISO-8601)","example":"2026-03-01T12:00:00.000Z"},"required":false,"name":"to","in":"query"},{"schema":{"type":"string","enum":["asc","desc"],"description":"Sort by checkedAt (default: desc)","example":"desc"},"required":false,"name":"sortDirection","in":"query"}],"responses":{"200":{"description":"Paginated check-ins","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Check-in record ID","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"eventId":{"type":["string","null"],"example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"sessionId":{"type":["string","null"],"description":"Physical session ID, null = reception (event-level)","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"attendeeId":{"type":["string","null"],"example":"550e8400-e29b-41d4-a716-446655440002"},"checkedAt":{"type":["string","null"],"description":"Tap timestamp (the datetime-range filter field)","example":"2026-03-01T09:05:00.000Z"},"direction":{"type":["string","null"],"example":"in"},"method":{"type":["string","null"],"example":"qr_code"},"createdAt":{"type":["string","null"],"example":"2026-03-01T09:05:01.000Z"},"updatedAt":{"type":["string","null"],"example":"2026-03-01T09:05:01.000Z"}},"required":["id","eventId","sessionId","attendeeId","checkedAt","direction","method","createdAt","updatedAt"],"description":"Per-session attendee check-in record"}},"total":{"type":"number","example":120},"limit":{"type":"number","example":50},"offset":{"type":"number","example":0}},"required":["data","total","limit","offset"]}}}},"400":{"description":"Validation error (from after to, range over 90 days, sessionId + receptionOnly)","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}},"post":{"tags":["check-ins"],"summary":"Record a check-in tap","description":"Records a check-in tap (sessionId null/omitted = reception). Exact duplicates (event, attendee, session, checkedAt, direction) are ignored and returned with deduped:true. Requires write.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event UUID"},"required":true,"name":"eventId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sessionId":{"type":["string","null"],"format":"uuid","description":"Session ID, null or omitted = reception","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"attendeeId":{"type":"string","format":"uuid","description":"Attendee UUID","example":"550e8400-e29b-41d4-a716-446655440002"},"checkedAt":{"type":"string","format":"date-time","description":"Tap timestamp (ISO-8601)","example":"2026-03-01T00:00:00.000Z"},"direction":{"type":"string","enum":["in","out"],"description":"Tap direction","example":"in"},"method":{"type":"string","enum":["qr_code","search","walk_in"],"description":"Check-in method (default: qr_code)","example":"qr_code"}},"required":["attendeeId","checkedAt","direction"],"description":"Record a check-in tap — exact duplicates (event, attendee, session, checkedAt, direction) are ignored and returned with deduped:true."}}}},"responses":{"201":{"description":"Created (or deduped)","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Check-in record ID","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"eventId":{"type":["string","null"],"example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"sessionId":{"type":["string","null"],"description":"Physical session ID, null = reception (event-level)","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"attendeeId":{"type":["string","null"],"example":"550e8400-e29b-41d4-a716-446655440002"},"checkedAt":{"type":["string","null"],"description":"Tap timestamp (the datetime-range filter field)","example":"2026-03-01T09:05:00.000Z"},"direction":{"type":["string","null"],"example":"in"},"method":{"type":["string","null"],"example":"qr_code"},"createdAt":{"type":["string","null"],"example":"2026-03-01T09:05:01.000Z"},"updatedAt":{"type":["string","null"],"example":"2026-03-01T09:05:01.000Z"}},"required":["id","eventId","sessionId","attendeeId","checkedAt","direction","method","createdAt","updatedAt"],"description":"Per-session attendee check-in record"},"deduped":{"type":"boolean","description":"True when the exact tap already existed (event, attendee, session, checkedAt, direction) — no new row written","example":false}},"required":["data","deduped"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Session not found in this event","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}}},"/v2/events/{eventId}/check-ins/overview":{"get":{"tags":["check-ins"],"summary":"Per-session check-in counts","description":"Counts visible taps grouped by session (sessionId null = reception). Accepts the same checkedAt from/to range (max 90 days). Requires read.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event UUID"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"string","format":"date-time","description":"Inclusive lower bound on checkedAt (ISO-8601)","example":"2026-03-01T00:00:00.000Z"},"required":false,"name":"from","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Inclusive upper bound on checkedAt (ISO-8601)","example":"2026-03-01T12:00:00.000Z"},"required":false,"name":"to","in":"query"}],"responses":{"200":{"description":"Per-session counts","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"sessionId":{"type":["string","null"],"description":"Session ID, null = reception","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"count":{"type":"number","example":42}},"required":["sessionId","count"]}}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}}},"/v2/events/{eventId}/check-ins/timeline":{"get":{"tags":["check-ins"],"summary":"Attendee check-in timeline","description":"Chronological tap history for one attendee (ascending checkedAt), with optional from/to range. Requires read.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event UUID"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Attendee UUID","example":"550e8400-e29b-41d4-a716-446655440002"},"required":true,"name":"attendeeId","in":"query"},{"schema":{"type":"integer","minimum":1,"maximum":100,"default":100,"description":"Limit 1-100, default 100","example":100},"required":false,"name":"limit","in":"query"},{"schema":{"type":["integer","null"],"minimum":0,"default":0,"description":"Offset","example":0},"required":false,"name":"offset","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Inclusive lower bound on checkedAt (ISO-8601)","example":"2026-03-01T00:00:00.000Z"},"required":false,"name":"from","in":"query"},{"schema":{"type":"string","format":"date-time","description":"Inclusive upper bound on checkedAt (ISO-8601)","example":"2026-03-01T12:00:00.000Z"},"required":false,"name":"to","in":"query"}],"responses":{"200":{"description":"Paginated timeline","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Check-in record ID","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"eventId":{"type":["string","null"],"example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"sessionId":{"type":["string","null"],"description":"Physical session ID, null = reception (event-level)","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"attendeeId":{"type":["string","null"],"example":"550e8400-e29b-41d4-a716-446655440002"},"checkedAt":{"type":["string","null"],"description":"Tap timestamp (the datetime-range filter field)","example":"2026-03-01T09:05:00.000Z"},"direction":{"type":["string","null"],"example":"in"},"method":{"type":["string","null"],"example":"qr_code"},"createdAt":{"type":["string","null"],"example":"2026-03-01T09:05:01.000Z"},"updatedAt":{"type":["string","null"],"example":"2026-03-01T09:05:01.000Z"}},"required":["id","eventId","sessionId","attendeeId","checkedAt","direction","method","createdAt","updatedAt"],"description":"Per-session attendee check-in record"}},"total":{"type":"number","example":120},"limit":{"type":"number","example":50},"offset":{"type":"number","example":0}},"required":["data","total","limit","offset"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}}},"/v2/events/{eventId}/check-ins/{checkInId}":{"get":{"tags":["check-ins"],"summary":"Get check-in by ID","description":"Get a single check-in tap by ID, scoped to an event.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event UUID"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Check-in UUID"},"required":true,"name":"checkInId","in":"path"}],"responses":{"200":{"description":"Check-in","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Check-in record ID","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"eventId":{"type":["string","null"],"example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"sessionId":{"type":["string","null"],"description":"Physical session ID, null = reception (event-level)","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"attendeeId":{"type":["string","null"],"example":"550e8400-e29b-41d4-a716-446655440002"},"checkedAt":{"type":["string","null"],"description":"Tap timestamp (the datetime-range filter field)","example":"2026-03-01T09:05:00.000Z"},"direction":{"type":["string","null"],"example":"in"},"method":{"type":["string","null"],"example":"qr_code"},"createdAt":{"type":["string","null"],"example":"2026-03-01T09:05:01.000Z"},"updatedAt":{"type":["string","null"],"example":"2026-03-01T09:05:01.000Z"}},"required":["id","eventId","sessionId","attendeeId","checkedAt","direction","method","createdAt","updatedAt"],"description":"Per-session attendee check-in record"}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}},"patch":{"tags":["check-ins"],"summary":"Correct a check-in tap","description":"Partial correction of a tap (session, timestamp, direction, method). Requires write.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event UUID"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Check-in UUID"},"required":true,"name":"checkInId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sessionId":{"type":["string","null"],"format":"uuid","description":"Move tap to another session (null = reception)"},"checkedAt":{"type":"string","format":"date-time","description":"Corrected tap timestamp","example":"2026-03-01T00:00:00.000Z"},"direction":{"type":"string","enum":["in","out"],"description":"Corrected direction"},"method":{"type":"string","enum":["qr_code","search","walk_in"],"description":"Corrected method"}},"description":"Correct a check-in tap — at least one field."}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","format":"uuid","description":"Check-in record ID","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"eventId":{"type":["string","null"],"example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"sessionId":{"type":["string","null"],"description":"Physical session ID, null = reception (event-level)","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"attendeeId":{"type":["string","null"],"example":"550e8400-e29b-41d4-a716-446655440002"},"checkedAt":{"type":["string","null"],"description":"Tap timestamp (the datetime-range filter field)","example":"2026-03-01T09:05:00.000Z"},"direction":{"type":["string","null"],"example":"in"},"method":{"type":["string","null"],"example":"qr_code"},"createdAt":{"type":["string","null"],"example":"2026-03-01T09:05:01.000Z"},"updatedAt":{"type":["string","null"],"example":"2026-03-01T09:05:01.000Z"}},"required":["id","eventId","sessionId","attendeeId","checkedAt","direction","method","createdAt","updatedAt"],"description":"Per-session attendee check-in record"}}}},"400":{"description":"Validation","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}},"delete":{"tags":["check-ins"],"summary":"Delete a check-in tap","description":"Soft-deletes a tap (sets deletedAt — audit preserved, excluded from list/counts/timeline). Requires write.","security":[{"organizerApiToken":[]}],"parameters":[{"schema":{"type":"string","format":"uuid","description":"Event UUID"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Check-in UUID"},"required":true,"name":"checkInId","in":"path"}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"type":"object","properties":{"deleted":{"type":"boolean","example":true},"checkInId":{"type":"string","format":"uuid","example":"6daeaa1f-f82f-4068-a8b2-712894b00184"}},"required":["deleted","checkInId"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string","example":"ERR_PUBLIC_API_NOT_FOUND"},"message":{"type":"string","example":"Resource not found"},"status":{"type":"number","example":404},"meta":{}},"required":["code","message","status"]}}}}}}},"/v2/events/{eventId}/attendees/{attendeeId}/addons":{"get":{"tags":["attendee addons"],"summary":"List attendee addons","description":"Returns all addon items for a specific attendee within an event. Each addon represents the attendee's selection of a ticket class add-on, including quantity and selection status.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Event UUID"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Attendee UUID"},"required":true,"name":"attendeeId","in":"path"}],"responses":{"200":{"description":"Array of addon items belonging to the attendee","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440003"},"eventId":{"type":"string","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"claimedByAttendeeId":{"type":["string","null"],"example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"orderItemId":{"type":["string","null"],"example":null},"addOnId":{"type":"string","example":"473ed8f7-deae-4bee-bb32-eafd860aebe4"},"qty":{"type":"number","example":1},"selectionStatus":{"type":["string","null"],"example":"waitlisted"},"waitlistedAt":{"type":["string","null"],"example":"2026-08-17T09:36:08.887Z"},"confirmedAt":{"type":["string","null"],"example":null},"createdAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.518Z"},"updatedAt":{"type":["string","null"],"example":"2026-08-17T09:36:09.620Z"}},"required":["id","eventId","claimedByAttendeeId","orderItemId","addOnId","qty","selectionStatus","waitlistedAt","confirmedAt","createdAt","updatedAt"],"description":"Attendee addon row — represents an attendee's selected addon item within an event"}}}}},"401":{"description":"Unauthorized — missing or invalid JWT","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden — user is not an organiser of this event","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}}}},"post":{"tags":["attendee addons"],"summary":"Create an attendee addon","description":"Adds a new addon selection for an attendee. The addon is linked to a ticket class add-on ID and starts with a selection status (default: regular).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Event UUID"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Attendee UUID"},"required":true,"name":"attendeeId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"addOnId":{"type":"string","format":"uuid","description":"The ticket class add-on ID that this attendee has selected","example":"473ed8f7-deae-4bee-bb32-eafd860aebe4"},"qty":{"type":"integer","minimum":1,"default":1,"description":"Quantity of this addon selected","example":1},"selectionStatus":{"type":"string","enum":["regular","confirmed","waitlisted"],"default":"regular","description":"Initial selection status","example":"regular"}},"required":["addOnId"]}}}},"responses":{"201":{"description":"Addon item created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440003"},"eventId":{"type":"string","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"claimedByAttendeeId":{"type":["string","null"],"example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"orderItemId":{"type":["string","null"],"example":null},"addOnId":{"type":"string","example":"473ed8f7-deae-4bee-bb32-eafd860aebe4"},"qty":{"type":"number","example":1},"selectionStatus":{"type":["string","null"],"example":"waitlisted"},"waitlistedAt":{"type":["string","null"],"example":"2026-08-17T09:36:08.887Z"},"confirmedAt":{"type":["string","null"],"example":null},"createdAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.518Z"},"updatedAt":{"type":["string","null"],"example":"2026-08-17T09:36:09.620Z"}},"required":["id","eventId","claimedByAttendeeId","orderItemId","addOnId","qty","selectionStatus","waitlistedAt","confirmedAt","createdAt","updatedAt"],"description":"Attendee addon row — represents an attendee's selected addon item within an event"}}}},"400":{"description":"Validation error — invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}},"401":{"description":"Unauthorized — missing or invalid JWT","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden — user is not an organiser of this event","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}}}}},"/v2/events/{eventId}/attendees/{attendeeId}/addons/{addOnId}":{"get":{"tags":["attendee addons"],"summary":"Get attendee addon by ID","description":"Returns a single addon item by its ID, scoped to a specific attendee and event.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Event UUID"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Attendee UUID"},"required":true,"name":"attendeeId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Addon item UUID"},"required":true,"name":"addOnId","in":"path"}],"responses":{"200":{"description":"Addon item","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440003"},"eventId":{"type":"string","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"claimedByAttendeeId":{"type":["string","null"],"example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"orderItemId":{"type":["string","null"],"example":null},"addOnId":{"type":"string","example":"473ed8f7-deae-4bee-bb32-eafd860aebe4"},"qty":{"type":"number","example":1},"selectionStatus":{"type":["string","null"],"example":"waitlisted"},"waitlistedAt":{"type":["string","null"],"example":"2026-08-17T09:36:08.887Z"},"confirmedAt":{"type":["string","null"],"example":null},"createdAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.518Z"},"updatedAt":{"type":["string","null"],"example":"2026-08-17T09:36:09.620Z"}},"required":["id","eventId","claimedByAttendeeId","orderItemId","addOnId","qty","selectionStatus","waitlistedAt","confirmedAt","createdAt","updatedAt"],"description":"Attendee addon row — represents an attendee's selected addon item within an event"}}}},"401":{"description":"Unauthorized — missing or invalid JWT","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden — user is not an organiser of this event","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Not found — addon item does not exist for this attendee","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}}}},"patch":{"tags":["attendee addons"],"summary":"Update attendee addon","description":"Updates an attendee addon item. Accepts optional fields: selection_status (with transition validation), qty, and addOnId. At least one field must be provided. Valid status transitions: regular→waitlisted, waitlisted→confirmed, confirmed→waitlisted.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Event UUID"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Attendee UUID"},"required":true,"name":"attendeeId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Addon item UUID"},"required":true,"name":"addOnId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"selection_status":{"type":"string","enum":["confirmed","waitlisted"],"description":"New selection status. Transitions: regular→waitlisted, waitlisted→confirmed, confirmed→waitlisted. See route description for status meanings."},"qty":{"type":"integer","minimum":1,"description":"New quantity"},"addOnId":{"type":"string","format":"uuid","description":"New add-on ID"}}}}}},"responses":{"200":{"description":"Addon item updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440003"},"eventId":{"type":"string","example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"claimedByAttendeeId":{"type":["string","null"],"example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"orderItemId":{"type":["string","null"],"example":null},"addOnId":{"type":"string","example":"473ed8f7-deae-4bee-bb32-eafd860aebe4"},"qty":{"type":"number","example":1},"selectionStatus":{"type":["string","null"],"example":"waitlisted"},"waitlistedAt":{"type":["string","null"],"example":"2026-08-17T09:36:08.887Z"},"confirmedAt":{"type":["string","null"],"example":null},"createdAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.518Z"},"updatedAt":{"type":["string","null"],"example":"2026-08-17T09:36:09.620Z"}},"required":["id","eventId","claimedByAttendeeId","orderItemId","addOnId","qty","selectionStatus","waitlistedAt","confirmedAt","createdAt","updatedAt"],"description":"Attendee addon row — represents an attendee's selected addon item within an event"}}}},"400":{"description":"Validation error or invalid status transition","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}},"401":{"description":"Unauthorized — missing or invalid JWT","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden — user is not an organiser of this event","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Not found — addon item does not exist for this attendee","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}}}}},"/v2/events/{eventId}/attendees/{attendeeId}/tickets":{"get":{"tags":["attendee tickets"],"summary":"List attendee tickets","description":"Returns all tickets assigned to a specific attendee within an event. Each ticket represents the attendee's claim on a ticket class (the purchased or assigned instance).","parameters":[{"schema":{"type":"string","format":"uuid","description":"Event UUID"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Attendee UUID"},"required":true,"name":"attendeeId","in":"path"}],"responses":{"200":{"description":"Array of tickets belonging to the attendee","content":{"application/json":{"schema":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440005"},"ticketClassId":{"type":["string","null"],"example":"550e8400-e29b-41d4-a716-446655440006"},"eventId":{"type":["string","null"],"example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"claimedByAttendeeId":{"type":["string","null"],"example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"orderItemId":{"type":["string","null"],"example":null},"createdAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.518Z"},"updatedAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.518Z"}},"required":["id","ticketClassId","eventId","claimedByAttendeeId","orderItemId","createdAt","updatedAt"],"description":"Attendee ticket row — represents a ticket instance assigned to an attendee for a specific ticket class"}}}}},"401":{"description":"Unauthorized — missing or invalid JWT","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden — user is not an organiser of this event","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}}}},"post":{"tags":["attendee tickets"],"summary":"Create an attendee ticket","description":"Assigns a ticket class to an attendee. Creates a new ticket instance linked to the specified ticket class.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Event UUID"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Attendee UUID"},"required":true,"name":"attendeeId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ticketClassId":{"type":"string","format":"uuid","description":"The ticket class ID to assign to this attendee","example":"550e8400-e29b-41d4-a716-446655440006"}},"required":["ticketClassId"]}}}},"responses":{"201":{"description":"Ticket created successfully","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440005"},"ticketClassId":{"type":["string","null"],"example":"550e8400-e29b-41d4-a716-446655440006"},"eventId":{"type":["string","null"],"example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"claimedByAttendeeId":{"type":["string","null"],"example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"orderItemId":{"type":["string","null"],"example":null},"createdAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.518Z"},"updatedAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.518Z"}},"required":["id","ticketClassId","eventId","claimedByAttendeeId","orderItemId","createdAt","updatedAt"],"description":"Attendee ticket row — represents a ticket instance assigned to an attendee for a specific ticket class"}}}},"400":{"description":"Validation error — invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}},"401":{"description":"Unauthorized — missing or invalid JWT","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden — user is not an organiser of this event","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}}}}},"/v2/events/{eventId}/attendees/{attendeeId}/tickets/{ticketId}":{"get":{"tags":["attendee tickets"],"summary":"Get attendee ticket by ID","description":"Returns a single ticket by its ID, scoped to a specific attendee and event.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Event UUID"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Attendee UUID"},"required":true,"name":"attendeeId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Ticket UUID"},"required":true,"name":"ticketId","in":"path"}],"responses":{"200":{"description":"Ticket","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440005"},"ticketClassId":{"type":["string","null"],"example":"550e8400-e29b-41d4-a716-446655440006"},"eventId":{"type":["string","null"],"example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"claimedByAttendeeId":{"type":["string","null"],"example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"orderItemId":{"type":["string","null"],"example":null},"createdAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.518Z"},"updatedAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.518Z"}},"required":["id","ticketClassId","eventId","claimedByAttendeeId","orderItemId","createdAt","updatedAt"],"description":"Attendee ticket row — represents a ticket instance assigned to an attendee for a specific ticket class"}}}},"401":{"description":"Unauthorized — missing or invalid JWT","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden — user is not an organiser of this event","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Not found — ticket does not exist for this attendee","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}}}},"patch":{"tags":["attendee tickets"],"summary":"Update attendee ticket","description":"Updates an attendee ticket. Currently supports reassigning the ticket class via the ticketClassId field.","parameters":[{"schema":{"type":"string","format":"uuid","description":"Event UUID"},"required":true,"name":"eventId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Attendee UUID"},"required":true,"name":"attendeeId","in":"path"},{"schema":{"type":"string","format":"uuid","description":"Ticket UUID"},"required":true,"name":"ticketId","in":"path"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"ticketClassId":{"type":"string","format":"uuid","description":"New ticket class ID to reassign this ticket to"}}}}}},"responses":{"200":{"description":"Ticket updated successfully","content":{"application/json":{"schema":{"type":"object","properties":{"id":{"type":"string","example":"550e8400-e29b-41d4-a716-446655440005"},"ticketClassId":{"type":["string","null"],"example":"550e8400-e29b-41d4-a716-446655440006"},"eventId":{"type":["string","null"],"example":"aaff0556-85b5-4ac4-a484-486c7f319c08"},"claimedByAttendeeId":{"type":["string","null"],"example":"6daeaa1f-f82f-4068-a8b2-712894b00184"},"orderItemId":{"type":["string","null"],"example":null},"createdAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.518Z"},"updatedAt":{"type":["string","null"],"example":"2026-08-17T08:28:35.518Z"}},"required":["id","ticketClassId","eventId","claimedByAttendeeId","orderItemId","createdAt","updatedAt"],"description":"Attendee ticket row — represents a ticket instance assigned to an attendee for a specific ticket class"}}}},"400":{"description":"Validation error — invalid request body","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}},"401":{"description":"Unauthorized — missing or invalid JWT","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}},"403":{"description":"Forbidden — user is not an organiser of this event","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}},"404":{"description":"Not found — ticket does not exist for this attendee","content":{"application/json":{"schema":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"status":{"type":"number"},"meta":{}},"required":["code","message","status"]}}}}}}}},"webhooks":{}}