{"openapi":"3.0.0","paths":{"/api/billing/entitlements/me":{"get":{"operationId":"BillingController_getMyEntitlements","parameters":[],"responses":{"200":{"description":"Entitlement resolved"}},"summary":"Resolve the calling user's entitlement","tags":["Billing"]}},"/api/billing/checkout":{"post":{"operationId":"BillingController_createCheckout","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCheckoutDto"}}}},"responses":{"201":{"description":"Checkout session created"}},"summary":"Create a Stripe Checkout Session","tags":["Billing"]}},"/api/billing/portal":{"post":{"operationId":"BillingController_createPortal","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBillingPortalDto"}}}},"responses":{"201":{"description":"Portal session created"}},"summary":"Create a Billing Portal Session","tags":["Billing"]}},"/api/billing/subscription/cancel":{"post":{"description":"Schedules cancel_at_period_end on the caller’s Stripe subscription. Studio access continues until currentPeriodEnd; no Customer Portal required.","operationId":"BillingController_cancelSubscription","parameters":[],"responses":{"200":{"description":"Cancellation scheduled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CancelSubscriptionResponseDto"}}}}},"summary":"Cancel Studio subscription at period end","tags":["Billing"]}},"/api/billing/transactions/mine":{"get":{"description":"Self-scoped purchase history for the calling user — used by the Settings → Purchase history panel. Identical shape to the admin transactions list but auto-filtered to the caller, so a host without `transactions.read` (an Anna-style account-level role) can still see their own greeting-card and SMS purchases for Concern 6.5.","operationId":"BillingController_listMyTransactions","parameters":[{"name":"page","required":false,"in":"query","description":"Page number","schema":{"default":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page","schema":{"default":10,"example":10,"type":"number"}},{"name":"sortBy","required":false,"in":"query","description":"Sort field","schema":{"example":"createdAt","type":"string"}},{"name":"sortOrder","required":false,"in":"query","description":"Sort order","schema":{"default":"desc","type":"string","enum":["asc","desc"]}},{"name":"search","required":false,"in":"query","description":"Search query","schema":{"example":"john","type":"string"}},{"name":"type","required":false,"in":"query","description":"Filter by transaction type","schema":{"type":"string","enum":["SUBSCRIPTION","EVENT_UPGRADE","SMS_TOPUP","GREETING_CARD","GREETING_CARD_SMS_PACK"]}},{"name":"status","required":false,"in":"query","description":"Filter by status","schema":{"type":"string","enum":["COMPLETE","OPEN","EXPIRED"]}},{"name":"userId","required":false,"in":"query","description":"Filter by user ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Purchase history fetched successfully"}},"summary":"Current user's purchase history","tags":["Billing"]}},"/api/billing/transactions":{"get":{"operationId":"BillingController_listTransactions","parameters":[{"name":"page","required":false,"in":"query","description":"Page number","schema":{"default":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page","schema":{"default":10,"example":10,"type":"number"}},{"name":"sortBy","required":false,"in":"query","description":"Sort field","schema":{"example":"createdAt","type":"string"}},{"name":"sortOrder","required":false,"in":"query","description":"Sort order","schema":{"default":"desc","type":"string","enum":["asc","desc"]}},{"name":"search","required":false,"in":"query","description":"Search query","schema":{"example":"john","type":"string"}},{"name":"type","required":false,"in":"query","description":"Filter by transaction type","schema":{"type":"string","enum":["SUBSCRIPTION","EVENT_UPGRADE","SMS_TOPUP","GREETING_CARD","GREETING_CARD_SMS_PACK"]}},{"name":"status","required":false,"in":"query","description":"Filter by status","schema":{"type":"string","enum":["COMPLETE","OPEN","EXPIRED"]}},{"name":"userId","required":false,"in":"query","description":"Filter by user ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Transactions fetched successfully"}},"summary":"List all transactions for the organization","tags":["Billing"]}},"/api/billing/transactions/stats":{"get":{"operationId":"BillingController_getTransactionStats","parameters":[{"name":"page","required":false,"in":"query","description":"Page number","schema":{"default":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page","schema":{"default":10,"example":10,"type":"number"}},{"name":"sortBy","required":false,"in":"query","description":"Sort field","schema":{"example":"createdAt","type":"string"}},{"name":"sortOrder","required":false,"in":"query","description":"Sort order","schema":{"default":"desc","type":"string","enum":["asc","desc"]}},{"name":"search","required":false,"in":"query","description":"Search query","schema":{"example":"john","type":"string"}},{"name":"type","required":false,"in":"query","description":"Filter by transaction type","schema":{"type":"string","enum":["SUBSCRIPTION","EVENT_UPGRADE","SMS_TOPUP","GREETING_CARD","GREETING_CARD_SMS_PACK"]}},{"name":"status","required":false,"in":"query","description":"Filter by status","schema":{"type":"string","enum":["COMPLETE","OPEN","EXPIRED"]}},{"name":"userId","required":false,"in":"query","description":"Filter by user ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Transaction stats fetched successfully"}},"summary":"Earnings totals broken down by transaction type","tags":["Billing"]}},"/api/billing/webhooks":{"post":{"operationId":"BillingWebhookController_handleWebhook","parameters":[{"name":"stripe-signature","required":true,"in":"header","schema":{"type":"string"}}],"responses":{"201":{"description":""}},"summary":"Stripe Webhook Endpoint","tags":["Billing"]}},"/api/billing/usage":{"get":{"operationId":"UsageController_getUsageSummary","parameters":[],"responses":{"200":{"description":"Usage summary fetched successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsageSummaryResponseDto"}}}}},"summary":"Get organization usage summary for the current billing period","tags":["Billing / Usage"]}},"/api/billing/usage/events/{eventId}":{"get":{"operationId":"UsageController_getEventUsage","parameters":[{"name":"eventId","required":true,"in":"path","description":"Event ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"responses":{"200":{"description":"Event usage fetched successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EventUsageSummaryDto"}}}}},"summary":"Get usage details for a specific event","tags":["Billing / Usage"]}},"/api/storage/upload-url":{"post":{"description":"Generates a presigned URL for uploading files directly to S3/MinIO. Client uploads directly to S3, then calls /storage/confirm to attach the file to a resource.","operationId":"StorageController_generateUploadUrl","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerateUploadUrlDto"}}}},"responses":{"200":{"description":"Presigned upload URL generated successfully"},"400":{"description":"Invalid file type or size"},"401":{"description":"Not authenticated"}},"summary":"Generate presigned upload URL","tags":["Storage"]}},"/api/storage/confirm":{"post":{"description":"Confirms that a file has been uploaded to S3 and optionally attaches it to a resource (event invitation or greeting card design). Returns the permanent download URL.","operationId":"StorageController_confirmUpload","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmUploadDto"}}}},"responses":{"200":{"description":"Upload confirmed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmUploadResponseDto"}}}},"400":{"description":"Invalid key or resource"},"401":{"description":"Not authenticated"}},"summary":"Confirm file upload and attach to resource","tags":["Storage"]}},"/api/storage/delete":{"delete":{"description":"Deletes a file from S3/MinIO storage using its key.","operationId":"StorageController_deleteFile","parameters":[],"responses":{"200":{"description":"File deleted successfully"},"404":{"description":"File not found"}},"summary":"Delete a file from storage","tags":["Storage"]}},"/api/storage/download-url":{"get":{"description":"Generates a presigned URL for downloading a file.","operationId":"StorageController_getDownloadUrl","parameters":[{"name":"key","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Download URL generated"},"404":{"description":"File not found"}},"summary":"Get a presigned download URL","tags":["Storage"]}},"/api/events":{"post":{"description":"Creates a new event owned by the authenticated user. The event starts in DRAFT status with ESSENTIAL tier billing defaults.","operationId":"EventController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateEventDto"}}}},"responses":{"201":{"description":"Event created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleEventDto"}}}},"400":{"description":"Invalid input data"},"401":{"description":"Not authenticated"}},"summary":"Create a new event","tags":["Events"]},"get":{"description":"Returns a paginated list of events owned by the authenticated user. Supports search, filtering by type/status/date range, and sorting.","operationId":"EventController_findAll","parameters":[{"name":"page","required":false,"in":"query","description":"Page number","schema":{"default":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page","schema":{"default":10,"example":10,"type":"number"}},{"name":"sortBy","required":false,"in":"query","description":"Field to sort events by","schema":{"example":"createdAt","type":"string","enum":["eventDate","createdAt","rsvpBy"]}},{"name":"sortOrder","required":false,"in":"query","description":"Sort order","schema":{"default":"desc","type":"string","enum":["asc","desc"]}},{"name":"search","required":false,"in":"query","description":"Search query","schema":{"example":"john","type":"string"}},{"name":"userId","required":false,"in":"query","description":"Filter events by user (host) ID. Only admins can use this parameter.","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}},{"name":"allEvents","required":false,"in":"query","description":"When true, retrieve events across all hosts in the organization. Requires events.read.all permission.","schema":{"example":true,"type":"boolean"}},{"name":"status","required":false,"in":"query","description":"Filter by event status","schema":{"example":"PUBLISHED","type":"string","enum":["DRAFT","PUBLISHED","AUTO_CLOSED","MANUALLY_CLOSED","DUPLICATE","CLONED"]}},{"name":"dateFrom","required":false,"in":"query","description":"Filter events on or after this date (ISO 8601)","schema":{"format":"date-time","example":"2026-01-01T00:00:00.000Z","type":"string"}},{"name":"dateTo","required":false,"in":"query","description":"Filter events on or before this date (ISO 8601)","schema":{"format":"date-time","example":"2026-12-31T23:59:59.000Z","type":"string"}}],"responses":{"200":{"description":"Events fetched successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAllEventDto"}}}}},"summary":"List all events for the current user","tags":["Events"]}},"/api/events/{id}":{"get":{"description":"Returns detailed event information. Users with the `events.read.all` permission can fetch any event in their organization; otherwise the caller must own the event.","operationId":"EventController_findOne","parameters":[{"name":"id","required":true,"in":"path","description":"Event ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"responses":{"200":{"description":"Event fetched successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleEventDto"}}}},"404":{"description":"Event not found"}},"summary":"Get a single event by ID","tags":["Events"]},"patch":{"description":"Updates an existing event. The event owner can always update; users with the `events.update.all` permission can update any event in the organization. Billing fields (tier, guestLimit, paymentStatus, smsCredits) cannot be changed via this endpoint.","operationId":"EventController_update","parameters":[{"name":"id","required":true,"in":"path","description":"Event ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateEventDto"}}}},"responses":{"200":{"description":"Event updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleEventDto"}}}},"400":{"description":"Invalid input or status transition"},"404":{"description":"Event not found"}},"summary":"Update an event","tags":["Events"]},"delete":{"description":"Permanently deletes an event and all related data (guests, form fields, tokens). This action cannot be undone. The event owner can always delete; users with `events.delete.all` can delete any event in the organization.","operationId":"EventController_delete","parameters":[{"name":"id","required":true,"in":"path","description":"Event ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"responses":{"200":{"description":"Event deleted successfully"},"404":{"description":"Event not found"}},"summary":"Delete an event","tags":["Events"]}},"/api/events/{id}/preview":{"get":{"description":"Returns the event in the same shape as the public RSVP endpoint, but takes an event ID and works regardless of status. Used by the host preview dialog so DRAFT events (e.g. duplicates) can be previewed.","operationId":"EventController_findOneForPreview","parameters":[{"name":"id","required":true,"in":"path","description":"Event ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"responses":{"200":{"description":"Event preview fetched successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleEventDto"}}}},"404":{"description":"Event not found"}},"summary":"Get event by ID in public RSVP shape (host preview)","tags":["Events"]}},"/api/events/public/{slug}":{"get":{"description":"Returns event details for the public-facing RSVP page. Only PUBLISHED events are accessible. No authentication required.","operationId":"EventController_findBySlug","parameters":[{"name":"slug","required":true,"in":"path","description":"Event slug (URL-friendly identifier)","schema":{"example":"sarah-and-john-wedding-abc123","type":"string"}}],"responses":{"200":{"description":"Event fetched successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleEventDto"}}}},"404":{"description":"Event not found or not published"}},"summary":"Get a published event by slug (public RSVP page)","tags":["Events"]}},"/api/events/public/{slug}/availability":{"get":{"description":"Lightweight public lookup used by the RSVP page to differentiate a truly unknown slug from one whose event exists but is DRAFT or closed. Returns just status + name so DRAFT events do not leak full details. When the slug is taken, also returns a `suggestion` field with the next available variant (e.g. `wedding-2`) so the host UI can offer a one-click alternative. Always 200; the body indicates existence.","operationId":"EventController_getEventAvailability","parameters":[{"name":"slug","required":true,"in":"path","description":"Event slug (URL-friendly identifier)","schema":{"example":"sarah-and-john-wedding-abc123","type":"string"}},{"name":"eventId","required":true,"in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Availability fetched successfully"}},"summary":"Check whether a public RSVP slug points to a viewable event","tags":["Events"]}},"/api/events/public/{slug}/rsvp":{"post":{"description":"Guest submits RSVP for a published event. No authentication required. Creates guest record with ATTENDING or DECLINED status.","operationId":"EventController_submitRsvp","parameters":[{"name":"slug","required":true,"in":"path","description":"Event slug","schema":{"example":"sarah-and-john-wedding-abc123","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubmitRsvpDto"}}}},"responses":{"201":{"description":"RSVP submitted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuestRsvpResponseDto"}}}},"400":{"description":"RSVP deadline passed or at capacity"},"404":{"description":"Event not found"},"409":{"description":"Email already RSVPed for this event"}},"summary":"Submit RSVP (guest)","tags":["Events"]}},"/api/events/public/{slug}/rsvp/{token}":{"get":{"description":"Fetches the current RSVP state for a guest using their unique edit token. Returns guest data and form field responses. No authentication required.","operationId":"EventController_getRsvpByToken","parameters":[{"name":"slug","required":true,"in":"path","description":"Event slug","schema":{"example":"sarah-and-john-wedding-abc123","type":"string"}},{"name":"token","required":true,"in":"path","description":"Unique guest edit token","schema":{"example":"a1b2c3d4e5f6...","type":"string"}}],"responses":{"200":{"description":"RSVP fetched successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuestRsvpResponseDto"}}}},"400":{"description":"Token expired or revoked"},"404":{"description":"Invalid token or event not found"}},"summary":"Get current RSVP by edit token (guest)","tags":["Events"]},"patch":{"description":"Updates an existing RSVP using the unique edit token. Guests can change status, party size, and form field responses. No authentication required.","operationId":"EventController_updateRsvpByToken","parameters":[{"name":"slug","required":true,"in":"path","description":"Event slug","schema":{"example":"sarah-and-john-wedding-abc123","type":"string"}},{"name":"token","required":true,"in":"path","description":"Unique guest edit token","schema":{"example":"a1b2c3d4e5f6...","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateRsvpDto"}}}},"responses":{"200":{"description":"RSVP updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GuestRsvpResponseDto"}}}},"400":{"description":"RSVP deadline passed, token expired, or event closed"},"404":{"description":"Invalid token or event not found"}},"summary":"Update RSVP by edit token (guest)","tags":["Events"]}},"/api/events/{id}/guests":{"get":{"description":"Returns a paginated list of guests for the specified event. Supports search (name/email), filtering by status, and sorting.","operationId":"EventController_findAllGuests","parameters":[{"name":"id","required":true,"in":"path","description":"Event ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}},{"name":"page","required":false,"in":"query","description":"Page number","schema":{"default":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page","schema":{"default":10,"example":10,"type":"number"}},{"name":"sortBy","required":false,"in":"query","description":"Sort field","schema":{"example":"createdAt","type":"string"}},{"name":"sortOrder","required":false,"in":"query","description":"Sort order","schema":{"default":"desc","type":"string","enum":["asc","desc"]}},{"name":"search","required":false,"in":"query","description":"Search query","schema":{"example":"john","type":"string"}},{"name":"status","required":false,"in":"query","description":"Filter by RSVP status","schema":{"example":"ATTENDING","type":"string","enum":["PENDING","ATTENDING","DECLINED"]}}],"responses":{"200":{"description":"Guests fetched successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAllGuestsDto"}}}},"404":{"description":"Event not found"}},"summary":"List guests for an event","tags":["Events"]},"post":{"description":"Adds one or more guests to an existing event. Each guest receives a unique RSVP edit token. Validates guest limit and checks for duplicate emails within the event.","operationId":"EventController_addGuests","parameters":[{"name":"id","required":true,"in":"path","description":"Event ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddGuestsToEventDto"}}}},"responses":{"201":{"description":"Guests added successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAllGuestsDto"}}}},"400":{"description":"Guest limit exceeded"},"404":{"description":"Event not found"},"409":{"description":"Duplicate email within event"}},"summary":"Add guests to an existing event","tags":["Events"]}},"/api/events/{id}/guests/export":{"get":{"description":"Exports the full guest list for an event as CSV or PDF. PDF includes event header, summary stats, and formatted guest table.","operationId":"EventController_exportGuests","parameters":[{"name":"id","required":true,"in":"path","description":"Event ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}},{"name":"format","required":true,"in":"query","description":"Export file format","schema":{"enum":["csv","pdf"],"type":"string"}}],"responses":{"200":{"description":"File exported successfully"},"404":{"description":"Event not found"}},"summary":"Export guest list","tags":["Events"]}},"/api/events/{id}/guests/{guestId}":{"get":{"description":"Returns detailed guest information including all form field responses.","operationId":"EventController_findOneGuest","parameters":[{"name":"id","required":true,"in":"path","description":"Event ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}},{"name":"guestId","required":true,"in":"path","description":"Guest ID","schema":{"example":"cmieqoaba0000iymt4fab9999","type":"string"}}],"responses":{"200":{"description":"Guest fetched successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleGuestDto"}}}},"404":{"description":"Event or guest not found"}},"summary":"Get a single guest by ID","tags":["Events"]},"patch":{"description":"Host updates guest details (name, email, phone, status, party size) and/or custom form field responses. All fields are optional — send only what needs to change.","operationId":"EventController_updateGuest","parameters":[{"name":"id","required":true,"in":"path","description":"Event ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}},{"name":"guestId","required":true,"in":"path","description":"Guest ID","schema":{"example":"cmieqoaba0000iymt4fab9999","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateGuestByHostDto"}}}},"responses":{"200":{"description":"Guest updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleGuestDto"}}}},"400":{"description":"Validation error"},"404":{"description":"Event or guest not found"},"409":{"description":"Duplicate email within event"}},"summary":"Update a guest (host)","tags":["Events"]},"delete":{"description":"Permanently removes a guest and all related data (form responses, edit tokens) from the event. This action cannot be undone.","operationId":"EventController_deleteGuest","parameters":[{"name":"id","required":true,"in":"path","description":"Event ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}},{"name":"guestId","required":true,"in":"path","description":"Guest ID","schema":{"example":"cmieqoaba0000iymt4fab9999","type":"string"}}],"responses":{"200":{"description":"Guest removed successfully"},"404":{"description":"Event or guest not found"}},"summary":"Delete a guest","tags":["Events"]}},"/api/events/{id}/publish":{"post":{"description":"Transitions a DRAFT event to PUBLISHED status, making it accessible via the public RSVP page. Only DRAFT events can be published.","operationId":"EventController_publish","parameters":[{"name":"id","required":true,"in":"path","description":"Event ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"responses":{"200":{"description":"Event published successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleEventDto"}}}},"400":{"description":"Event is not in DRAFT status"},"404":{"description":"Event not found"}},"summary":"Publish a draft event","tags":["Events"]}},"/api/events/{id}/close":{"post":{"description":"Transitions a PUBLISHED event to MANUALLY_CLOSED status, preventing further RSVPs. Only PUBLISHED events can be closed.","operationId":"EventController_close","parameters":[{"name":"id","required":true,"in":"path","description":"Event ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"responses":{"200":{"description":"Event closed successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleEventDto"}}}},"400":{"description":"Event is not in PUBLISHED status"},"404":{"description":"Event not found"}},"summary":"Manually close a published event","tags":["Events"]}},"/api/events/{id}/duplicate":{"post":{"description":"Creates a copy of an existing event with a new slug and DRAFT status. Copies event details and form fields, but NOT guests, tokens, or billing data.","operationId":"EventController_duplicate","parameters":[{"name":"id","required":true,"in":"path","description":"Source Event ID to duplicate","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"responses":{"201":{"description":"Event duplicated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleEventDto"}}}},"404":{"description":"Source event not found"}},"summary":"Duplicate an existing event","tags":["Events"]}},"/api/events/{id}/send-invitation":{"post":{"description":"Queues RSVP invitation emails for all guests with PENDING status. Uses the outbox pattern for reliable delivery.","operationId":"EventController_sendInvitations","parameters":[{"name":"id","required":true,"in":"path","description":"Event ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"responses":{"200":{"description":"Invitations queued successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendInvitationsResponseDto"}}}}},"summary":"Send invitation emails to all pending guests","tags":["Events"]}},"/api/events/{id}/send-sms-invitation":{"post":{"description":"Queues SMS invitations for guests with PENDING status and a phone number. SMS credits are deducted by the listener via CommService. Uses the outbox pattern for reliable delivery. Gated on the `sms_notifications` entitlement so ESSENTIAL hosts get a 403 with FEATURE_NOT_AVAILABLE instead of a confusing credit-check failure.","operationId":"EventController_sendSmsInvitations","parameters":[{"name":"id","required":true,"in":"path","description":"Event ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"responses":{"200":{"description":"SMS invitations queued successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendInvitationsResponseDto"}}}}},"summary":"Send SMS invitations to all pending guests with a phone number","tags":["Events"]}},"/api/events/{id}/grant-premium":{"post":{"operationId":"EventController_grantComplimentaryPremium","parameters":[{"name":"id","required":true,"in":"path","description":"Event ID","schema":{"type":"string"}}],"responses":{"201":{"description":"Event upgraded to PREMIUM"},"403":{"description":"Admin account required"}},"summary":"Admin: grant a complimentary PREMIUM upgrade to an event (no Stripe charge)","tags":["Events"]},"delete":{"operationId":"EventController_revokeComplimentaryPremium","parameters":[{"name":"id","required":true,"in":"path","description":"Event ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Complimentary PREMIUM revoked"},"400":{"description":"Event is not on a complimentary grant"},"403":{"description":"Admin account required"}},"summary":"Admin: revoke a complimentary PREMIUM upgrade for an event","tags":["Events"]}},"/api/events/{id}/responses-share":{"post":{"description":"Idempotent — returns the existing token if one is already active, otherwise generates a fresh one. Use the /rotate endpoint to force a new token (which invalidates the old link).","operationId":"EventController_getOrCreateResponsesShareToken","parameters":[{"name":"id","required":true,"in":"path","description":"Event ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Share token returned"},"404":{"description":"Event not found"}},"summary":"Generate or fetch the read-only responses share link","tags":["Events"]},"delete":{"operationId":"EventController_revokeResponsesShareToken","parameters":[{"name":"id","required":true,"in":"path","description":"Event ID","schema":{"type":"string"}}],"responses":{"204":{"description":"Share link revoked"},"404":{"description":"Event not found"}},"summary":"Revoke the responses share link","tags":["Events"]}},"/api/events/{id}/responses-share/rotate":{"post":{"description":"Replaces the existing token with a fresh one. Any previously-shared link will return 404 immediately.","operationId":"EventController_rotateResponsesShareToken","parameters":[{"name":"id","required":true,"in":"path","description":"Event ID","schema":{"type":"string"}}],"responses":{"200":{"description":"New share token issued"},"404":{"description":"Event not found"}},"summary":"Rotate the responses share link","tags":["Events"]}},"/api/events/public/responses/{token}":{"get":{"description":"Returns the same event shape the host responses page consumes (event + active form fields + tier flags). 404 if the token has been revoked or never existed.","operationId":"EventController_findByResponsesShareToken","parameters":[{"name":"token","required":true,"in":"path","description":"Responses share token","schema":{"type":"string"}}],"responses":{"200":{"description":"Event fetched"},"404":{"description":"Token unknown or revoked"}},"summary":"Get the event behind a responses share link (public)","tags":["Events"]}},"/api/events/public/responses/{token}/guests":{"get":{"operationId":"EventController_findGuestsByResponsesShareToken","parameters":[{"name":"token","required":true,"in":"path","description":"Responses share token","schema":{"type":"string"}},{"name":"page","required":false,"in":"query","description":"Page number","schema":{"default":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page","schema":{"default":10,"example":10,"type":"number"}},{"name":"sortBy","required":false,"in":"query","description":"Sort field","schema":{"example":"createdAt","type":"string"}},{"name":"sortOrder","required":false,"in":"query","description":"Sort order","schema":{"default":"desc","type":"string","enum":["asc","desc"]}},{"name":"search","required":false,"in":"query","description":"Search query","schema":{"example":"john","type":"string"}},{"name":"status","required":false,"in":"query","description":"Filter by RSVP status","schema":{"example":"ATTENDING","type":"string","enum":["PENDING","ATTENDING","DECLINED"]}}],"responses":{"200":{"description":"Guests fetched"},"404":{"description":"Token unknown or revoked"}},"summary":"List guests for a responses share link (public, read-only)","tags":["Events"]}},"/api/events/public/responses/{token}/guests/export":{"get":{"description":"Mirrors the host export. Gated by the event's tier (Premium/Studio) exactly like the host endpoint.","operationId":"EventController_exportGuestsByResponsesShareToken","parameters":[{"name":"token","required":true,"in":"path","description":"Responses share token","schema":{"type":"string"}},{"name":"format","required":true,"in":"query","description":"Export file format","schema":{"enum":["xlsx","pdf"],"type":"string"}}],"responses":{"200":{"description":"File exported successfully"},"403":{"description":"Event tier does not allow export"},"404":{"description":"Token unknown or revoked"}},"summary":"Export guests via a responses share link (public, read-only)","tags":["Events"]}},"/api/audit":{"get":{"operationId":"AuditController_findAll","parameters":[{"name":"page","required":false,"in":"query","description":"Page number","schema":{"default":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page","schema":{"default":10,"example":10,"type":"number"}},{"name":"sortBy","required":false,"in":"query","description":"Sort field","schema":{"example":"createdAt","type":"string"}},{"name":"sortOrder","required":false,"in":"query","description":"Sort order","schema":{"default":"desc","type":"string","enum":["asc","desc"]}},{"name":"search","required":false,"in":"query","description":"Search query","schema":{"example":"john","type":"string"}},{"name":"entityType","required":false,"in":"query","description":"Filter by entity type (e.g., lead, note, booking)","schema":{"example":"lead","type":"string"}},{"name":"entityId","required":false,"in":"query","description":"Filter by specific entity ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}},{"name":"action","required":false,"in":"query","description":"Filter by action (e.g., leads.create, leads.status_change)","schema":{"example":"leads.create","type":"string"}},{"name":"actorUserId","required":false,"in":"query","description":"Filter by actor user ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}},{"name":"dateFrom","required":false,"in":"query","description":"Filter audit logs from this date","schema":{"example":"2024-01-01","type":"string"}},{"name":"dateTo","required":false,"in":"query","description":"Filter audit logs to this date","schema":{"example":"2024-12-31","type":"string"}}],"responses":{"200":{"description":"Audit logs fetched successfully"}},"summary":"Get all audit logs (admin-only)","tags":["Audit"]}},"/api/audit/entity/{entityType}/{entityId}":{"get":{"operationId":"AuditController_findByEntity","parameters":[{"name":"entityType","required":true,"in":"path","description":"The type of entity (e.g., lead, note)","schema":{"example":"lead","type":"string"}},{"name":"entityId","required":true,"in":"path","description":"The ID of the entity","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"responses":{"200":{"description":"Entity audit trail fetched successfully"}},"summary":"Get audit trail for a specific entity","tags":["Audit"]}},"/api/communications/config/twilio":{"get":{"operationId":"ConfigController_getTwilioConfig","parameters":[],"responses":{"200":{"description":"Twilio config retrieved"}},"summary":"Get Twilio configuration (masked)","tags":["Communication Config"]},"put":{"operationId":"ConfigController_saveTwilioConfig","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TwilioConfigDto"}}}},"responses":{"200":{"description":"Twilio config saved"}},"summary":"Save Twilio configuration","tags":["Communication Config"]}},"/api/communications/config/smtp/test":{"get":{"operationId":"ConfigController_testSmtpConnection","parameters":[],"responses":{"200":{"description":"SMTP connection tested"}},"summary":"Test SMTP connection","tags":["Communication Config"]}},"/api/communications/config/smtp":{"get":{"operationId":"ConfigController_getSmtpConfig","parameters":[],"responses":{"200":{"description":"SMTP config retrieved"}},"summary":"Get SMTP configuration (masked)","tags":["Communication Config"]},"put":{"operationId":"ConfigController_saveSmtpConfig","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SmtpConfigDto"}}}},"responses":{"200":{"description":"SMTP config saved"}},"summary":"Save SMTP configuration","tags":["Communication Config"]}},"/api/communications/config/status":{"get":{"operationId":"ConfigController_getProviderStatus","parameters":[],"responses":{"200":{"description":"Provider status retrieved"}},"summary":"Get provider status","tags":["Communication Config"]}},"/api/communications/templates":{"get":{"description":"Lists communication templates. The `userId` query parameter is honoured only when the caller has `users.read` (admin role); for hosts it is silently ignored so they cannot enumerate other hosts' templates.","operationId":"TemplateController_findAll","parameters":[{"name":"page","required":false,"in":"query","description":"Page number","schema":{"default":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page","schema":{"default":10,"example":10,"type":"number"}},{"name":"sortBy","required":false,"in":"query","description":"Sort field","schema":{"example":"createdAt","type":"string"}},{"name":"sortOrder","required":false,"in":"query","description":"Sort order","schema":{"default":"desc","type":"string","enum":["asc","desc"]}},{"name":"type","required":false,"in":"query","description":"Filter by channel type","schema":{"type":"string","enum":["SMS","EMAIL"]}},{"name":"status","required":false,"in":"query","description":"Filter by status","schema":{"type":"string","enum":["DRAFT","APPROVED","ARCHIVED"]}},{"name":"search","required":false,"in":"query","description":"Search by name or description","schema":{"type":"string"}},{"name":"userId","required":false,"in":"query","description":"Admin-only: when set (and the caller has `users.read`), returns only templates owned by this user. Pass `org` to limit to org-wide defaults. Other callers see this parameter ignored.","schema":{"type":"string"}}],"responses":{"200":{"description":"Templates retrieved"}},"summary":"Get all templates","tags":["Communication Templates"]},"post":{"description":"Creates a template owned by the caller. Admins (callers with `users.read`) can pass `?onBehalfOfUserId=<userId>` to assign ownership to another host, which lets a system admin maintain templates on behalf of any user. The action is recorded in the audit log with both the actor and the target.","operationId":"TemplateController_create","parameters":[{"name":"onBehalfOfUserId","required":true,"in":"query","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCommTemplateDto"}}}},"responses":{"201":{"description":"Template created"}},"summary":"Create a new template","tags":["Communication Templates"]}},"/api/communications/templates/placeholders":{"get":{"operationId":"TemplateController_getPlaceholders","parameters":[],"responses":{"200":{"description":"Placeholders retrieved"}},"summary":"Get available placeholders","tags":["Communication Templates"]}},"/api/communications/templates/{id}":{"get":{"operationId":"TemplateController_findOne","parameters":[{"name":"id","required":true,"in":"path","description":"Template ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"responses":{"200":{"description":"Template retrieved"}},"summary":"Get a single template","tags":["Communication Templates"]},"patch":{"operationId":"TemplateController_update","parameters":[{"name":"id","required":true,"in":"path","description":"Template ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCommTemplateDto"}}}},"responses":{"200":{"description":"Template updated"}},"summary":"Update a template","tags":["Communication Templates"]}},"/api/communications/templates/{id}/approve":{"post":{"operationId":"TemplateController_approve","parameters":[{"name":"id","required":true,"in":"path","description":"Template ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"responses":{"200":{"description":"Template approved"}},"summary":"Approve a template","tags":["Communication Templates"]}},"/api/communications/templates/{id}/archive":{"post":{"operationId":"TemplateController_archive","parameters":[{"name":"id","required":true,"in":"path","description":"Template ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"responses":{"200":{"description":"Template archived"}},"summary":"Archive a template","tags":["Communication Templates"]}},"/api/communications/templates/{id}/new-version":{"post":{"operationId":"TemplateController_createNewVersion","parameters":[{"name":"id","required":true,"in":"path","description":"Template ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"responses":{"201":{"description":"New version created"}},"summary":"Create a new version of a template","tags":["Communication Templates"]}},"/api/communications/templates/{id}/preview":{"post":{"operationId":"TemplateController_preview","parameters":[{"name":"id","required":true,"in":"path","description":"Template ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PreviewTemplateDto"}}}},"responses":{"200":{"description":"Preview generated"}},"summary":"Preview a template with entity data","tags":["Communication Templates"]}},"/api/communications/sms":{"post":{"operationId":"CommController_sendSms","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendSmsDto"}}}},"responses":{"201":{"description":"SMS sent successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponseDto"}}}}},"summary":"Send an SMS message","tags":["Communications"]}},"/api/communications/email":{"post":{"operationId":"CommController_sendEmail","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendEmailDto"}}}},"responses":{"201":{"description":"Email sent successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MessageResponseDto"}}}}},"summary":"Send an email message","tags":["Communications"]}},"/api/communications/messages":{"get":{"operationId":"CommController_findMessages","parameters":[{"name":"page","required":false,"in":"query","description":"Page number","schema":{"default":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page","schema":{"default":10,"example":10,"type":"number"}},{"name":"sortBy","required":false,"in":"query","description":"Sort field","schema":{"example":"createdAt","type":"string"}},{"name":"sortOrder","required":false,"in":"query","description":"Sort order","schema":{"default":"desc","type":"string","enum":["asc","desc"]}},{"name":"direction","required":false,"in":"query","description":"Filter by direction","schema":{"type":"string","enum":["IN","OUT"]}},{"name":"status","required":false,"in":"query","description":"Filter by status","schema":{"type":"string","enum":["QUEUED","SENT","DELIVERED","FAILED","BOUNCED"]}},{"name":"channel","required":false,"in":"query","description":"Filter by channel","schema":{"type":"string","enum":["SMS","EMAIL"]}},{"name":"search","required":false,"in":"query","description":"Search in toAddress, subject, bodyText","schema":{"type":"string"}}],"responses":{"200":{"description":"Messages retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAllMessagesDto"}}}}},"summary":"Get all messages with pagination","tags":["Communications"]}},"/api/communications/messages/{id}":{"get":{"operationId":"CommController_findOne","parameters":[{"name":"id","required":true,"in":"path","description":"Message ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"responses":{"200":{"description":"Message retrieved","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleMessageDto"}}}}},"summary":"Get a single message","tags":["Communications"]}},"/api/organizations":{"post":{"operationId":"OrganizationController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrganizationDto"}}}},"responses":{"201":{"description":"Organization created successfully"},"403":{"description":"Forbidden - Super admin access required"},"409":{"description":"Conflict - Slug already exists"}},"summary":"Create a new organization","tags":["Organizations"]},"get":{"operationId":"OrganizationController_findAll","parameters":[{"name":"includeInactive","required":false,"in":"query","description":"Include inactive organizations in the response","schema":{"type":"boolean"}}],"responses":{"200":{"description":"List of organizations"}},"summary":"Get all organizations (public)","tags":["Organizations"]}},"/api/organizations/check-slug/{slug}":{"get":{"operationId":"OrganizationController_checkSlug","parameters":[{"name":"slug","required":true,"in":"path","description":"Organization slug to check","schema":{"example":"my-organization","type":"string"}}],"responses":{"200":{"description":"Slug availability status"}},"summary":"Check if a slug is available","tags":["Organizations"]}},"/api/organizations/by-slug/{slug}":{"get":{"operationId":"OrganizationController_findBySlug","parameters":[{"name":"slug","required":true,"in":"path","description":"Organization slug","schema":{"example":"my-organization","type":"string"}}],"responses":{"200":{"description":"Organization details"},"404":{"description":"Organization not found"}},"summary":"Get organization by slug (public)","tags":["Organizations"]}},"/api/organizations/settings/branding":{"get":{"description":"Admin-only. Returns the four overridable branding fields. Null on a field means the brand default is in effect for that slot.","operationId":"OrganizationController_getBranding","parameters":[],"responses":{"200":{"description":"Branding fetched successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationBrandingResponseDto"}}}}},"summary":"Get the system-email branding for the caller's organization","tags":["Organizations"]},"patch":{"description":"Admin-only. Each field is optional — pass only what you want to change. Send an empty string to clear an override (revert to default).","operationId":"OrganizationController_updateBranding","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateOrganizationBrandingDto"}}}},"responses":{"200":{"description":"Branding updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OrganizationBrandingResponseDto"}}}},"400":{"description":"Validation error"}},"summary":"Update system-email branding for the caller's organization","tags":["Organizations"]}},"/api/organizations/{id}":{"get":{"operationId":"OrganizationController_findOne","parameters":[{"name":"id","required":true,"in":"path","description":"Organization ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"responses":{"200":{"description":"Organization details"},"404":{"description":"Organization not found"}},"summary":"Get organization by ID","tags":["Organizations"]},"patch":{"operationId":"OrganizationController_update","parameters":[{"name":"id","required":true,"in":"path","description":"Organization ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateOrganizationDto"}}}},"responses":{"200":{"description":"Organization updated successfully"},"403":{"description":"Forbidden - Super admin access required"},"404":{"description":"Organization not found"}},"summary":"Update an organization (Super Admin only)","tags":["Organizations"]},"delete":{"operationId":"OrganizationController_remove","parameters":[{"name":"id","required":true,"in":"path","description":"Organization ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"responses":{"200":{"description":"Organization deleted successfully"},"403":{"description":"Forbidden - Super admin access required"},"404":{"description":"Organization not found"},"409":{"description":"Conflict - Organization has users"}},"summary":"Delete an organization (Super Admin only)","tags":["Organizations"]}},"/api/greeting-cards":{"post":{"description":"Creates a new greeting card order in DRAFT status. The card starts at $19 base price and can be customized with design and recipients.","operationId":"GreetingCardController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateGreetingCardDto"}}}},"responses":{"201":{"description":"Greeting card created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleGreetingCardDto"}}}},"400":{"description":"Invalid input data"},"401":{"description":"Not authenticated"}},"summary":"Create a new greeting card","tags":["Greeting Cards"]},"get":{"description":"Returns a paginated list of greeting cards for the authenticated user. Response meta includes account KPIs: totalRecipients, sent, drafts, totalOpens. Supports filtering by status and searching by title.","operationId":"GreetingCardController_findAll","parameters":[{"name":"page","required":false,"in":"query","description":"Page number (1-indexed)","schema":{"minimum":1,"default":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Number of items per page","schema":{"minimum":1,"maximum":100,"default":10,"type":"number"}},{"name":"status","required":false,"in":"query","description":"Filter by greeting card status","schema":{"type":"string","enum":["DRAFT","SCHEDULED","SENT","PARTIALLY_SENT","FAILED","CANCELED"]}},{"name":"search","required":false,"in":"query","description":"Search by title","schema":{"example":"Holiday","type":"string"}}],"responses":{"200":{"description":"Greeting cards fetched successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAllGreetingCardsDto"}}}}},"summary":"List all greeting cards","tags":["Greeting Cards"]}},"/api/greeting-cards/{id}":{"patch":{"description":"Updates greeting card title, design URL, drop shadow, or linked event. Allowed in any status, including after the card has been sent.","operationId":"GreetingCardController_update","parameters":[{"name":"id","required":true,"in":"path","description":"Greeting card ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateGreetingCardDto"}}}},"responses":{"200":{"description":"Greeting card updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleGreetingCardDto"}}}},"404":{"description":"Greeting card not found"}},"summary":"Update greeting card details","tags":["Greeting Cards"]},"get":{"description":"Returns full details of a single greeting card including recipients, SMS packs, and linked event information.","operationId":"GreetingCardController_findOne","parameters":[{"name":"id","required":true,"in":"path","description":"Greeting card ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Greeting card fetched successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleGreetingCardDto"}}}},"404":{"description":"Greeting card not found"}},"summary":"Get greeting card details","tags":["Greeting Cards"]},"delete":{"description":"Deletes a greeting card and all its recipients. Can only delete cards in DRAFT status.","operationId":"GreetingCardController_delete","parameters":[{"name":"id","required":true,"in":"path","description":"Greeting card ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Greeting card deleted successfully"},"400":{"description":"Cannot delete after processing"},"404":{"description":"Greeting card not found"}},"summary":"Delete greeting card","tags":["Greeting Cards"]}},"/api/greeting-cards/{id}/recipients":{"post":{"description":"Adds multiple recipients to the greeting card at once. Frontend handles CSV import and sends prepared recipient list. Each recipient needs name, channel (EMAIL/SMS), and appropriate contact info. Allowed in any status — call POST /:id/send afterwards to deliver to the new recipients only.","operationId":"GreetingCardController_addRecipients","parameters":[{"name":"id","required":true,"in":"path","description":"Greeting card ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddRecipientsDto"}}}},"responses":{"200":{"description":"Recipients added successfully"},"400":{"description":"Invalid recipient data"},"404":{"description":"Greeting card not found"}},"summary":"Add recipients to greeting card","tags":["Greeting Cards"]}},"/api/greeting-cards/{id}/checkout":{"post":{"description":"Creates a Stripe checkout session for payment. Base price: $19 (includes up to 100 SMS). Additional SMS packs: $10 per 50 recipients. Returns checkout URL to redirect user to Stripe.","operationId":"GreetingCardController_createCheckout","parameters":[{"name":"id","required":true,"in":"path","description":"Greeting card ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCheckoutDto"}}}},"responses":{"200":{"description":"Checkout session created"},"400":{"description":"Missing design or recipients, or card already processed"},"404":{"description":"Greeting card not found"}},"summary":"Create Stripe checkout session","tags":["Greeting Cards"]}},"/api/greeting-cards/{id}/payment/sync":{"post":{"description":"Looks up the host's recent Stripe checkout sessions and, if any of them targets this card and resolved to PAID, activates the card the same way the webhook would. Self-recovery for the rare case where the webhook is delayed, dropped, or the live/sandbox cutover dropped a delivery. Idempotent: re-syncing an already-active card is a no-op.","operationId":"GreetingCardController_syncGreetingCardPayment","parameters":[{"name":"id","required":true,"in":"path","description":"Greeting card ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Payment status checked (and possibly recovered)"}},"summary":"Manually reconcile a greeting card payment with Stripe","tags":["Greeting Cards"]}},"/api/greeting-cards/{id}/sms-packs/checkout":{"post":{"description":"Creates a Stripe checkout session to purchase extra SMS packs ($10 per pack of 50). Allowed in any status, including SENT — pair with POST /:id/recipients and POST /:id/send to add and deliver to new recipients after the original send.","operationId":"GreetingCardController_createSmsPackCheckout","parameters":[{"name":"id","required":true,"in":"path","description":"Greeting card ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSmsPackCheckoutDto"}}}},"responses":{"200":{"description":"Checkout session created"},"404":{"description":"Greeting card not found"}},"summary":"Buy additional SMS packs for an existing greeting card","tags":["Greeting Cards"]}},"/api/greeting-cards/{id}/send":{"post":{"description":"Sends the greeting card to recipients via email and SMS. Payment must be complete (status other than DRAFT). When the body is omitted (or `deliveryIds` is empty) all PENDING deliveries are sent — the default behaviour. When `deliveryIds` is supplied (Concern 6.6 send-more dialog), the send is restricted to those ids; any delivery in that list that has already been SENT / DELIVERED / FAILED is duplicated as a fresh PENDING row and re-dispatched so the audit trail of earlier sends is preserved.","operationId":"GreetingCardController_send","parameters":[{"name":"id","required":true,"in":"path","description":"Greeting card ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SendGreetingCardDto"}}}},"responses":{"200":{"description":"Greeting card sent successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleGreetingCardDto"}}}},"404":{"description":"Greeting card not found"}},"summary":"Send greeting card to pending recipients","tags":["Greeting Cards"]}},"/api/greeting-cards/public/{id}":{"get":{"description":"Returns design metadata for the public /g/[cardId] page. Unpaid drafts return 404 so hosts cannot share cards before payment.","operationId":"GreetingCardController_findPublicView","parameters":[{"name":"id","required":true,"in":"path","description":"Greeting card ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Greeting card is viewable"},"404":{"description":"Not found or not yet paid"}},"summary":"View a paid greeting card (public)","tags":["Greeting Cards"]}},"/api/greeting-cards/public/{id}/open":{"post":{"description":"Called when a recipient loads /g/[cardId] with `?d=<deliveryId>`. Counts toward host open stats (first open per delivery only).","operationId":"GreetingCardController_recordPublicOpen","parameters":[{"name":"id","required":true,"in":"path","description":"Greeting card ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RecordGreetingCardOpenDto"}}}},"responses":{"200":{"description":"Open recorded or already counted"},"404":{"description":"Not found or not yet paid"}},"summary":"Record greeting card link open (public)","tags":["Greeting Cards"]}},"/api/greeting-cards/{id}/deliveries":{"get":{"description":"Returns delivery tracking information for all recipients. Shows status (PENDING/SENT/DELIVERED/FAILED) and timestamps.","operationId":"GreetingCardController_getDeliveries","parameters":[{"name":"id","required":true,"in":"path","description":"Greeting card ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Deliveries fetched successfully","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/GreetingCardDeliveryResponseDto"}}}}},"404":{"description":"Greeting card not found"}},"summary":"Get greeting card delivery status","tags":["Greeting Cards"]}},"/api/role":{"get":{"operationId":"RoleController_findAll","parameters":[{"name":"page","required":false,"in":"query","description":"Page number","schema":{"default":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page","schema":{"default":10,"example":10,"type":"number"}},{"name":"sortBy","required":false,"in":"query","description":"Sort field","schema":{"example":"createdAt","type":"string"}},{"name":"sortOrder","required":false,"in":"query","description":"Sort order","schema":{"default":"desc","type":"string","enum":["asc","desc"]}},{"name":"search","required":false,"in":"query","description":"Search query","schema":{"example":"john","type":"string"}},{"name":"name","required":false,"in":"query","description":"The name of the role","schema":{"example":"Admin","type":"string"}},{"name":"description","required":false,"in":"query","description":"The description of the role","schema":{"example":"Administrator role with full access","type":"string"}}],"responses":{"200":{"description":"Roles fetched successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAllRoleDto"}}}}},"summary":"Get all roles","tags":["Role"]},"post":{"operationId":"RoleController_createRole","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRoleDto"}}}},"responses":{"201":{"description":"Role created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleRoleDto"}}}}},"summary":"Create a new role","tags":["Role"]}},"/api/role/{id}":{"get":{"operationId":"RoleController_findOne","parameters":[{"name":"id","required":true,"in":"path","description":"Role ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"responses":{"200":{"description":"Role fetched successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleRoleDto"}}}},"404":{"description":"Role not found"}},"summary":"Get a single role","tags":["Role"]},"patch":{"operationId":"RoleController_updateRole","parameters":[{"name":"id","required":true,"in":"path","description":"Role ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateRoleDto"}}}},"responses":{"200":{"description":"Role updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleRoleDto"}}}}},"summary":"Update a role","tags":["Role"]},"delete":{"operationId":"RoleController_deleteRole","parameters":[{"name":"id","required":true,"in":"path","description":"Role ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"responses":{"200":{"description":"Role deleted successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleRoleDto"}}}}},"summary":"Delete a role","tags":["Role"]}},"/api/role/{id}/permissions/add":{"post":{"operationId":"RoleController_addPermissionsToRole","parameters":[{"name":"id","required":true,"in":"path","description":"Role ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManageRolePermissionsDto"}}}},"responses":{"200":{"description":"Permissions added to role successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleRoleDto"}}}}},"summary":"Add permissions to a role","tags":["Role"]}},"/api/role/{id}/permissions/remove":{"post":{"operationId":"RoleController_removePermissionsFromRole","parameters":[{"name":"id","required":true,"in":"path","description":"Role ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ManageRolePermissionsDto"}}}},"responses":{"200":{"description":"Permissions removed from role successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleRoleDto"}}}}},"summary":"Remove permissions from a role","tags":["Role"]}},"/api/permissions":{"get":{"operationId":"PermissionController_findAll","parameters":[{"name":"page","required":false,"in":"query","description":"Page number","schema":{"default":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page","schema":{"default":10,"example":10,"type":"number"}},{"name":"sortBy","required":false,"in":"query","description":"Sort field","schema":{"example":"createdAt","type":"string"}},{"name":"sortOrder","required":false,"in":"query","description":"Sort order","schema":{"default":"desc","type":"string","enum":["asc","desc"]}},{"name":"search","required":false,"in":"query","description":"Search query","schema":{"example":"john","type":"string"}},{"name":"name","required":false,"in":"query","description":"The name of the permission","schema":{"example":"leads.read","type":"string"}},{"name":"description","required":false,"in":"query","description":"The description of the permission","schema":{"example":"Permission to read leads","type":"string"}}],"responses":{"200":{"description":"Permissions fetched successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAllPermissionDto"}}}}},"summary":"Get all permissions","tags":["Permission"]}},"/api/permissions/{id}":{"get":{"operationId":"PermissionController_findOne","parameters":[{"name":"id","required":true,"in":"path","description":"Permission ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"responses":{"200":{"description":"Permission fetched successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSinglePermissionDto"}}}},"404":{"description":"Permission not found"}},"summary":"Get a single permission","tags":["Permission"]}},"/api/user":{"get":{"operationId":"UserController_findAll","parameters":[{"name":"page","required":false,"in":"query","description":"Page number","schema":{"default":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page","schema":{"default":10,"example":10,"type":"number"}},{"name":"sortBy","required":false,"in":"query","description":"Sort field","schema":{"example":"createdAt","type":"string"}},{"name":"sortOrder","required":false,"in":"query","description":"Sort order","schema":{"default":"desc","type":"string","enum":["asc","desc"]}},{"name":"search","required":false,"in":"query","description":"Search query","schema":{"example":"john","type":"string"}},{"name":"name","required":false,"in":"query","description":"The name of the user","schema":{"example":"John Doe","type":"string"}},{"name":"email","required":false,"in":"query","description":"The email of the user","schema":{"example":"john@example.com","type":"string"}},{"name":"username","required":false,"in":"query","description":"The username of the user","schema":{"example":"johndoe","type":"string"}},{"name":"role","required":false,"in":"query","description":"Filter by role name","schema":{"example":"Admin","type":"string"}}],"responses":{"200":{"description":"Users fetched successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAllUserDto"}}}}},"summary":"Get all users","tags":["User"]},"post":{"operationId":"UserController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateUserDto"}}}},"responses":{"201":{"description":"User created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleUserDto"}}}},"409":{"description":"User with this email already exists"}},"summary":"Create a new user","tags":["User"]}},"/api/user/{id}":{"get":{"operationId":"UserController_findOne","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"responses":{"200":{"description":"User fetched successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleUserDto"}}}},"404":{"description":"User not found"}},"summary":"Get a single user","tags":["User"]},"patch":{"operationId":"UserController_update","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserDto"}}}},"responses":{"200":{"description":"User updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleUserDto"}}}},"404":{"description":"User not found"}},"summary":"Update a user (name and image only)","tags":["User"]},"delete":{"operationId":"UserController_delete","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"responses":{"200":{"description":"User deleted successfully"},"404":{"description":"User not found"}},"summary":"Delete a user and all related data","tags":["User"]}},"/api/user/{id}/roles":{"patch":{"operationId":"UserController_assignRoles","parameters":[{"name":"id","required":true,"in":"path","description":"User ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssignRolesDto"}}}},"responses":{"200":{"description":"User roles updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleUserDto"}}}},"400":{"description":"Invalid role IDs"},"404":{"description":"User not found"}},"summary":"Assign roles to a user","tags":["User"]}},"/api/user/{id}/subscription":{"patch":{"operationId":"UserController_updateSubscription","parameters":[{"name":"id","required":true,"in":"path","description":"Target user ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateUserSubscriptionDto"}}}},"responses":{"200":{"description":"Subscription updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserSubscriptionSummaryDto"}}}},"400":{"description":"Paid Stripe subscription requires confirmCancelPaidSub: true to downgrade"},"403":{"description":"Admin account required"}},"summary":"Admin: change a user's subscription plan (grant/revoke complimentary STUDIO)","tags":["User"]}},"/api/user/change-password":{"patch":{"operationId":"UserController_changePassword","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChangePasswordDto"}}}},"responses":{"200":{"description":"Password changed successfully"},"400":{"description":"Invalid password change request"},"401":{"description":"Current password is incorrect"}},"summary":"Change current user password","tags":["User"]}},"/api/user/me/recent-colors":{"get":{"operationId":"UserController_getRecentColors","parameters":[],"responses":{"200":{"description":"Recent colours fetched"}},"summary":"Get the current user's most-recently-used colours per field","tags":["User"]},"patch":{"operationId":"UserController_setRecentColors","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetRecentColorsDto"}}}},"responses":{"200":{"description":"Recent colours updated"}},"summary":"Replace the recent-colours list for a single colour-picker field on the current user","tags":["User"]}},"/api/user/me/reminder-defaults":{"get":{"description":"Six booleans (3 cadences × 2 channels) that the Settings → Messaging toggle card writes. Used as the fallback for any event whose own `remind*` column is NULL.","operationId":"UserController_getReminderDefaults","parameters":[],"responses":{"200":{"description":"Reminder defaults fetched"}},"summary":"Get the current host's default reminder cadence","tags":["User"]},"patch":{"operationId":"UserController_updateReminderDefaults","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateReminderDefaultsDto"}}}},"responses":{"200":{"description":"Reminder defaults updated"}},"summary":"Partially update the current host's default reminder cadence","tags":["User"]}},"/api/user/me/fonts":{"get":{"operationId":"UserFontsController_list","parameters":[],"responses":{"200":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/CustomFontResponseDto"}}}}}},"summary":"List the current user’s uploaded custom fonts","tags":["User Fonts"]},"post":{"operationId":"UserFontsController_register","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterCustomFontDto"}}}},"responses":{"200":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CustomFontResponseDto"}}}}},"summary":"Register a font that was already uploaded to storage","tags":["User Fonts"]}},"/api/user/me/fonts/{id}":{"delete":{"operationId":"UserFontsController_remove","parameters":[{"name":"id","required":true,"in":"path","schema":{"type":"string"}}],"responses":{"200":{"description":""}},"summary":"Delete one of the current user’s uploaded fonts","tags":["User Fonts"]}},"/api/onboarding":{"get":{"description":"Returns the host onboarding state including profile completion, first event creation, and completed steps.","operationId":"OnboardingController_getState","parameters":[],"responses":{"200":{"description":"Onboarding state fetched successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingStateResponseDto"}}}},"404":{"description":"User not found"}},"summary":"Get current onboarding state","tags":["Onboarding"]}},"/api/onboarding/complete-step":{"post":{"description":"Marks a specific onboarding step as complete. The firstEvent step is auto-set when the host creates their first event.","operationId":"OnboardingController_completeStep","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompleteStepDto"}}}},"responses":{"200":{"description":"Step marked complete successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OnboardingStateResponseDto"}}}},"400":{"description":"Invalid step"},"404":{"description":"User not found"}},"summary":"Mark onboarding step complete","tags":["Onboarding"]}},"/api/custom-category":{"get":{"operationId":"CustomCategoryController_findAll","parameters":[{"name":"page","required":false,"in":"query","description":"Page number","schema":{"default":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page","schema":{"default":10,"example":10,"type":"number"}},{"name":"sortBy","required":false,"in":"query","description":"Sort field","schema":{"example":"createdAt","type":"string"}},{"name":"sortOrder","required":false,"in":"query","description":"Sort order","schema":{"default":"desc","type":"string","enum":["asc","desc"]}},{"name":"search","required":false,"in":"query","description":"Search query","schema":{"example":"john","type":"string"}},{"name":"code","required":false,"in":"query","description":"The code of the custom category","schema":{"example":"rsvp_status","type":"string"}},{"name":"name","required":false,"in":"query","description":"The name of the custom category","schema":{"example":"RSVP Status","type":"string"}},{"name":"isSystem","required":false,"in":"query","description":"Whether this is a system category","schema":{"example":false,"type":"boolean"}}],"responses":{"200":{"description":"Custom categories fetched successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetAllCustomCategoryDto"}}}}},"summary":"Get all custom categories with pagination","tags":["Custom Category"]},"post":{"operationId":"CustomCategoryController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCustomCategoryDto"}}}},"responses":{"201":{"description":"Custom category created successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleCustomCategoryDto"}}}}},"summary":"Create a new custom category","tags":["Custom Category"]}},"/api/custom-category/code/{code}":{"get":{"operationId":"CustomCategoryController_findOneByCode","parameters":[{"name":"code","required":true,"in":"path","description":"Category code","schema":{"example":"rsvp_status","type":"string"}}],"responses":{"200":{"description":"Custom category fetched successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleCustomCategoryDto"}}}},"404":{"description":"Custom category not found"}},"summary":"Get a single custom category by code with all options","tags":["Custom Category"]}},"/api/custom-category/{id}":{"get":{"operationId":"CustomCategoryController_findOne","parameters":[{"name":"id","required":true,"in":"path","description":"Category ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"responses":{"200":{"description":"Custom category fetched successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleCustomCategoryDto"}}}},"404":{"description":"Custom category not found"}},"summary":"Get a single custom category with all options","tags":["Custom Category"]},"patch":{"operationId":"CustomCategoryController_update","parameters":[{"name":"id","required":true,"in":"path","description":"Category ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCustomCategoryDto"}}}},"responses":{"200":{"description":"Custom category updated successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetSingleCustomCategoryDto"}}}}},"summary":"Update a custom category","tags":["Custom Category"]},"delete":{"operationId":"CustomCategoryController_delete","parameters":[{"name":"id","required":true,"in":"path","description":"Category ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"responses":{"200":{"description":"Custom category deleted successfully"}},"summary":"Delete a custom category","tags":["Custom Category"]}},"/api/custom-category/{categoryId}/options":{"post":{"operationId":"CustomCategoryController_createOption","parameters":[{"name":"categoryId","required":true,"in":"path","description":"Category ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateCustomCategoryOptionDto"}}}},"responses":{"201":{"description":"Custom category option created successfully"}},"summary":"Create a new option for a custom category","tags":["Custom Category"]}},"/api/custom-category/{categoryId}/options/{optionId}":{"patch":{"operationId":"CustomCategoryController_updateOption","parameters":[{"name":"categoryId","required":true,"in":"path","description":"Category ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}},{"name":"optionId","required":true,"in":"path","description":"Option ID","schema":{"example":"cmieqoaba0000iymt4fab0346","type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateCustomCategoryOptionDto"}}}},"responses":{"200":{"description":"Custom category option updated successfully"}},"summary":"Update a custom category option","tags":["Custom Category"]},"delete":{"operationId":"CustomCategoryController_deleteOption","parameters":[{"name":"categoryId","required":true,"in":"path","description":"Category ID","schema":{"example":"cmieqoaba0000iymt4fab0345","type":"string"}},{"name":"optionId","required":true,"in":"path","description":"Option ID","schema":{"example":"cmieqoaba0000iymt4fab0346","type":"string"}}],"responses":{"200":{"description":"Custom category option deleted successfully"}},"summary":"Delete a custom category option","tags":["Custom Category"]}},"/api/ai-extract":{"post":{"description":"Upload a file (multipart/form-data) or provide an imageUrl (JSON body). Use mode=full to extract all event data, or mode=colors for just the colour palette.","operationId":"AiExtractController_extract","parameters":[],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","description":"Event poster/invitation image or PDF"},"imageUrl":{"type":"string","description":"URL of an online event image (alternative to file)"},"mode":{"type":"string","enum":["full","colors"],"default":"full","description":"Extraction mode"}}}},"application/json":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","description":"Event poster/invitation image or PDF"},"imageUrl":{"type":"string","description":"URL of an online event image (alternative to file)"},"mode":{"type":"string","enum":["full","colors"],"default":"full","description":"Extraction mode"}}}}}},"responses":{"200":{"description":"Successfully extracted event data","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExtractResponseDto"}}}},"400":{"description":"Bad request (no image, invalid file type)"},"429":{"description":"Rate limit exceeded"},"500":{"description":"AI processing error"}},"summary":"Extract event data or colours from an image/PDF","tags":["AI Extract"]}},"/api/health/live":{"get":{"operationId":"HealthController_liveness","parameters":[],"responses":{"200":{"description":"The Health Check is successful","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"ok"},"info":{"type":"object","example":{"database":{"status":"up"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"error":{"type":"object","example":{},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"details":{"type":"object","example":{"database":{"status":"up"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true}}}}}}},"503":{"description":"The Health Check is not successful","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"error"},"info":{"type":"object","example":{"database":{"status":"up"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"error":{"type":"object","example":{"redis":{"status":"down","message":"Could not connect"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"details":{"type":"object","example":{"database":{"status":"up"},"redis":{"status":"down","message":"Could not connect"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true}}}}}}}},"summary":"Liveness probe","tags":["Health"]}},"/api/health/ready":{"get":{"operationId":"HealthController_readiness","parameters":[],"responses":{"200":{"description":"The Health Check is successful","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"ok"},"info":{"type":"object","example":{"database":{"status":"up"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"error":{"type":"object","example":{},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"details":{"type":"object","example":{"database":{"status":"up"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true}}}}}}},"503":{"description":"The Health Check is not successful","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"error"},"info":{"type":"object","example":{"database":{"status":"up"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"error":{"type":"object","example":{"redis":{"status":"down","message":"Could not connect"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"details":{"type":"object","example":{"database":{"status":"up"},"redis":{"status":"down","message":"Could not connect"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true}}}}}}}},"summary":"Readiness probe","tags":["Health"]}},"/api/health/deep":{"get":{"operationId":"HealthController_deepCheck","parameters":[],"responses":{"200":{"description":"The Health Check is successful","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"ok"},"info":{"type":"object","example":{"database":{"status":"up"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"error":{"type":"object","example":{},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"details":{"type":"object","example":{"database":{"status":"up"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true}}}}}}},"503":{"description":"The Health Check is not successful","content":{"application/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"error"},"info":{"type":"object","example":{"database":{"status":"up"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"error":{"type":"object","example":{"redis":{"status":"down","message":"Could not connect"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true},"nullable":true},"details":{"type":"object","example":{"database":{"status":"up"},"redis":{"status":"down","message":"Could not connect"}},"additionalProperties":{"type":"object","required":["status"],"properties":{"status":{"type":"string"}},"additionalProperties":true}}}}}}}},"summary":"Deep health check for debugging","tags":["Health"]}},"/api/schedules/jobs":{"get":{"operationId":"SchedulerController_getJobs","parameters":[],"responses":{"default":{"description":"","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ScheduleJobResponseDto"}}}}}},"security":[{"bearer":[]}],"summary":"List all runnable scheduled jobs","tags":["Schedules"]}},"/api/schedules/trigger":{"post":{"operationId":"SchedulerController_triggerJob","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TriggerScheduleDto"}}}},"responses":{"default":{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleTriggerResponseDto"}}}}},"security":[{"bearer":[]}],"summary":"Manually trigger a scheduled job","tags":["Schedules"]}},"/api/dashboard/kpis":{"get":{"description":"Returns aggregated KPIs for the authenticated user including event counts, guest response aggregates, headcount, communication stats, recent events, and upcoming events.","operationId":"DashboardController_getKpis","parameters":[],"responses":{"200":{"description":"Dashboard KPIs fetched successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetDashboardKpiDto"}}}}},"summary":"Get dashboard KPIs","tags":["Dashboard"]}},"/api/dashboard/admin-kpis":{"get":{"description":"Returns org-wide aggregated KPIs for admins including revenue, user stats, event stats, subscriptions, and recent activity.","operationId":"DashboardController_getAdminKpis","parameters":[],"responses":{"200":{"description":"Admin dashboard KPIs fetched successfully","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminKpiResponseDto"}}}}},"summary":"Get admin dashboard KPIs","tags":["Dashboard"]}},"/api/support-tickets/contact-inquiry":{"post":{"description":"Public, unauthenticated endpoint. Creates a support ticket in the configured internal org with sender data on `metadata`.","operationId":"SupportTicketController_submitContactInquiry","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateContactInquiryDto"}}}},"responses":{"201":{"description":"Inquiry received"},"429":{"description":"Rate limit exceeded"}},"summary":"Submit a contact inquiry from the public marketing site","tags":["Support Tickets"]}},"/api/support-tickets":{"post":{"operationId":"SupportTicketController_create","parameters":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateSupportTicketDto"}}}},"responses":{"201":{"description":"Support ticket created successfully"}},"summary":"Create a new support ticket","tags":["Support Tickets"]},"get":{"operationId":"SupportTicketController_findAll","parameters":[{"name":"page","required":false,"in":"query","description":"Page number","schema":{"default":1,"example":1,"type":"number"}},{"name":"limit","required":false,"in":"query","description":"Items per page","schema":{"default":10,"example":10,"type":"number"}},{"name":"sortBy","required":false,"in":"query","description":"Sort field","schema":{"example":"createdAt","type":"string"}},{"name":"sortOrder","required":false,"in":"query","description":"Sort order","schema":{"default":"desc","type":"string","enum":["asc","desc"]}},{"name":"search","required":false,"in":"query","description":"Search query","schema":{"example":"john","type":"string"}},{"name":"status","required":false,"in":"query","description":"Filter by ticket status","schema":{"example":"OPEN","type":"string","enum":["OPEN","IN_PROGRESS","RESOLVED","CLOSED"]}},{"name":"category","required":false,"in":"query","description":"Filter by ticket category","schema":{"example":"TECHNICAL","type":"string","enum":["BILLING","EVENTS","TECHNICAL","CONTACT_INQUIRY","OTHER"]}},{"name":"priority","required":false,"in":"query","description":"Filter by ticket priority","schema":{"example":"NORMAL","type":"string","enum":["LOW","NORMAL","HIGH","URGENT"]}}],"responses":{"200":{"description":"Support tickets fetched successfully"}},"summary":"List support tickets","tags":["Support Tickets"]}},"/api/support-tickets/{id}":{"get":{"operationId":"SupportTicketController_findOne","parameters":[{"name":"id","required":true,"in":"path","description":"Support ticket ID","schema":{"type":"string"}}],"responses":{"200":{"description":"Support ticket fetched successfully"},"404":{"description":"Support ticket not found"}},"summary":"Get a support ticket with messages","tags":["Support Tickets"]},"patch":{"operationId":"SupportTicketController_update","parameters":[{"name":"id","required":true,"in":"path","description":"Support ticket ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateSupportTicketDto"}}}},"responses":{"200":{"description":"Support ticket updated successfully"},"404":{"description":"Support ticket not found"}},"summary":"Update a support ticket (admin only)","tags":["Support Tickets"]}},"/api/support-tickets/{id}/reply":{"post":{"operationId":"SupportTicketController_reply","parameters":[{"name":"id","required":true,"in":"path","description":"Support ticket ID","schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplySupportTicketDto"}}}},"responses":{"201":{"description":"Reply sent successfully"},"404":{"description":"Support ticket not found"}},"summary":"Reply to a support ticket","tags":["Support Tickets"]}}},"info":{"title":"RSVPeasy API","description":"RSVPeasy documentation.\n\n## Authentication\n\nThis API uses session-based authentication with Bearer token support. Sessions are valid for 30 days and auto-refresh on activity.\n\n### Getting Started\n1. **Get an Organization ID** - Call `GET /api/organizations` to list available organizations\n2. **Sign Up/Sign In** - Use one of the authentication methods below\n3. **Use the token** - Include `Authorization: Bearer <session-token>` in requests\n\n### Email/Password Authentication\n- `POST /api/auth/sign-up/email` - Register with email/password\n- `POST /api/auth/sign-in/email` - Login with email/password\n\n### Email OTP (Passwordless)\n- `POST /api/auth/email-otp/send-verification-otp` - Send OTP to email (type: sign-in, email-verification, forget-password)\n- `POST /api/auth/sign-in/email-otp` - Sign in with email + OTP code\n- `POST /api/auth/email-otp/verify-email` - Verify email address with OTP\n- `POST /api/auth/email-otp/reset-password` - Reset password with OTP\n\n### Phone/SMS OTP (Passwordless)\n- `POST /api/auth/phone-number/send-otp` - Send OTP to phone number\n- `POST /api/auth/phone-number/verify` - Verify phone and sign in (auto-registers new users)\n- `POST /api/auth/phone-number/request-password-reset` - Request password reset via SMS\n- `POST /api/auth/phone-number/reset-password` - Reset password with SMS OTP\n\n### OAuth / Social Login\n- `GET /api/auth/sign-in/social?provider=google` - OAuth with Google\n\n### Session Management\n- `GET /api/auth/get-session` - Get current session and user info\n- `POST /api/auth/sign-out` - Logout and invalidate session\n- `GET /api/auth/list-sessions` - List all active sessions\n- `POST /api/auth/revoke-session` - Revoke a specific session\n\n### JWT Tokens (for service-to-service)\n- `GET /api/auth/token` - Get short-lived JWT from active session (15 min expiry)\n- `GET /api/auth/jwks` - Get public keys for JWT verification\n\n### Mobile Integration Notes\n- **Session lifetime**: 30 days, auto-extends on any API activity\n- **Silent refresh**: Sessions auto-refresh when `updateAge` (24h) is reached - no user action needed\n- **Secure storage**: Store session token in Keychain (iOS) / Keystore (Android)\n- **API calls**: Include `Authorization: Bearer <session-token>` header\n- **Session check**: On app launch, call `GET /api/auth/get-session` to verify token validity\n- **Rare expiration**: If session expires (30+ days inactive), use passwordless login:\n  - Email OTP: Send code to user's email, sign in without password\n  - SMS OTP: Send code to user's phone, sign in without password\n- **Biometric unlock**: Store session token behind biometric lock for seamless UX\n\n**Note:** All endpoints except auth and public organization routes require the `x-org-id` header.\n","version":"1.0","contact":{}},"tags":[],"servers":[],"components":{"securitySchemes":{"bearer-auth":{"scheme":"bearer","bearerFormat":"JWT","type":"http","name":"Authorization","description":"Enter your Bearer token","in":"header"},"x-org-id":{"type":"apiKey","in":"header","name":"x-org-id","description":"Organization ID (required for most endpoints)"}},"schemas":{"CreateCheckoutDto":{"type":"object","properties":{"additionalSmsPacks":{"type":"number","description":"Number of additional SMS packs to purchase (each pack = 50 SMS)","example":2,"minimum":0},"successUrl":{"type":"string","description":"Success URL to redirect after payment","example":"https://example.com/greeting-cards/success"},"cancelUrl":{"type":"string","description":"Cancel URL to redirect if payment is cancelled","example":"https://example.com/greeting-cards/cancel"}}},"CreateBillingPortalDto":{"type":"object","properties":{"returnUrl":{"type":"string","description":"Where Stripe sends the customer after they leave the billing portal. Must match an allowed app origin (FRONTEND_URL / CORS_ORIGIN / TRUSTED_ORIGINS).","example":"https://app.rsvpeasy.com/host/settings?tab=subscription&portal=return"}}},"CancelSubscriptionResponseDto":{"type":"object","properties":{"cancelAtPeriodEnd":{"type":"boolean","description":"Whether the subscription is set to cancel at period end","example":true},"currentPeriodEnd":{"type":"string","description":"ISO timestamp when Studio access ends","example":"2026-06-01T00:00:00.000Z"}},"required":["cancelAtPeriodEnd","currentPeriodEnd"]},"UsageMetricDto":{"type":"object","properties":{"metric":{"type":"string","description":"Metric key","example":"EVENT_CREATED"},"used":{"type":"number","description":"Current usage count","example":3},"limit":{"type":"object","description":"Maximum allowed (null = unlimited)","example":10},"percentage":{"type":"object","description":"Usage percentage (0-100)","example":30}},"required":["metric","used"]},"SmsCreditSummaryDto":{"type":"object","properties":{"remaining":{"type":"number","description":"Total remaining SMS credits","example":200},"total":{"type":"number","description":"Total SMS credits allocated","example":250},"used":{"type":"number","description":"Used credits","example":50}},"required":["remaining","total","used"]},"UsageSummaryResponseDto":{"type":"object","properties":{"tierCode":{"type":"string","description":"Current billing tier code","example":"PREMIUM"},"tierName":{"type":"string","description":"Current billing tier name","example":"Premium"},"metrics":{"description":"Usage breakdown per metric","type":"array","items":{"$ref":"#/components/schemas/UsageMetricDto"}},"smsCredits":{"description":"SMS credit summary","allOf":[{"$ref":"#/components/schemas/SmsCreditSummaryDto"}]},"dailyEmailsSent":{"type":"number","description":"Daily emails sent today","example":42},"dailyEmailLimit":{"type":"number","description":"Daily email limit","example":500},"periodStart":{"type":"string","description":"Billing period start","example":"2026-03-01T00:00:00.000Z"},"periodEnd":{"type":"string","description":"Billing period end","example":"2026-03-31T23:59:59.999Z"},"subscriptionStatus":{"type":"object","description":"Stripe subscription status when the user has a subscription","example":"ACTIVE","nullable":true},"cancelAtPeriodEnd":{"type":"boolean","description":"True when cancellation is scheduled at the end of the current billing period","example":false},"subscriptionPeriodEnd":{"type":"object","description":"End of the current Stripe subscription period (when cancellation takes effect)","example":"2026-06-03T00:00:00.000Z","nullable":true}},"required":["tierCode","tierName","metrics","smsCredits","dailyEmailsSent","dailyEmailLimit","periodStart","periodEnd"]},"EventUsageSummaryDto":{"type":"object","properties":{"eventId":{"type":"string","description":"Event ID","example":"cmieqoaba0000iymt4fab0345"},"eventName":{"type":"string","description":"Event name","example":"Summer Gala"},"tier":{"type":"string","description":"Current tier","example":"PREMIUM"},"guestCount":{"type":"number","description":"Guest count","example":45},"guestLimit":{"type":"number","description":"Guest limit","example":250},"smsSentCount":{"type":"number","description":"SMS sent count","example":12},"emailSentCount":{"type":"number","description":"Email sent count","example":40},"invitationsSentCount":{"type":"number","description":"Invitations sent count","example":45},"remindersSentCount":{"type":"number","description":"Reminders sent count","example":5}},"required":["eventId","eventName","tier","guestCount","guestLimit","smsSentCount","emailSentCount","invitationsSentCount","remindersSentCount"]},"GenerateUploadUrlDto":{"type":"object","properties":{"uploadType":{"type":"string","enum":["event_invitation","greeting_card_design","profile_avatar","custom_font"],"description":"Type of file being uploaded"},"filename":{"type":"string","description":"Original filename","example":"invitation.jpg"},"contentType":{"type":"string","description":"MIME type","example":"image/jpeg"},"resourceId":{"type":"string","description":"Resource ID (eventId or cardId)"},"fileSize":{"type":"number","description":"File size in bytes for validation","example":1048576}},"required":["uploadType","filename","contentType"]},"ConfirmUploadDto":{"type":"object","properties":{"key":{"type":"string","description":"S3 key returned from upload URL generation"},"resourceId":{"type":"string","description":"Resource ID to attach the URL to (eventId or cardId)"},"uploadType":{"type":"string","description":"Upload type for resource validation"}},"required":["key"]},"ConfirmUploadResponseDto":{"type":"object","properties":{}},"CreateAccommodationDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the accommodation","example":"The Ritz-Carlton","minLength":1,"maxLength":300},"imageUrl":{"type":"string","description":"URL of the accommodation image (16:9 ratio preferred)","example":"https://cdn.rsvpeasy.com/accommodations/hotel.jpg"},"description":{"type":"string","description":"Short description of the accommodation","example":"A luxurious 5-star hotel just minutes from the venue","maxLength":2000},"bookingLink":{"type":"string","description":"URL for guests to book this accommodation","example":"https://www.marriott.com/reservation?groupCode=WEDDING"},"isVisible":{"type":"boolean","description":"Whether this accommodation is visible on the RSVP page","example":true,"default":true}},"required":["name"]},"CreateFormFieldDto":{"type":"object","properties":{"label":{"type":"string","description":"Display label for the form field","example":"Dietary restrictions","minLength":1,"maxLength":200},"placeholder":{"type":"string","description":"Placeholder text for the input","example":"e.g., Vegetarian, Gluten-free","maxLength":200},"helpText":{"type":"string","description":"Help text shown below the field","example":"Please let us know about any dietary requirements","maxLength":500},"fieldType":{"type":"string","description":"Type of the form field","enum":["TEXT","EMAIL","PHONE","NUMBER","BOOLEAN","DROPDOWN","DATE","TEXTAREA"],"example":"TEXT"},"isRequired":{"type":"boolean","description":"Whether this field is required","example":false,"default":false},"isActive":{"type":"boolean","description":"Whether this field is visible on the RSVP form","example":true,"default":true},"sortOrder":{"type":"number","description":"Sort order for display (lower = first)","example":0,"default":0},"scope":{"type":"string","description":"Question scope. `RSVP` (default) asks once per submission; `GUEST` repeats the question for the primary guest and every plus-one (Anna section 5.3).","enum":["RSVP","GUEST"],"example":"RSVP","default":"RSVP"},"options":{"description":"Dropdown options for DROPDOWN fields (stored inline as a JSON array)","example":["Vegetarian","Vegan"],"type":"array","items":{"type":"string"}}},"required":["label","fieldType"]},"CreateGuestDto":{"type":"object","properties":{"name":{"type":"string","description":"Guest full name","example":"Jane Smith","minLength":2,"maxLength":200},"email":{"type":"string","description":"Guest email address (optional — some hosts may only have phone)","example":"jane.smith@example.com"},"phone":{"type":"string","description":"Guest mobile number (defaults to AU when no country code is given; international numbers in E.164 format are accepted)","example":"+61412345678","maxLength":30},"addressee":{"type":"string","description":"Display name for the invitation (e.g., \"The Smith Family\")","example":"The Smith Family","maxLength":200},"partySize":{"type":"number","description":"Number of people in the party (maps to adultCount)","example":1,"default":1,"minimum":1,"maximum":50}},"required":["name"]},"CreateEventDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the event","example":"Sarah & John Wedding","minLength":2,"maxLength":200},"eventDate":{"format":"date-time","type":"string","description":"Date of the event (ISO 8601 format)","example":"2026-06-15T00:00:00.000Z"},"startTime":{"format":"date-time","type":"string","description":"Start time of the event (ISO 8601 format)","example":"2026-06-15T14:00:00.000Z"},"rsvpBy":{"format":"date-time","type":"string","description":"RSVP deadline (ISO 8601 format). Must be before eventDate.","example":"2026-06-01T23:59:59.000Z"},"locationName":{"type":"string","description":"Name of the event location/venue","example":"The Grand Ballroom","maxLength":300},"slug":{"type":"string","description":"Custom URL slug for the event. Must be lowercase alphanumeric with hyphens. If omitted, a slug is auto-generated from the event name.","example":"sarah-and-john-wedding","maxLength":100},"address":{"type":"string","description":"Full address of the event location","example":"123 Main St, New York, NY 10001","maxLength":500},"hostMessage":{"type":"string","description":"Personal message from the host displayed on the RSVP page","example":"We are thrilled to invite you to celebrate our special day with us!","maxLength":5000},"maxPublicPartySize":{"type":"number","description":"Optional cap on total people (primary + additional guests) per public RSVP submission. Omit or set null for no cap. Edit-token RSVPs are governed by Guest.maxPartySize and are unaffected.","example":6,"minimum":1},"invitationUrl":{"type":"string","description":"URL to the uploaded invitation image/media (S3/CDN URL)","example":"https://cdn.rsvpeasy.com/invitations/my-invite.jpg"},"backgroundColor":{"type":"string","description":"Background color for the RSVP page (hex code)","example":"#FFFFFF","maxLength":20},"textColor":{"type":"string","description":"Text color for the RSVP page (hex code)","example":"#333333","maxLength":20},"accentColor":{"type":"string","description":"Accent color for buttons and highlights (hex code)","example":"#D4AF37","maxLength":20},"fontFamily":{"type":"string","description":"Font family for the RSVP page","example":"Playfair Display","maxLength":100},"showAnimation":{"type":"boolean","description":"Whether to show entrance animation on the RSVP page","example":false,"default":false},"isDropShadowRequired":{"type":"boolean","description":"Whether to apply a drop shadow to the invitation image","example":false,"default":false},"hideEventDetails":{"type":"boolean","description":"Hide the structured date/time/location block on the public RSVP page (useful when the invitation image already shows them)","example":false,"default":false},"remindOneWeekEmail":{"type":"boolean","description":"Send an email reminder one week before the event date.","default":true},"remindOneWeekSms":{"type":"boolean","description":"Send an SMS reminder one week before the event date.","default":false},"remindTwoDayEmail":{"type":"boolean","description":"Send an email reminder two days before the event date.","default":true},"remindTwoDaySms":{"type":"boolean","description":"Send an SMS reminder two days before the event date.","default":false},"remindDayOfEmail":{"type":"boolean","description":"Send an email reminder on the day of the event.","default":true},"remindDayOfSms":{"type":"boolean","description":"Send an SMS reminder on the day of the event.","default":false},"acceptResponseText":{"type":"string","description":"Custom text shown when guest accepts the invitation","example":"We're so glad you can make it!","maxLength":500},"declineResponseText":{"type":"string","description":"Custom text shown when guest declines the invitation","example":"We'll miss you!","maxLength":500},"headerFont":{"type":"string","description":"Header font name (e.g. display font)","example":"Playfair Display","maxLength":100},"bodyFont":{"type":"string","description":"Body font name (e.g. body text font)","example":"Open Sans","maxLength":100},"headerFontFamily":{"type":"string","description":"Header font family for the RSVP page","example":"Playfair Display, serif","maxLength":150},"bodyFontFamily":{"type":"string","description":"Body font family for the RSVP page","example":"Open Sans, sans-serif","maxLength":150},"headerSize":{"type":"string","description":"Header font size (e.g. \"24px\", \"1.5rem\")","example":"24px","maxLength":20},"bodySize":{"type":"string","description":"Body font size (e.g. \"16px\", \"1rem\")","example":"16px","maxLength":20},"buttonTextColor":{"type":"string","description":"Button text color (hex code)","example":"#FFFFFF","maxLength":20},"buttonBorderColor":{"type":"string","description":"Button border color (hex code)","example":"#D4AF37","maxLength":20},"buttonBorderRadius":{"type":"string","description":"Button border radius (e.g. \"8px\", \"0.5rem\")","example":"8px","maxLength":20},"buttonBgColor":{"type":"string","description":"Button background color (hex code)","example":"#D4AF37","maxLength":20},"headerTextColor":{"type":"string","description":"Header text color (hex code)","example":"#333333","maxLength":20},"accommodations":{"description":"List of accommodation options for this event","type":"array","items":{"$ref":"#/components/schemas/CreateAccommodationDto"}},"formFields":{"description":"Custom form fields for the RSVP page. Controls show/hide and required/optional per field.","type":"array","items":{"$ref":"#/components/schemas/CreateFormFieldDto"}},"guests":{"description":"Pre-loaded guest list. Each guest gets a unique RSVP token on creation. Frontend can populate this from manual entry, CSV import, or phone contacts.","type":"array","items":{"$ref":"#/components/schemas/CreateGuestDto"}},"registryLinkName":{"type":"string","description":"Display name for the registry link","example":"Our Wedding Registry","maxLength":300},"registryUrl":{"type":"string","description":"URL to the gift registry (https:// is added if no protocol is provided)","example":"https://www.zola.com/registry/sarahandjohn2026"}},"required":["name","eventDate","startTime","rsvpBy"]},"EventHostDto":{"type":"object","properties":{"id":{"type":"string","description":"Host user ID"},"name":{"type":"string","description":"Host display name"},"email":{"type":"string","description":"Host email address"},"image":{"type":"string","description":"Host avatar URL"}},"required":["id","email"]},"AccommodationResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Accommodation ID","example":"cmieqoaba0000iymt4fab9999"},"name":{"type":"string","description":"Accommodation name","example":"The Ritz-Carlton"},"imageUrl":{"type":"string","description":"Image URL (16:9 preferred)"},"description":{"type":"string","description":"Short description"},"bookingLink":{"type":"string","description":"Booking link URL"},"isVisible":{"type":"boolean","description":"Whether visible on RSVP page","example":true},"createdAt":{"format":"date-time","type":"string","description":"When the record was created"},"updatedAt":{"format":"date-time","type":"string","description":"When the record was last updated"}},"required":["id","name","isVisible","createdAt","updatedAt"]},"FormFieldResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Form field ID","example":"cmieqoaba0000iymt4fab9999"},"label":{"type":"string","description":"Display label","example":"Dietary restrictions"},"placeholder":{"type":"string","description":"Placeholder text"},"helpText":{"type":"string","description":"Help text shown below the field"},"fieldType":{"type":"string","description":"Field type","example":"TEXT"},"isRequired":{"type":"boolean","description":"Whether the field is required","example":false},"isActive":{"type":"boolean","description":"Whether the field is visible on the RSVP form","example":true},"sortOrder":{"type":"number","description":"Sort order for display","example":0},"scope":{"type":"string","description":"Question scope. `RSVP` asks once per submission; `GUEST` repeats the question for the primary guest and every plus-one (Anna section 5.3).","example":"RSVP"},"options":{"description":"Dropdown options for DROPDOWN fields (inline JSON array)","example":["Vegetarian","Vegan","Gluten-free"],"type":"array","items":{"type":"string"}}},"required":["id","label","fieldType","isRequired","isActive","sortOrder","scope"]},"GuestFieldResponseDto":{"type":"object","properties":{"fieldId":{"type":"string","description":"Form field ID","example":"cmieqoaba0000iymt4fab9999"},"label":{"type":"string","description":"Field label","example":"Dietary restrictions"},"fieldType":{"type":"string","description":"Field type","example":"TEXT"},"value":{"type":"object","description":"Response value","example":"Vegetarian"}},"required":["fieldId","label","fieldType","value"]},"AdditionalGuestResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Additional guest ID","example":"cmieqoaba0000iymt4fab0456"},"name":{"type":"string","description":"Additional guest full name","example":"Mia Smith"},"ageType":{"type":"string","description":"Additional guest age type","example":"ADULT"},"dietaryRestrictions":{"type":"string","description":"Additional guest dietary restrictions","example":"Vegetarian"}},"required":["id","name","ageType"]},"GuestRsvpResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Guest ID","example":"cmieqoaba0000iymt4fab0345"},"name":{"type":"string","description":"Guest name","example":"Jane Smith"},"email":{"type":"string","description":"Guest email (optional — phone-only guests can be invited via SMS)","example":"jane.smith@example.com"},"status":{"type":"string","description":"RSVP status","example":"ATTENDING"},"adultCount":{"type":"number","description":"Number of adults","example":1},"childCount":{"type":"number","description":"Number of children","example":0},"maxPartySize":{"type":"number","description":"Host-set ceiling on total people in this guest's party (primary + additional). Used by the RSVP form to cap the number of additional guests the guest can add.","example":1},"addressee":{"type":"string","description":"Display name for the invitation (e.g., \"The Smith Family\")","example":"The Smith Family"},"phone":{"type":"string","description":"Guest phone number","example":"+15551234567"},"editToken":{"type":"string","description":"Token for guest to edit RSVP later (if generated)"},"responses":{"description":"Form field responses (returned when fetching RSVP by token)","type":"array","items":{"$ref":"#/components/schemas/GuestFieldResponseDto"}},"formFields":{"description":"Active form fields for this event (returned when fetching RSVP by token)","type":"array","items":{"type":"string"}},"additionalGuests":{"description":"Additional guests attached to this RSVP","type":"array","items":{"$ref":"#/components/schemas/AdditionalGuestResponseDto"}}},"required":["id","name","status","adultCount","childCount"]},"EventResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Unique event identifier","example":"cmieqoaba0000iymt4fab0345"},"orgId":{"type":"string","description":"Organization ID this event belongs to","example":"cmieqoaba0000iymt4fab1234"},"userId":{"type":"string","description":"User ID of the event host/owner","example":"cmieqoaba0000iymt4fab5678"},"host":{"description":"Host/owner details (present when viewing all events)","allOf":[{"$ref":"#/components/schemas/EventHostDto"}]},"lastUpdatedBy":{"description":"User who last updated the event (host or admin acting on behalf). Present when viewing all events.","allOf":[{"$ref":"#/components/schemas/EventHostDto"}]},"slug":{"type":"string","description":"URL-friendly unique slug","example":"sarah-and-john-wedding-abc123"},"status":{"type":"string","description":"Current event status","example":"DRAFT"},"name":{"type":"string","description":"Name of the event","example":"Sarah & John Wedding"},"eventDate":{"format":"date-time","type":"string","description":"Date of the event","example":"2026-06-15T00:00:00.000Z"},"startTime":{"format":"date-time","type":"string","description":"Start time of the event","example":"2026-06-15T14:00:00.000Z"},"rsvpBy":{"format":"date-time","type":"string","description":"RSVP deadline","example":"2026-06-01T23:59:59.000Z"},"locationName":{"type":"string","description":"Name of the event venue","example":"The Grand Ballroom"},"address":{"type":"string","description":"Full address of the event venue","example":"123 Main St, New York, NY 10001"},"hostMessage":{"type":"string","description":"Personal message from the host","example":"We are thrilled to invite you to celebrate with us!"},"maxPublicPartySize":{"type":"object","description":"Optional ceiling on total people (primary + additional guests) per public RSVP submission. Null = no cap.","example":6},"invitationUrl":{"type":"string","description":"URL of the uploaded invitation"},"backgroundColor":{"type":"string","description":"Background color (hex)","example":"#FFFFFF"},"textColor":{"type":"string","description":"Text color (hex)","example":"#333333"},"accentColor":{"type":"string","description":"Accent color (hex)","example":"#D4AF37"},"fontFamily":{"type":"string","description":"Font family","example":"Playfair Display"},"showAnimation":{"type":"boolean","description":"Show animation on RSVP page","example":false},"isDropShadowRequired":{"type":"boolean","description":"Whether drop shadow is applied to the invitation image","example":false},"hideEventDetails":{"type":"boolean","description":"Hide the structured date/time/location block on the public RSVP page","example":false},"remindOneWeekEmail":{"type":"object","description":"Email reminder one week before. `null` = inherit host default.","example":true,"nullable":true},"remindOneWeekSms":{"type":"object","description":"SMS reminder one week before. `null` = inherit host default.","example":false,"nullable":true},"remindTwoDayEmail":{"type":"object","description":"Email reminder two days before. `null` = inherit host default.","example":true,"nullable":true},"remindTwoDaySms":{"type":"object","description":"SMS reminder two days before. `null` = inherit host default.","example":false,"nullable":true},"remindDayOfEmail":{"type":"object","description":"Email reminder on the day of the event. `null` = inherit host default.","example":true,"nullable":true},"remindDayOfSms":{"type":"object","description":"SMS reminder on the day of the event. `null` = inherit host default.","example":false,"nullable":true},"acceptResponseText":{"type":"string","description":"Text shown when guest accepts","example":"We're so glad you can make it!"},"declineResponseText":{"type":"string","description":"Text shown when guest declines","example":"We'll miss you!"},"headerFont":{"type":"string","description":"Header font name","example":"Playfair Display"},"bodyFont":{"type":"string","description":"Body font name","example":"Open Sans"},"headerFontFamily":{"type":"string","description":"Header font family","example":"Playfair Display, serif"},"bodyFontFamily":{"type":"string","description":"Body font family","example":"Open Sans, sans-serif"},"headerSize":{"type":"string","description":"Header font size","example":"24px"},"bodySize":{"type":"string","description":"Body font size","example":"16px"},"buttonTextColor":{"type":"string","description":"Button text color (hex)","example":"#FFFFFF"},"buttonBorderColor":{"type":"string","description":"Button border color (hex)","example":"#D4AF37"},"buttonBorderRadius":{"type":"string","description":"Button border radius","example":"8px"},"buttonBgColor":{"type":"string","description":"Button background color (hex)","example":"#D4AF37"},"headerTextColor":{"type":"string","description":"Header text color (hex)","example":"#333333"},"accommodations":{"description":"List of accommodation options","type":"array","items":{"$ref":"#/components/schemas/AccommodationResponseDto"}},"formFields":{"description":"Custom RSVP form fields (show/hide, required/optional)","type":"array","items":{"$ref":"#/components/schemas/FormFieldResponseDto"}},"qrCodeUrl":{"type":"string","description":"S3/CDN URL of the event QR code image"},"responsesShareToken":{"type":"object","description":"Active responses-share token, or null if no public read link is currently active.","nullable":true},"registryLinkName":{"type":"string","description":"Display name for the registry link","example":"Our Wedding Registry"},"registryUrl":{"type":"string","description":"Gift registry URL"},"guests":{"description":"Pre-loaded guest list with edit tokens (only returned on creation when guests were uploaded)","type":"array","items":{"$ref":"#/components/schemas/GuestRsvpResponseDto"}},"tier":{"type":"string","description":"Current plan tier for this event","example":"ESSENTIAL"},"effectiveTier":{"type":"string","description":"Effective entitlement tier — STUDIO if the host has an active Studio subscription, otherwise the event's own tier. Frontend gates should read this instead of `tier` so account-level Studio access unlocks every event the host owns.","example":"STUDIO"},"enabledFeatures":{"description":"Feature keys (matching `prisma/seed.ts`'s `featuresByTier`) the calling user is entitled to on this event. Resolved server-side via `EntitlementService.getEventFeatures` against the caller's live subscription + this event's tier. Frontend gates should read `enabledFeatures.includes('<key>')` rather than re-deriving from `tier` — that's the single source of truth for paid-feature UX.","example":["custom_fields","automated_reminders","export"],"type":"array","items":{"type":"string"}},"guestLimit":{"type":"number","description":"Maximum number of guests allowed","example":50},"paymentStatus":{"type":"string","description":"Payment status for this event","example":"FREE"},"smsInvitesRemaining":{"type":"number","description":"Remaining SMS invite credits","example":0},"smsRemindersRemaining":{"type":"number","description":"Remaining SMS reminder credits","example":0},"totalGuests":{"type":"number","description":"Total number of guests (when included by host fetches)","example":3},"attendingGuests":{"type":"number","description":"Total number of people attending (sum of adults + children across all ATTENDING guests, including additional guests added during RSVP)","example":18},"declinedGuests":{"type":"number","description":"Number of guests who declined","example":3},"pendingGuests":{"type":"number","description":"Number of guests pending response","example":4},"invitesSent":{"type":"number","description":"Distinct guests who have been sent an invitation at least once (not the count of invitation messages)","example":25},"acceptedGuests":{"type":"number","description":"Count of guest records with status = ATTENDING (not the headcount; see attendingGuests for total people including +1s)","example":12},"createdAt":{"format":"date-time","type":"string","description":"When the event was created","example":"2026-01-15T10:00:00.000Z"},"updatedAt":{"format":"date-time","type":"string","description":"When the event was last updated","example":"2026-01-15T10:00:00.000Z"},"customFonts":{"type":"array","description":"Host-uploaded custom fonts referenced by this event","items":{"type":"object","properties":{"fontFamily":{"type":"string"},"format":{"type":"string"},"fileUrl":{"type":"string"}}}}},"required":["id","orgId","userId","slug","status","name","eventDate","startTime","rsvpBy","showAnimation","isDropShadowRequired","hideEventDetails","tier","guestLimit","paymentStatus","smsInvitesRemaining","smsRemindersRemaining","createdAt","updatedAt"]},"GetSingleEventDto":{"type":"object","properties":{"success":{"type":"boolean","example":true,"description":"Indicates if the request was successful"},"statusCode":{"type":"number","example":200,"description":"HTTP status code"},"message":{"type":"string","example":"Operation completed successfully","description":"Response message"},"timestamp":{"type":"string","example":"2025-11-19T10:00:00.000Z","description":"Response timestamp"},"data":{"description":"Event object","allOf":[{"$ref":"#/components/schemas/EventResponseDto"}]}},"required":["success","statusCode","message","timestamp","data"]},"GetAllEventDto":{"type":"object","properties":{"success":{"type":"boolean","example":true,"description":"Indicates if the request was successful"},"statusCode":{"type":"number","example":200,"description":"HTTP status code"},"message":{"type":"string","example":"Operation completed successfully","description":"Response message"},"timestamp":{"type":"string","example":"2025-11-19T10:00:00.000Z","description":"Response timestamp"},"data":{"description":"Array of event objects","type":"array","items":{"$ref":"#/components/schemas/EventResponseDto"}},"meta":{"type":"object","description":"Pagination metadata","properties":{"page":{"type":"number","example":1},"limit":{"type":"number","example":10},"total":{"type":"number","example":50},"totalPages":{"type":"number","example":5},"hasNext":{"type":"boolean","example":true},"hasPrev":{"type":"boolean","example":false}}}},"required":["success","statusCode","message","timestamp","data","meta"]},"AdditionalGuestDetailDto":{"type":"object","properties":{"name":{"type":"string","description":"Additional guest full name","example":"Mia Smith","minLength":2,"maxLength":200},"ageType":{"type":"string","description":"Additional guest age type","enum":["ADULT","CHILD"],"example":"ADULT"},"dietaryRestrictions":{"type":"string","description":"Additional guest dietary restrictions","example":"Vegetarian","maxLength":300}},"required":["name","ageType"]},"SubmitRsvpDto":{"type":"object","properties":{"name":{"type":"string","description":"Guest full name","example":"Jane Smith","minLength":2,"maxLength":200},"email":{"type":"string","description":"Guest email address","example":"jane.smith@example.com"},"phone":{"type":"string","description":"Guest mobile number (defaults to AU when no country code is given; international numbers in E.164 format are accepted)","example":"+61412345678","maxLength":30},"status":{"type":"string","description":"RSVP status","enum":["PENDING","ATTENDING","DECLINED"],"example":"ATTENDING"},"adultCount":{"type":"number","description":"Number of adults attending (including guest). Use 0 when status is DECLINED.","example":1,"default":1,"minimum":0,"maximum":20},"childCount":{"type":"number","description":"Number of children attending","example":0,"default":0,"minimum":0,"maximum":20},"addressee":{"type":"string","description":"Display name for the invitation (e.g., \"The Smith Family\")","example":"The Smith Family","maxLength":200},"responses":{"description":"Responses to custom form fields","type":"array","items":{"$ref":"#/components/schemas/FormFieldResponseDto"}},"additionalGuests":{"description":"Additional guests and their details (name, age type, dietary restrictions)","type":"array","items":{"$ref":"#/components/schemas/AdditionalGuestDetailDto"}}},"required":["name","email","status"]},"UpdateRsvpDto":{"type":"object","properties":{"status":{"type":"string","description":"Updated RSVP status","enum":["PENDING","ATTENDING","DECLINED"],"example":"ATTENDING"},"adultCount":{"type":"number","description":"Updated number of adults attending (including guest)","example":2,"minimum":1,"maximum":20},"childCount":{"type":"number","description":"Updated number of children attending","example":1,"minimum":0,"maximum":20},"addressee":{"type":"string","description":"Display name for the invitation (e.g., \"The Smith Family\")","example":"The Smith Family"},"responses":{"description":"Updated responses to custom form fields","type":"array","items":{"$ref":"#/components/schemas/FormFieldResponseDto"}},"additionalGuests":{"description":"Updated additional guests (full replacement list: create, update, remove)","type":"array","items":{"$ref":"#/components/schemas/AdditionalGuestDetailDto"}}}},"GuestListItemDto":{"type":"object","properties":{"id":{"type":"string","description":"Guest ID","example":"cmieqoaba0000iymt4fab0345"},"name":{"type":"string","description":"Guest name","example":"Jane Smith"},"email":{"type":"string","description":"Guest email (optional — phone-only guests can be invited via SMS)","example":"jane.smith@example.com"},"phone":{"type":"string","description":"Guest phone number","example":"+1234567890"},"status":{"type":"string","description":"RSVP status","example":"ATTENDING"},"adultCount":{"type":"number","description":"Number of adults","example":1},"childCount":{"type":"number","description":"Number of children","example":0},"maxPartySize":{"type":"number","description":"Host-set ceiling on total people in this guest's party (primary + additional). This is the count the host invited them with, independent of the actual `adultCount`+`childCount` the guest submits when they RSVP.","example":2},"addressee":{"type":"string","description":"Display name for the invitation (e.g., \"The Smith Family\")","example":"The Smith Family"},"responses":{"description":"Form field responses","type":"array","items":{"$ref":"#/components/schemas/GuestFieldResponseDto"}},"additionalGuests":{"description":"Additional guests attached to this RSVP","type":"array","items":{"$ref":"#/components/schemas/AdditionalGuestResponseDto"}}},"required":["id","name","status","adultCount","childCount"]},"GetAllGuestsDto":{"type":"object","properties":{"success":{"type":"boolean","example":true,"description":"Indicates if the request was successful"},"statusCode":{"type":"number","example":200,"description":"HTTP status code"},"message":{"type":"string","example":"Operation completed successfully","description":"Response message"},"timestamp":{"type":"string","example":"2025-11-19T10:00:00.000Z","description":"Response timestamp"},"data":{"description":"Array of guest objects","type":"array","items":{"$ref":"#/components/schemas/GuestListItemDto"}},"meta":{"type":"object","description":"Pagination metadata","properties":{"page":{"type":"number","example":1},"limit":{"type":"number","example":10},"total":{"type":"number","example":50},"totalPages":{"type":"number","example":5},"hasNext":{"type":"boolean","example":true},"hasPrev":{"type":"boolean","example":false}}},"guestCounts":{"type":"object","description":"Guest count summary for the event","properties":{"total":{"type":"number","example":25},"attending":{"type":"number","example":18},"declined":{"type":"number","example":3},"pending":{"type":"number","example":4}}}},"required":["success","statusCode","message","timestamp","data","meta","guestCounts"]},"GuestDetailResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"Guest ID","example":"cmieqoaba0000iymt4fab0345"},"name":{"type":"string","description":"Guest name","example":"Jane Smith"},"email":{"type":"string","description":"Guest email (optional — phone-only guests can be invited via SMS)","example":"jane.smith@example.com"},"phone":{"type":"string","description":"Guest phone number","example":"+1234567890"},"status":{"type":"string","description":"RSVP status","example":"ATTENDING"},"adultCount":{"type":"number","description":"Number of adults","example":1},"childCount":{"type":"number","description":"Number of children","example":0},"maxPartySize":{"type":"number","description":"Host-set ceiling on total people in this guest's party (primary + additional). This is the count the host invited them with, independent of the actual `adultCount`+`childCount` the guest submits when they RSVP.","example":2},"addressee":{"type":"string","description":"Display name for the invitation (e.g., \"The Smith Family\")","example":"The Smith Family"},"responses":{"description":"Form field responses","type":"array","items":{"$ref":"#/components/schemas/GuestFieldResponseDto"}},"additionalGuests":{"description":"Additional guests attached to this RSVP","type":"array","items":{"$ref":"#/components/schemas/AdditionalGuestResponseDto"}}},"required":["id","name","status","adultCount","childCount"]},"GetSingleGuestDto":{"type":"object","properties":{"success":{"type":"boolean","example":true,"description":"Indicates if the request was successful"},"statusCode":{"type":"number","example":200,"description":"HTTP status code"},"message":{"type":"string","example":"Operation completed successfully","description":"Response message"},"timestamp":{"type":"string","example":"2025-11-19T10:00:00.000Z","description":"Response timestamp"},"data":{"description":"Guest detail object","allOf":[{"$ref":"#/components/schemas/GuestDetailResponseDto"}]}},"required":["success","statusCode","message","timestamp","data"]},"UpdateGuestByHostDto":{"type":"object","properties":{"name":{"type":"string","description":"Guest full name","example":"Jane Smith"},"email":{"type":"object","description":"Guest email address (must be unique per event; send null to clear)","example":"jane.smith@example.com","nullable":true},"phone":{"type":"object","description":"Guest mobile number (defaults to AU when no country code is given; international numbers in E.164 format are accepted)","example":"+61412345678","maxLength":30},"addressee":{"type":"object","description":"Display name for the invitation (e.g., \"The Smith Family\")","example":"The Smith Family"},"status":{"type":"string","description":"RSVP status","enum":["PENDING","ATTENDING","DECLINED"],"example":"ATTENDING"},"adultCount":{"type":"number","description":"Number of adults (0 when declined)","example":2,"minimum":0,"maximum":20},"childCount":{"type":"number","description":"Number of children","example":1,"minimum":0,"maximum":20},"maxPartySize":{"type":"number","description":"Host-set ceiling on total people in this guest's party (primary + additional guests). The RSVP form prevents the guest from adding more people than this.","example":4,"minimum":1,"maximum":50},"responses":{"description":"Responses to custom form fields (upsert by fieldId; omitted fields are left unchanged)","type":"array","items":{"$ref":"#/components/schemas/FormFieldResponseDto"}},"additionalGuests":{"description":"Additional guests (full replacement — all existing additional guests are removed and replaced with this list). Omit to leave unchanged.","type":"array","items":{"$ref":"#/components/schemas/AdditionalGuestDetailDto"}}}},"AddGuestsToEventDto":{"type":"object","properties":{"guests":{"description":"Array of guests to add to the event","type":"array","items":{"$ref":"#/components/schemas/CreateGuestDto"}}},"required":["guests"]},"UpdateAccommodationItemDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the accommodation","example":"The Ritz-Carlton","minLength":1,"maxLength":300},"imageUrl":{"type":"string","description":"URL of the accommodation image (16:9 ratio preferred)","example":"https://cdn.rsvpeasy.com/accommodations/hotel.jpg"},"description":{"type":"string","description":"Short description of the accommodation","example":"A luxurious 5-star hotel just minutes from the venue","maxLength":2000},"bookingLink":{"type":"string","description":"URL for guests to book this accommodation","example":"https://www.marriott.com/reservation?groupCode=WEDDING"},"isVisible":{"type":"boolean","description":"Whether this accommodation is visible on the RSVP page","example":true,"default":true},"id":{"type":"string","description":"ID of an existing accommodation record. Omit for new entries.","example":"cmieqoaba0000iymt4fab9999"}},"required":["name"]},"UpdateFormFieldItemDto":{"type":"object","properties":{"label":{"type":"string","description":"Display label for the form field","example":"Dietary restrictions","minLength":1,"maxLength":200},"placeholder":{"type":"string","description":"Placeholder text for the input","example":"e.g., Vegetarian, Gluten-free","maxLength":200},"helpText":{"type":"string","description":"Help text shown below the field","example":"Please let us know about any dietary requirements","maxLength":500},"fieldType":{"type":"string","description":"Type of the form field","enum":["TEXT","EMAIL","PHONE","NUMBER","BOOLEAN","DROPDOWN","DATE","TEXTAREA"],"example":"TEXT"},"isRequired":{"type":"boolean","description":"Whether this field is required","example":false,"default":false},"isActive":{"type":"boolean","description":"Whether this field is visible on the RSVP form","example":true,"default":true},"sortOrder":{"type":"number","description":"Sort order for display (lower = first)","example":0,"default":0},"scope":{"type":"string","description":"Question scope. `RSVP` (default) asks once per submission; `GUEST` repeats the question for the primary guest and every plus-one (Anna section 5.3).","enum":["RSVP","GUEST"],"example":"RSVP","default":"RSVP"},"options":{"description":"Dropdown options for DROPDOWN fields (stored inline as a JSON array)","example":["Vegetarian","Vegan"],"type":"array","items":{"type":"string"}},"id":{"type":"string","description":"ID of an existing form field record. Omit for new entries.","example":"cmieqoaba0000iymt4fab9999"}},"required":["label","fieldType"]},"UpdateEventDto":{"type":"object","properties":{"name":{"type":"string","description":"Name of the event","example":"Sarah & John Wedding","minLength":2,"maxLength":200},"eventDate":{"format":"date-time","type":"string","description":"Date of the event (ISO 8601 format)","example":"2026-06-15T00:00:00.000Z"},"startTime":{"format":"date-time","type":"string","description":"Start time of the event (ISO 8601 format)","example":"2026-06-15T14:00:00.000Z"},"rsvpBy":{"format":"date-time","type":"string","description":"RSVP deadline (ISO 8601 format). Must be before eventDate.","example":"2026-06-01T23:59:59.000Z"},"locationName":{"type":"string","description":"Name of the event location/venue","example":"The Grand Ballroom","maxLength":300},"slug":{"type":"string","description":"Custom URL slug for the event. Must be lowercase alphanumeric with hyphens. If omitted, a slug is auto-generated from the event name.","example":"sarah-and-john-wedding","maxLength":100},"address":{"type":"string","description":"Full address of the event location","example":"123 Main St, New York, NY 10001","maxLength":500},"hostMessage":{"type":"string","description":"Personal message from the host displayed on the RSVP page","example":"We are thrilled to invite you to celebrate our special day with us!","maxLength":5000},"maxPublicPartySize":{"type":"number","description":"Optional cap on total people (primary + additional guests) per public RSVP submission. Omit or set null for no cap. Edit-token RSVPs are governed by Guest.maxPartySize and are unaffected.","example":6,"minimum":1},"invitationUrl":{"type":"string","description":"URL to the uploaded invitation image/media (S3/CDN URL)","example":"https://cdn.rsvpeasy.com/invitations/my-invite.jpg"},"backgroundColor":{"type":"string","description":"Background color for the RSVP page (hex code)","example":"#FFFFFF","maxLength":20},"textColor":{"type":"string","description":"Text color for the RSVP page (hex code)","example":"#333333","maxLength":20},"accentColor":{"type":"string","description":"Accent color for buttons and highlights (hex code)","example":"#D4AF37","maxLength":20},"fontFamily":{"type":"string","description":"Font family for the RSVP page","example":"Playfair Display","maxLength":100},"showAnimation":{"type":"boolean","description":"Whether to show entrance animation on the RSVP page","example":false,"default":false},"isDropShadowRequired":{"type":"boolean","description":"Whether to apply a drop shadow to the invitation image","example":false,"default":false},"hideEventDetails":{"type":"boolean","description":"Hide the structured date/time/location block on the public RSVP page (useful when the invitation image already shows them)","example":false,"default":false},"remindOneWeekEmail":{"type":"boolean","description":"Send an email reminder one week before the event date.","default":true},"remindOneWeekSms":{"type":"boolean","description":"Send an SMS reminder one week before the event date.","default":false},"remindTwoDayEmail":{"type":"boolean","description":"Send an email reminder two days before the event date.","default":true},"remindTwoDaySms":{"type":"boolean","description":"Send an SMS reminder two days before the event date.","default":false},"remindDayOfEmail":{"type":"boolean","description":"Send an email reminder on the day of the event.","default":true},"remindDayOfSms":{"type":"boolean","description":"Send an SMS reminder on the day of the event.","default":false},"acceptResponseText":{"type":"string","description":"Custom text shown when guest accepts the invitation","example":"We're so glad you can make it!","maxLength":500},"declineResponseText":{"type":"string","description":"Custom text shown when guest declines the invitation","example":"We'll miss you!","maxLength":500},"headerFont":{"type":"string","description":"Header font name (e.g. display font)","example":"Playfair Display","maxLength":100},"bodyFont":{"type":"string","description":"Body font name (e.g. body text font)","example":"Open Sans","maxLength":100},"headerFontFamily":{"type":"string","description":"Header font family for the RSVP page","example":"Playfair Display, serif","maxLength":150},"bodyFontFamily":{"type":"string","description":"Body font family for the RSVP page","example":"Open Sans, sans-serif","maxLength":150},"headerSize":{"type":"string","description":"Header font size (e.g. \"24px\", \"1.5rem\")","example":"24px","maxLength":20},"bodySize":{"type":"string","description":"Body font size (e.g. \"16px\", \"1rem\")","example":"16px","maxLength":20},"buttonTextColor":{"type":"string","description":"Button text color (hex code)","example":"#FFFFFF","maxLength":20},"buttonBorderColor":{"type":"string","description":"Button border color (hex code)","example":"#D4AF37","maxLength":20},"buttonBorderRadius":{"type":"string","description":"Button border radius (e.g. \"8px\", \"0.5rem\")","example":"8px","maxLength":20},"buttonBgColor":{"type":"string","description":"Button background color (hex code)","example":"#D4AF37","maxLength":20},"headerTextColor":{"type":"string","description":"Header text color (hex code)","example":"#333333","maxLength":20},"accommodations":{"description":"List of accommodation options. Items with an `id` update existing records; items without `id` create new ones. All existing accommodations not in this array will be deleted.","type":"array","items":{"$ref":"#/components/schemas/UpdateAccommodationItemDto"}},"formFields":{"description":"List of form fields. Replaces all existing form fields. Any existing GuestResponse data for custom fields will be removed when form fields are replaced.","type":"array","items":{"$ref":"#/components/schemas/UpdateFormFieldItemDto"}},"guests":{"description":"Pre-loaded guest list. Each guest gets a unique RSVP token on creation. Frontend can populate this from manual entry, CSV import, or phone contacts.","type":"array","items":{"$ref":"#/components/schemas/CreateGuestDto"}},"registryLinkName":{"type":"string","description":"Display name for the registry link","example":"Our Wedding Registry","maxLength":300},"registryUrl":{"type":"string","description":"URL to the gift registry (https:// is added if no protocol is provided)","example":"https://www.zola.com/registry/sarahandjohn2026"},"status":{"type":"string","description":"Event status. Allowed transitions: DRAFT → PUBLISHED, PUBLISHED → MANUALLY_CLOSED.","enum":["DRAFT","PUBLISHED","AUTO_CLOSED","MANUALLY_CLOSED","DUPLICATE","CLONED"],"example":"PUBLISHED"}}},"SendInvitationsResponseDto":{"type":"object","properties":{"message":{"type":"string","description":"Result message","example":"Successfully queued 5 invitations."},"count":{"type":"number","description":"Number of invitations queued","example":5}},"required":["message","count"]},"TwilioConfigDto":{"type":"object","properties":{"accountSid":{"type":"string","description":"Twilio Account SID"},"authToken":{"type":"string","description":"Twilio Auth Token"},"messagingServiceSid":{"type":"string","description":"Twilio Messaging Service SID"},"fromNumber":{"type":"string","description":"Twilio phone number (E.164 format, e.g. +61412345678)"}},"required":["accountSid","authToken","fromNumber"]},"SmtpConfigDto":{"type":"object","properties":{"host":{"type":"string","description":"SMTP host"},"port":{"type":"number","description":"SMTP port"},"secure":{"type":"boolean","description":"Use TLS/SSL"},"user":{"type":"string","description":"SMTP username"},"pass":{"type":"string","description":"SMTP password"},"fromEmail":{"type":"string","description":"From email address"},"fromName":{"type":"string","description":"From display name"},"replyTo":{"type":"string","description":"Reply-to email address"}},"required":["host","port","secure","user","pass","fromEmail","fromName"]},"CreateCommTemplateDto":{"type":"object","properties":{"type":{"type":"string","enum":["SMS","EMAIL"],"description":"Template channel type (SMS or EMAIL)"},"name":{"type":"string","description":"Unique template name within the organization"},"description":{"type":"string","description":"Template description"},"tags":{"description":"Tags for categorization","type":"array","items":{"type":"string"}},"subjectTemplate":{"type":"string","description":"Subject template - email only (LiquidJS syntax)"},"bodyTemplate":{"type":"string","description":"Body template (LiquidJS syntax)"},"bodyHtmlTemplate":{"type":"string","description":"HTML body template - email only (LiquidJS syntax)"},"greeting":{"type":"string","description":"Header greeting text, e.g. \"You're Invited!\"","example":"You're Invited!"},"customMessage":{"type":"string","description":"Custom body message from the host","example":"We would love for you to join us for this special occasion."},"buttonLabel":{"type":"string","description":"CTA button label, e.g. \"RSVP Now\"","example":"RSVP Now"},"closingLine":{"type":"string","description":"Optional sign-off line shown above the footer (replaces the prior hardcoded \"With love,\" block). Empty by default.","example":"Looking forward to celebrating with you,"}},"required":["type","name","bodyTemplate"]},"UpdateCommTemplateDto":{"type":"object","properties":{"name":{"type":"string","description":"Template name"},"description":{"type":"string","description":"Template description"},"tags":{"description":"Tags for categorization","type":"array","items":{"type":"string"}},"subjectTemplate":{"type":"string","description":"Subject template - email only"},"bodyTemplate":{"type":"string","description":"Body template"},"bodyHtmlTemplate":{"type":"string","description":"HTML body template - email only"},"greeting":{"type":"string","description":"Header greeting text, e.g. \"You're Invited!\""},"customMessage":{"type":"string","description":"Custom body message from the host"},"buttonLabel":{"type":"string","description":"CTA button label, e.g. \"RSVP Now\""},"closingLine":{"type":"string","description":"Optional sign-off line shown above the footer. Empty disables the closing block entirely."}}},"PreviewTemplateDto":{"type":"object","properties":{"entityType":{"type":"string","description":"Entity type (event or guest)"},"entityId":{"type":"string","description":"Entity ID for context data"},"eventId":{"type":"string","description":"Event ID to use for context data"},"guestId":{"type":"string","description":"Guest ID to use for context data"}}},"SendSmsDto":{"type":"object","properties":{"to":{"type":"string","description":"Recipient phone number (any format, will be normalized to +61)"},"body":{"type":"string","description":"SMS message body"},"templateId":{"type":"string","description":"Template ID to render body from"},"eventId":{"type":"string","description":"Event ID for template context"},"guestId":{"type":"string","description":"Guest ID for template context"}},"required":["to","body"]},"MessageResponseDto":{"type":"object","properties":{"id":{"type":"string"},"orgId":{"type":"string"},"channel":{"type":"string"},"direction":{"type":"string"},"fromAddress":{"type":"string"},"toAddress":{"type":"string"},"subject":{"type":"string"},"bodyText":{"type":"string"},"bodyHtml":{"type":"string"},"provider":{"type":"string"},"providerMessageId":{"type":"string"},"status":{"type":"string"},"errorCode":{"type":"string"},"errorMessage":{"type":"string"},"sentAt":{"format":"date-time","type":"string"},"deliveredAt":{"format":"date-time","type":"string"},"templateId":{"type":"string"},"segmentCount":{"type":"number"},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"}},"required":["id","orgId","channel","direction","fromAddress","toAddress","bodyText","status","createdAt","updatedAt"]},"SendEmailDto":{"type":"object","properties":{"to":{"type":"string","description":"Recipient email address"},"subject":{"type":"string","description":"Email subject line"},"bodyText":{"type":"string","description":"Plain text body"},"bodyHtml":{"type":"string","description":"HTML body"},"templateId":{"type":"string","description":"Template ID to render content from"},"eventId":{"type":"string","description":"Event ID for template context"},"guestId":{"type":"string","description":"Guest ID for template context"},"greetingCardDeliveryId":{"type":"string","description":"Greeting card delivery ID for template context (used by the greeting-card sender)"},"cc":{"description":"CC recipients","type":"array","items":{"type":"string"}},"bcc":{"description":"BCC recipients","type":"array","items":{"type":"string"}},"replyTo":{"type":"string","description":"Reply-To address"}},"required":["to","subject","bodyText"]},"GetAllMessagesDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/MessageResponseDto"}}},"required":["data"]},"GetSingleMessageDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/MessageResponseDto"}},"required":["data"]},"CreateOrganizationDto":{"type":"object","properties":{"name":{"type":"string","description":"Organization name","example":"Acme Corporation"},"slug":{"type":"string","description":"Unique URL-friendly slug","example":"acme-corp","pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"description":{"type":"string","description":"Organization description","example":"A leading tech company"}},"required":["name","slug"]},"OrganizationBrandingResponseDto":{"type":"object","properties":{"emailSenderDisplayName":{"type":"object","description":"Sender display name override. Null = use brand default.","nullable":true},"emailSignatureLine":{"type":"object","description":"Signature line override. Null = use brand default.","nullable":true},"emailFooterText":{"type":"object","description":"Footer text override. Null = use brand default.","nullable":true},"emailAccentColor":{"type":"object","description":"Hero accent colour override (hex). Null = use brand default.","nullable":true}}},"UpdateOrganizationBrandingDto":{"type":"object","properties":{"emailSenderDisplayName":{"type":"string","description":"Display name shown in the \"From\" position of system emails. Falls back to \"RSVPeasy\" when blank.","example":"RSVPeasy","maxLength":50},"emailSignatureLine":{"type":"string","description":"Signature line shown at the bottom of system emails (e.g. password reset, greeting card delivery). Falls back to \"The RSVPeasy team\" when blank.","example":"The RSVPeasy team","maxLength":80},"emailFooterText":{"type":"string","description":"Footer attribution line (\"Sent via RSVPeasy\" by default). Falls back to the default when blank.","example":"Sent via RSVPeasy","maxLength":60},"emailAccentColor":{"type":"string","description":"Accent colour used for the system-email hero band. Hex format (e.g. \"#968f80\"). Falls back to the brand taupe when blank.","example":"#968f80"}}},"UpdateOrganizationDto":{"type":"object","properties":{"name":{"type":"string","description":"Organization name","example":"Acme Corporation"},"description":{"type":"string","description":"Organization description","example":"A leading tech company"},"isActive":{"type":"boolean","description":"Whether the organization is active","example":true}}},"CreateGreetingCardDto":{"type":"object","properties":{"title":{"type":"string","description":"Optional title for the greeting card","example":"Holiday Season Greetings","maxLength":200},"designUrl":{"type":"string","description":"URL of the greeting card design image","example":"https://example.com/designs/holiday-card.jpg"},"isDropShadowRequired":{"type":"boolean","description":"Whether to apply a drop shadow to the greeting card design","example":false,"default":false},"eventId":{"type":"string","description":"Optional event ID to link this greeting card to an existing event","example":"evt_abc123"}}},"GreetingCardDeliveryResponseDto":{"type":"object","properties":{"id":{"type":"string"},"orderId":{"type":"string"},"recipientName":{"type":"string"},"recipientEmail":{"type":"string"},"recipientPhone":{"type":"string"},"channel":{"type":"string","enum":["EMAIL","SMS"]},"status":{"type":"string","enum":["PENDING","SENT","DELIVERED","FAILED","CANCELED"]},"sentAt":{"format":"date-time","type":"string"},"deliveredAt":{"format":"date-time","type":"string"},"openedAt":{"format":"date-time","type":"string","description":"When the recipient first opened the public greeting card link"}},"required":["id","orderId","recipientName","channel","status"]},"GreetingCardSmsPackResponseDto":{"type":"object","properties":{"id":{"type":"string"},"quantity":{"type":"number"},"amount":{"type":"number"}},"required":["id","quantity","amount"]},"GreetingCardResponseDto":{"type":"object","properties":{"id":{"type":"string"},"orgId":{"type":"string"},"userId":{"type":"string"},"eventId":{"type":"string"},"status":{"type":"string","enum":["DRAFT","SCHEDULED","SENT","PARTIALLY_SENT","FAILED","CANCELED"]},"title":{"type":"string"},"designUrl":{"type":"string"},"isDropShadowRequired":{"type":"boolean","description":"Whether drop shadow is applied to the design","example":false},"totalRecipients":{"type":"number"},"smsRecipients":{"type":"number"},"emailRecipients":{"type":"number"},"price":{"type":"object"},"currency":{"type":"string"},"stripePriceId":{"type":"string"},"stripeInvoiceId":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"updatedAt":{"format":"date-time","type":"string"},"deliveries":{"type":"array","items":{"$ref":"#/components/schemas/GreetingCardDeliveryResponseDto"}},"smsPacks":{"type":"array","items":{"$ref":"#/components/schemas/GreetingCardSmsPackResponseDto"}}},"required":["id","orgId","userId","status","isDropShadowRequired","totalRecipients","smsRecipients","emailRecipients","price","currency","createdAt","updatedAt"]},"GetSingleGreetingCardDto":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/GreetingCardResponseDto"}},"required":["data"]},"UpdateGreetingCardDto":{"type":"object","properties":{"title":{"type":"string","description":"Title for the greeting card","example":"Holiday Season Greetings","maxLength":200},"designUrl":{"type":"string","description":"URL of the greeting card design image","example":"https://example.com/designs/holiday-card.jpg"},"isDropShadowRequired":{"type":"boolean","description":"Whether to apply a drop shadow to the greeting card design","example":false},"eventId":{"type":"string","description":"Optional event ID to link this greeting card to","example":"evt_abc123"}}},"RecipientDto":{"type":"object","properties":{"recipientName":{"type":"string","description":"Name of the recipient","example":"John Doe"},"recipientEmail":{"type":"string","description":"Email address of the recipient (required if channel is EMAIL)","example":"john@example.com"},"recipientPhone":{"type":"string","description":"Phone number of the recipient (required if channel is SMS). Accepts any valid international format — libphonenumber-js parses with AU as the default when no `+CC` prefix is present.","example":"+15551234567"},"channel":{"type":"string","description":"Delivery channel for the greeting card","enum":["EMAIL","SMS"],"example":"EMAIL"}},"required":["recipientName","recipientEmail","recipientPhone","channel"]},"AddRecipientsDto":{"type":"object","properties":{"recipients":{"description":"Array of recipients to add to the greeting card","minItems":1,"type":"array","items":{"$ref":"#/components/schemas/RecipientDto"}}},"required":["recipients"]},"CreateSmsPackCheckoutDto":{"type":"object","properties":{"packs":{"type":"number","description":"Number of SMS packs to purchase (each pack = 50 SMS recipients)","example":2,"minimum":1},"successUrl":{"type":"string","description":"Success URL to redirect after payment"},"cancelUrl":{"type":"string","description":"Cancel URL to redirect if payment is cancelled"}},"required":["packs"]},"SendGreetingCardDto":{"type":"object","properties":{"deliveryIds":{"description":"Optional list of delivery IDs to target. Omit or pass an empty array to send to all PENDING deliveries (current default behaviour).","example":["cm123abc","cm456def"],"type":"array","items":{"type":"string"}}}},"GreetingCardListKpisDto":{"type":"object","properties":{"totalRecipients":{"type":"number","description":"Sum of recipients across all greeting cards for this host","example":42},"sent":{"type":"number","description":"Greeting cards with at least one successful send (SENT or PARTIALLY_SENT)","example":3},"drafts":{"type":"number","description":"Greeting cards still in DRAFT (unpaid / not finished)","example":1},"totalOpens":{"type":"number","description":"Deliveries where the recipient opened the public greeting card link","example":18}},"required":["totalRecipients","sent","drafts","totalOpens"]},"GetAllGreetingCardsDto":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/GreetingCardResponseDto"}},"total":{"type":"number"},"page":{"type":"number"},"limit":{"type":"number"},"totalPages":{"type":"number"},"totalRecipients":{"$ref":"#/components/schemas/GreetingCardListKpisDto"},"sent":{"type":"number"},"drafts":{"type":"number"},"totalOpens":{"type":"number"}},"required":["data","total","page","limit","totalPages"]},"RecordGreetingCardOpenDto":{"type":"object","properties":{"deliveryId":{"type":"string","description":"Greeting card delivery id from the `d` query param on the public link","example":"cmieqoaba0000iymt4fab0345"}},"required":["deliveryId"]},"RoleResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"The id of the role","example":"123e4567-e89b-12d3-a456-426614174000"},"orgId":{"type":"string","description":"The organization ID of the role","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"The name of the role","example":"Admin"},"description":{"type":"object","description":"The description of the role","example":"Administrator role with full access","nullable":true},"createdAt":{"format":"date-time","type":"string","description":"The created at date of the role","example":"2021-01-01T00:00:00.000Z"},"updatedAt":{"format":"date-time","type":"string","description":"The updated at date of the role","example":"2021-01-01T00:00:00.000Z"},"permissionCount":{"type":"number","description":"The number of permissions assigned to this role","example":12}},"required":["id","orgId","name","description","createdAt","updatedAt","permissionCount"]},"GetAllRoleDto":{"type":"object","properties":{"success":{"type":"boolean","example":true,"description":"Indicates if the request was successful"},"statusCode":{"type":"number","example":200,"description":"HTTP status code"},"message":{"type":"string","example":"Operation completed successfully","description":"Response message"},"timestamp":{"type":"string","example":"2025-11-19T10:00:00.000Z","description":"Response timestamp"},"data":{"type":"array","items":{"$ref":"#/components/schemas/RoleResponseDto"}},"meta":{"type":"object","properties":{"page":{"type":"number","example":1},"limit":{"type":"number","example":10},"total":{"type":"number","example":100},"totalPages":{"type":"number","example":10},"hasNext":{"type":"boolean","example":true},"hasPrev":{"type":"boolean","example":false}}}},"required":["success","statusCode","message","timestamp","data","meta"]},"CreateRoleDto":{"type":"object","properties":{"name":{"type":"string","description":"The name of the role","example":"Admin"},"description":{"type":"string","description":"The description of the role","example":"Administrator role with full access"}},"required":["name"]},"PermissionResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"The id of the permission","example":"123e4567-e89b-12d3-a456-426614174000"},"orgId":{"type":"string","description":"The organization ID of the permission","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"The name of the permission","example":"leads.read"},"description":{"type":"object","description":"The description of the permission","example":"Permission to read leads","nullable":true}},"required":["id","orgId","name","description"]},"RoleWithPermissionsResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"The id of the role","example":"123e4567-e89b-12d3-a456-426614174000"},"orgId":{"type":"string","description":"The organization ID of the role","example":"123e4567-e89b-12d3-a456-426614174000"},"name":{"type":"string","description":"The name of the role","example":"Admin"},"description":{"type":"object","description":"The description of the role","example":"Administrator role with full access","nullable":true},"createdAt":{"format":"date-time","type":"string","description":"The created at date of the role","example":"2021-01-01T00:00:00.000Z"},"updatedAt":{"format":"date-time","type":"string","description":"The updated at date of the role","example":"2021-01-01T00:00:00.000Z"},"permissionCount":{"type":"number","description":"The number of permissions assigned to this role","example":12},"permissions":{"description":"The permissions associated with this role","example":[{"id":"123e4567-e89b-12d3-a456-426614174000","orgId":"123e4567-e89b-12d3-a456-426614174000","name":"leads.read","description":"Permission to read leads"}],"type":"array","items":{"$ref":"#/components/schemas/PermissionResponseDto"}}},"required":["id","orgId","name","description","createdAt","updatedAt","permissionCount","permissions"]},"GetSingleRoleDto":{"type":"object","properties":{"success":{"type":"boolean","example":true,"description":"Indicates if the request was successful"},"statusCode":{"type":"number","example":200,"description":"HTTP status code"},"message":{"type":"string","example":"Operation completed successfully","description":"Response message"},"timestamp":{"type":"string","example":"2025-11-19T10:00:00.000Z","description":"Response timestamp"},"data":{"$ref":"#/components/schemas/RoleWithPermissionsResponseDto"}},"required":["success","statusCode","message","timestamp","data"]},"UpdateRoleDto":{"type":"object","properties":{"name":{"type":"string","description":"The name of the role","example":"Admin"},"description":{"type":"string","description":"The description of the role","example":"Administrator role with full access"}}},"ManageRolePermissionsDto":{"type":"object","properties":{"permissionIds":{"description":"Array of permission IDs to add or remove from the role","example":["123e4567-e89b-12d3-a456-426614174000","223e4567-e89b-12d3-a456-426614174001"],"type":"array","items":{"type":"string"}}},"required":["permissionIds"]},"GetAllPermissionDto":{"type":"object","properties":{"success":{"type":"boolean","example":true,"description":"Indicates if the request was successful"},"statusCode":{"type":"number","example":200,"description":"HTTP status code"},"message":{"type":"string","example":"Operation completed successfully","description":"Response message"},"timestamp":{"type":"string","example":"2025-11-19T10:00:00.000Z","description":"Response timestamp"},"data":{"type":"array","items":{"$ref":"#/components/schemas/PermissionResponseDto"}},"meta":{"type":"object","properties":{"page":{"type":"number","example":1},"limit":{"type":"number","example":10},"total":{"type":"number","example":100},"totalPages":{"type":"number","example":10},"hasNext":{"type":"boolean","example":true},"hasPrev":{"type":"boolean","example":false}}}},"required":["success","statusCode","message","timestamp","data","meta"]},"GetSinglePermissionDto":{"type":"object","properties":{"success":{"type":"boolean","example":true,"description":"Indicates if the request was successful"},"statusCode":{"type":"number","example":200,"description":"HTTP status code"},"message":{"type":"string","example":"Operation completed successfully","description":"Response message"},"timestamp":{"type":"string","example":"2025-11-19T10:00:00.000Z","description":"Response timestamp"},"data":{"$ref":"#/components/schemas/PermissionResponseDto"}},"required":["success","statusCode","message","timestamp","data"]},"UserWithRoleResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"The id of the user","example":"123e4567-e89b-12d3-a456-426614174000"},"orgId":{"type":"string","description":"The organization ID of the user","example":"123e4567-e89b-12d3-a456-426614174000"},"username":{"type":"object","description":"The username of the user","example":"johndoe","nullable":true},"name":{"type":"object","description":"The name of the user","example":"John Doe","nullable":true},"email":{"type":"string","description":"The email of the user","example":"john@example.com"},"emailVerified":{"type":"boolean","description":"Email verification status","example":true},"image":{"type":"object","description":"User profile image","example":"https://example.com/image.jpg","nullable":true},"createdAt":{"format":"date-time","type":"string","description":"User creation date","example":"2024-01-01T00:00:00.000Z"},"updatedAt":{"format":"date-time","type":"string","description":"User last update date","example":"2024-01-01T00:00:00.000Z"},"onboardingStatus":{"type":"string","description":"User onboarding status","enum":["PENDING","ACTIVE","SUSPENDED"],"example":"ACTIVE"},"accountType":{"type":"string","description":"User account type","enum":["ADMIN","HOST"],"example":"HOST"},"roles":{"description":"The roles associated with this user","example":[{"id":"123e4567-e89b-12d3-a456-426614174000","orgId":"123e4567-e89b-12d3-a456-426614174000","name":"Admin","description":"Administrator role with full access"}],"type":"array","items":{"$ref":"#/components/schemas/RoleResponseDto"}},"eventCount":{"type":"number","description":"Number of events created by this user","example":5},"planName":{"type":"object","description":"Name of the subscription plan","example":"Premium","nullable":true},"planCode":{"type":"object","description":"Code of the subscription plan","example":"PREMIUM","nullable":true},"subscriptionStatus":{"type":"string","description":"Status of the user subscription (when present)","enum":["ACTIVE","TRIALING","PAST_DUE","CANCELED","UNPAID","PAUSED"],"example":"ACTIVE","nullable":true},"currentPeriodEnd":{"type":"object","description":"End of the current billing period (when present)","example":"2026-06-03T00:00:00.000Z","nullable":true},"cancelAtPeriodEnd":{"type":"boolean","description":"True when the active subscription is scheduled to cancel at period end","example":false},"hasPaidSubscription":{"type":"boolean","description":"True when the active subscription is paid via Stripe.","example":false},"complimentaryGrant":{"type":"boolean","description":"True when the active subscription is a complimentary admin grant.","example":true},"isLastAdmin":{"type":"boolean","description":"True if this user holds Admin and is the only admin in the system. Frontend uses this to block role-changes that would lock the system out.","example":false}},"required":["id","orgId","username","name","email","emailVerified","image","createdAt","updatedAt","onboardingStatus","accountType","roles","eventCount","planName","planCode","isLastAdmin"]},"GetAllUserDto":{"type":"object","properties":{"success":{"type":"boolean","example":true,"description":"Indicates if the request was successful"},"statusCode":{"type":"number","example":200,"description":"HTTP status code"},"message":{"type":"string","example":"Operation completed successfully","description":"Response message"},"timestamp":{"type":"string","example":"2025-11-19T10:00:00.000Z","description":"Response timestamp"},"data":{"type":"array","items":{"$ref":"#/components/schemas/UserWithRoleResponseDto"}},"meta":{"type":"object","properties":{"page":{"type":"number","example":1},"limit":{"type":"number","example":10},"total":{"type":"number","example":100},"totalPages":{"type":"number","example":10},"hasNext":{"type":"boolean","example":true},"hasPrev":{"type":"boolean","example":false}}}},"required":["success","statusCode","message","timestamp","data","meta"]},"GetSingleUserDto":{"type":"object","properties":{"success":{"type":"boolean","example":true,"description":"Indicates if the request was successful"},"statusCode":{"type":"number","example":200,"description":"HTTP status code"},"message":{"type":"string","example":"Operation completed successfully","description":"Response message"},"timestamp":{"type":"string","example":"2025-11-19T10:00:00.000Z","description":"Response timestamp"},"data":{"$ref":"#/components/schemas/UserWithRoleResponseDto"}},"required":["success","statusCode","message","timestamp","data"]},"CreateUserDto":{"type":"object","properties":{"name":{"type":"string","description":"The name of the user","example":"John Doe"},"email":{"type":"string","description":"The email of the user","example":"john@example.com"},"password":{"type":"string","description":"The password for the user account","example":"SecurePassword123!","minLength":8},"username":{"type":"string","description":"The username of the user","example":"johndoe"},"image":{"type":"string","description":"User profile image URL","example":"https://example.com/image.jpg"},"roleId":{"type":"string","description":"Optional role ID to assign on creation. Defaults to the Host role when omitted. Assigning the Admin role requires the actor to currently have Admin.","example":"role_admin_123"}},"required":["name","email","password"]},"AssignRolesDto":{"type":"object","properties":{"roleIds":{"description":"Array of role IDs to assign to the user","example":["role-id-1","role-id-2"],"type":"array","items":{"type":"string"}}},"required":["roleIds"]},"UpdateUserSubscriptionDto":{"type":"object","properties":{"tierCode":{"type":"string","description":"Tier code to assign to the user. ESSENTIAL revokes the comp grant; STUDIO grants a complimentary subscription. PREMIUM is event-scoped and is granted via the per-event endpoint.","enum":["ESSENTIAL","STUDIO"],"example":"STUDIO"},"confirmCancelPaidSub":{"type":"boolean","description":"Required acknowledgement when downgrading a user who currently has a paid Stripe subscription — the Stripe sub is cancelled at period end.","example":false}},"required":["tierCode"]},"UserSubscriptionSummaryDto":{"type":"object","properties":{"planCode":{"type":"object","example":"STUDIO","nullable":true},"planName":{"type":"object","example":"Studio","nullable":true},"status":{"type":"string","enum":["ACTIVE","TRIALING","PAST_DUE","CANCELED","UNPAID","PAUSED"],"example":"ACTIVE","nullable":true},"currentPeriodEnd":{"type":"object","example":"2026-06-03T00:00:00.000Z","nullable":true},"hasPaidSubscription":{"type":"boolean","description":"True when the active subscription is paid via Stripe.","example":false},"complimentaryGrant":{"type":"boolean","description":"True when the active subscription is a complimentary grant.","example":true}},"required":["planCode","planName","status","currentPeriodEnd","hasPaidSubscription","complimentaryGrant"]},"ChangePasswordDto":{"type":"object","properties":{"currentPassword":{"type":"string","description":"Current account password","example":"old-password-123"},"newPassword":{"type":"string","description":"New account password (minimum 6 characters)","example":"new-password-123"}},"required":["currentPassword","newPassword"]},"SetRecentColorsDto":{"type":"object","properties":{"field":{"type":"string","description":"Form field this recent-colours list belongs to","enum":["backgroundColor","textColor","accentColor","headerTextColor","buttonBgColor","buttonTextColor","buttonBorderColor"],"example":"backgroundColor"},"colors":{"description":"Most-recently-used hex colours for this field, newest first","example":["#FF5733","#2D2929","#F4F1EC"],"type":"array","items":{"type":"string"}}},"required":["field","colors"]},"UpdateReminderDefaultsDto":{"type":"object","properties":{"defaultRemindOneWeekEmail":{"type":"boolean","description":"Default email reminder one week before","example":true},"defaultRemindOneWeekSms":{"type":"boolean","description":"Default SMS reminder one week before","example":false},"defaultRemindTwoDayEmail":{"type":"boolean","description":"Default email reminder two days before","example":true},"defaultRemindTwoDaySms":{"type":"boolean","description":"Default SMS reminder two days before","example":false},"defaultRemindDayOfEmail":{"type":"boolean","description":"Default email reminder on the day of the event","example":true},"defaultRemindDayOfSms":{"type":"boolean","description":"Default SMS reminder on the day of the event","example":false}}},"UpdateUserDto":{"type":"object","properties":{"name":{"type":"string","description":"The name of the user","example":"John Doe"},"image":{"type":"string","description":"User profile image URL","example":"https://example.com/image.jpg"}}},"CustomFontResponseDto":{"type":"object","properties":{"id":{"type":"string"},"fontFamily":{"type":"string"},"format":{"type":"string","enum":["WOFF","WOFF2","TTF","OTF"]},"fileUrl":{"type":"string","description":"Public URL for the @font-face src"},"sizeBytes":{"type":"object"},"createdAt":{"format":"date-time","type":"string"}},"required":["id","fontFamily","format","fileUrl","createdAt"]},"RegisterCustomFontDto":{"type":"object","properties":{"fontFamily":{"type":"string","description":"Display name shown in the font dropdown and used as the @font-face family","example":"Anna's Wedding Script"},"format":{"type":"string","enum":["WOFF","WOFF2","TTF","OTF"],"description":"Font file format"},"fileKey":{"type":"string","description":"Storage key returned by the /storage/upload-url presign step","example":"org_dev_001/users/fonts/anna-script-abc123.woff2"},"sizeBytes":{"type":"number","description":"File size in bytes (optional, surfaced to the host as quota information)"}},"required":["fontFamily","format","fileKey"]},"OnboardingStateResponseDto":{"type":"object","properties":{"profileCompleted":{"type":"boolean","description":"Whether the host has completed their profile","example":false},"hasCreatedFirstEvent":{"type":"boolean","description":"Whether the host has created their first event","example":false},"hasCompletedOnboarding":{"type":"boolean","description":"Whether the host has completed all onboarding steps","example":false},"completedSteps":{"description":"List of completed step identifiers","example":["profile"],"type":"array","items":{"type":"string"}}},"required":["profileCompleted","hasCreatedFirstEvent","hasCompletedOnboarding","completedSteps"]},"CompleteStepDto":{"type":"object","properties":{"step":{"type":"string","description":"The onboarding step to mark as complete","enum":["profile","firstEvent"],"example":"profile"}},"required":["step"]},"CustomCategoryResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"The id of the custom category","example":"123e4567-e89b-12d3-a456-426614174000"},"orgId":{"type":"string","description":"The organization ID of the custom category","example":"123e4567-e89b-12d3-a456-426614174000"},"code":{"type":"string","description":"The code of the custom category","example":"rsvp_status"},"name":{"type":"string","description":"The name of the custom category","example":"RSVP Status"},"description":{"type":"object","description":"The description of the custom category","example":"RSVP status options for events","nullable":true},"isSystem":{"type":"boolean","description":"Whether this is a system category","example":false},"createdAt":{"format":"date-time","type":"string","description":"The created at timestamp of the custom category","example":"2025-11-19T10:00:00.000Z"},"updatedAt":{"format":"date-time","type":"string","description":"The updated at timestamp of the custom category","example":"2025-11-19T10:00:00.000Z"},"icon":{"type":"object","description":"The icon for the custom category","example":"user","nullable":true}},"required":["id","orgId","code","name","description","isSystem","createdAt","updatedAt","icon"]},"PaginationMeta":{"type":"object","properties":{}},"GetAllCustomCategoryDto":{"type":"object","properties":{"success":{"type":"boolean","example":true,"description":"Indicates if the request was successful"},"statusCode":{"type":"number","example":200,"description":"HTTP status code"},"message":{"type":"string","example":"Operation completed successfully","description":"Response message"},"timestamp":{"type":"string","example":"2025-11-19T10:00:00.000Z","description":"Response timestamp"},"data":{"type":"array","items":{"$ref":"#/components/schemas/CustomCategoryResponseDto"}},"meta":{"$ref":"#/components/schemas/PaginationMeta"}},"required":["success","statusCode","message","timestamp","data","meta"]},"CreateCustomCategoryDto":{"type":"object","properties":{"code":{"type":"string","description":"The code of the custom category","example":"rsvp_status"},"name":{"type":"string","description":"The name of the custom category","example":"RSVP Status"},"description":{"type":"string","description":"The description of the custom category","example":"RSVP status options for events"},"isSystem":{"type":"boolean","description":"Whether this is a system category (cannot be deleted)","example":false,"default":false},"icon":{"type":"string","description":"The icon for the custom category","example":"user"}},"required":["code","name"]},"CustomCategoryOptionResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"The id of the custom category option","example":"123e4567-e89b-12d3-a456-426614174000"},"categoryId":{"type":"string","description":"The id of the custom category","example":"123e4567-e89b-12d3-a456-426614174000"},"optionName":{"type":"string","description":"The name of the custom category option","example":"Attending"},"optionValue":{"type":"string","description":"The value of the custom category option","example":"attending"},"sortOrder":{"type":"number","description":"The sort order of the custom category option","example":1},"isActive":{"type":"boolean","description":"Whether this option is active","example":true},"colorHex":{"type":"object","description":"The color hex code for this option","example":"#FF5733","nullable":true},"icon":{"type":"object","description":"The icon for this option","example":"user","nullable":true},"createdAt":{"format":"date-time","type":"string","description":"The created at timestamp","example":"2025-11-19T10:00:00.000Z"},"updatedAt":{"format":"date-time","type":"string","description":"The updated at timestamp","example":"2025-11-19T10:00:00.000Z"}},"required":["id","categoryId","optionName","optionValue","sortOrder","isActive","colorHex","icon","createdAt","updatedAt"]},"CustomCategoryWithOptionsResponseDto":{"type":"object","properties":{"id":{"type":"string","description":"The id of the custom category","example":"123e4567-e89b-12d3-a456-426614174000"},"orgId":{"type":"string","description":"The organization ID of the custom category","example":"123e4567-e89b-12d3-a456-426614174000"},"code":{"type":"string","description":"The code of the custom category","example":"rsvp_status"},"name":{"type":"string","description":"The name of the custom category","example":"RSVP Status"},"description":{"type":"object","description":"The description of the custom category","example":"RSVP status options for events","nullable":true},"isSystem":{"type":"boolean","description":"Whether this is a system category","example":false},"createdAt":{"format":"date-time","type":"string","description":"The created at timestamp of the custom category","example":"2025-11-19T10:00:00.000Z"},"updatedAt":{"format":"date-time","type":"string","description":"The updated at timestamp of the custom category","example":"2025-11-19T10:00:00.000Z"},"icon":{"type":"object","description":"The icon for the custom category","example":"user","nullable":true},"options":{"description":"The options of the custom category","type":"array","items":{"$ref":"#/components/schemas/CustomCategoryOptionResponseDto"}}},"required":["id","orgId","code","name","description","isSystem","createdAt","updatedAt","icon","options"]},"GetSingleCustomCategoryDto":{"type":"object","properties":{"success":{"type":"boolean","example":true,"description":"Indicates if the request was successful"},"statusCode":{"type":"number","example":200,"description":"HTTP status code"},"message":{"type":"string","example":"Operation completed successfully","description":"Response message"},"timestamp":{"type":"string","example":"2025-11-19T10:00:00.000Z","description":"Response timestamp"},"data":{"$ref":"#/components/schemas/CustomCategoryWithOptionsResponseDto"}},"required":["success","statusCode","message","timestamp","data"]},"UpdateCustomCategoryDto":{"type":"object","properties":{"code":{"type":"string","description":"The code of the custom category","example":"rsvp_status"},"name":{"type":"string","description":"The name of the custom category","example":"RSVP Status"},"description":{"type":"string","description":"The description of the custom category","example":"RSVP status options for events"},"isSystem":{"type":"boolean","description":"Whether this is a system category (cannot be deleted)","example":false}}},"CreateCustomCategoryOptionDto":{"type":"object","properties":{"optionName":{"type":"string","description":"The name of the option","example":"Attending"},"optionValue":{"type":"string","description":"The value of the option","example":"attending"},"sortOrder":{"type":"number","description":"The sort order of the option","example":1},"isActive":{"type":"boolean","description":"Whether this option is active","example":true,"default":true},"colorHex":{"type":"string","description":"The color hex code for this option","example":"#FF5733"},"icon":{"type":"string","description":"The icon for this option","example":"user"}},"required":["optionName","optionValue","sortOrder"]},"UpdateCustomCategoryOptionDto":{"type":"object","properties":{"optionName":{"type":"string","description":"The name of the option","example":"Attending"},"optionValue":{"type":"string","description":"The value of the option","example":"attending"},"sortOrder":{"type":"number","description":"The sort order of the option","example":1},"isActive":{"type":"boolean","description":"Whether this option is active","example":true},"colorHex":{"type":"string","description":"The color hex code for this option","example":"#FF5733"},"icon":{"type":"string","description":"The icon for this option","example":"user"}}},"ExtractResponseDto":{"type":"object","properties":{"data":{"type":"object","description":"Extracted event data"},"confidence":{"type":"number","description":"Confidence score from 0 to 1","example":0.85},"mode":{"type":"string","description":"Extraction mode that was used","example":"full"}},"required":["data","confidence","mode"]},"ScheduleJobResponseDto":{"type":"object","properties":{"jobName":{"type":"string"},"cronExpression":{"type":"string"},"description":{"type":"string"},"manualTriggerEnabled":{"type":"boolean"}},"required":["jobName","cronExpression","description","manualTriggerEnabled"]},"TriggerScheduleDto":{"type":"object","properties":{"jobName":{"type":"string","description":"The name of the job to trigger","enum":["outbox.process","outbox.cleanup","event.reminders.process","event.event-reminders.process","billing.sms-credits.expire"],"example":"outbox.process"}},"required":["jobName"]},"ScheduleTriggerResponseDto":{"type":"object","properties":{"jobName":{"type":"string"},"startedAt":{"format":"date-time","type":"string"},"finishedAt":{"format":"date-time","type":"string"},"durationMs":{"type":"number"},"status":{"type":"string","enum":["success","skipped","failed"]},"result":{"type":"object"},"error":{"type":"string"}},"required":["jobName","startedAt","finishedAt","durationMs","status"]},"EventCountsDto":{"type":"object","properties":{"total":{"type":"number","example":12,"description":"Total events created"},"published":{"type":"number","example":5,"description":"Currently published events"},"draft":{"type":"number","example":3,"description":"Events in draft status"},"closed":{"type":"number","example":4,"description":"Closed events (auto + manual)"},"newThisMonth":{"type":"number","example":2,"description":"New events created this month"},"publishedChangeThisWeek":{"type":"number","example":1,"description":"Net published event change compared to last week"}},"required":["total","published","draft","closed","newThisMonth","publishedChangeThisWeek"]},"GuestAggregatesDto":{"type":"object","properties":{"total":{"type":"number","example":150,"description":"Total guests across all events"},"attending":{"type":"number","example":90,"description":"Guests who accepted"},"declined":{"type":"number","example":20,"description":"Guests who declined"},"pending":{"type":"number","example":40,"description":"Guests who have not responded"},"responseRate":{"type":"number","example":73,"description":"Percentage of guests who responded"},"responseRateChange":{"type":"number","example":12,"description":"Change in response rate compared to last week, in percentage points"}},"required":["total","attending","declined","pending","responseRate","responseRateChange"]},"HeadcountDto":{"type":"object","properties":{"totalAdults":{"type":"number","example":120,"description":"Total attending adults"},"totalChildren":{"type":"number","example":15,"description":"Total attending children"},"totalHeadcount":{"type":"number","example":135,"description":"Total headcount (adults + children)"}},"required":["totalAdults","totalChildren","totalHeadcount"]},"CommunicationStatsDto":{"type":"object","properties":{"smsSent":{"type":"number","example":50,"description":"Total SMS messages sent"},"emailsSent":{"type":"number","example":200,"description":"Total emails sent"},"invitationsSent":{"type":"number","example":150,"description":"Total invitations sent"},"remindersSent":{"type":"number","example":80,"description":"Total reminders sent"}},"required":["smsSent","emailsSent","invitationsSent","remindersSent"]},"RecentEventDto":{"type":"object","properties":{"id":{"type":"string","example":"cmieqoaba0000iymt4fab0345"},"name":{"type":"string","example":"Sarah & John Wedding"},"slug":{"type":"string","example":"sarah-john-wedding-abc123"},"status":{"type":"string","example":"PUBLISHED"},"eventDate":{"type":"object","example":"2026-06-15T00:00:00.000Z","nullable":true},"locationName":{"type":"object","example":"Grand Ballroom","nullable":true},"tier":{"type":"string","example":"ESSENTIAL"},"createdAt":{"format":"date-time","type":"string","example":"2026-04-01T10:00:00.000Z"},"guestCount":{"type":"number","example":45}},"required":["id","name","slug","status","eventDate","locationName","tier","createdAt","guestCount"]},"UpcomingEventDto":{"type":"object","properties":{"id":{"type":"string","example":"cmieqoaba0000iymt4fab0345"},"name":{"type":"string","example":"Sarah & John Wedding"},"slug":{"type":"string","example":"sarah-john-wedding-abc123"},"eventDate":{"type":"object","example":"2026-06-15T00:00:00.000Z","nullable":true},"locationName":{"type":"object","example":"Grand Ballroom","nullable":true},"rsvpBy":{"type":"object","example":"2026-06-01T00:00:00.000Z","nullable":true},"attendingCount":{"type":"number","example":30}},"required":["id","name","slug","eventDate","locationName","rsvpBy","attendingCount"]},"DashboardKpiDto":{"type":"object","properties":{"events":{"$ref":"#/components/schemas/EventCountsDto"},"guests":{"$ref":"#/components/schemas/GuestAggregatesDto"},"headcount":{"$ref":"#/components/schemas/HeadcountDto"},"communications":{"$ref":"#/components/schemas/CommunicationStatsDto"},"recentEvents":{"description":"Last 5 events created","type":"array","items":{"$ref":"#/components/schemas/RecentEventDto"}},"upcomingEvents":{"description":"Next 5 upcoming published events","type":"array","items":{"$ref":"#/components/schemas/UpcomingEventDto"}}},"required":["events","guests","headcount","communications","recentEvents","upcomingEvents"]},"GetDashboardKpiDto":{"type":"object","properties":{"success":{"type":"boolean","example":true,"description":"Indicates if the request was successful"},"statusCode":{"type":"number","example":200,"description":"HTTP status code"},"message":{"type":"string","example":"Operation completed successfully","description":"Response message"},"timestamp":{"type":"string","example":"2025-11-19T10:00:00.000Z","description":"Response timestamp"},"data":{"$ref":"#/components/schemas/DashboardKpiDto"}},"required":["success","statusCode","message","timestamp","data"]},"AdminRevenueDto":{"type":"object","properties":{"total":{"type":"number","example":15800},"subscriptions":{"type":"number","example":7900},"eventUpgrades":{"type":"number","example":6900},"smsTopups":{"type":"number","example":1000},"currency":{"type":"string","example":"aud"}},"required":["total","subscriptions","eventUpgrades","smsTopups","currency"]},"AdminUserStatsDto":{"type":"object","properties":{"total":{"type":"number","example":25},"admins":{"type":"number","example":2},"hosts":{"type":"number","example":23},"newThisMonth":{"type":"number","example":5}},"required":["total","admins","hosts","newThisMonth"]},"AdminEventStatsDto":{"type":"object","properties":{"total":{"type":"number","example":40},"published":{"type":"number","example":25},"draft":{"type":"number","example":10},"closed":{"type":"number","example":5}},"required":["total","published","draft","closed"]},"AdminSubscriptionStatsDto":{"type":"object","properties":{"active":{"type":"number","example":8},"canceled":{"type":"number","example":2},"total":{"type":"number","example":10}},"required":["active","canceled","total"]},"AdminGuestStatsDto":{"type":"object","properties":{"total":{"type":"number","example":500},"attending":{"type":"number","example":300},"declined":{"type":"number","example":50},"pending":{"type":"number","example":150},"responseRate":{"type":"number","example":70}},"required":["total","attending","declined","pending","responseRate"]},"RecentSignupDto":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"object"},"email":{"type":"string"},"accountType":{"type":"string"},"createdAt":{"format":"date-time","type":"string"}},"required":["id","name","email","accountType","createdAt"]},"RecentTransactionDto":{"type":"object","properties":{"id":{"type":"string"},"type":{"type":"string"},"amount":{"type":"object"},"currency":{"type":"object"},"status":{"type":"string"},"userName":{"type":"object"},"createdAt":{"format":"date-time","type":"string"}},"required":["id","type","amount","currency","status","userName","createdAt"]},"AdminKpiResponseDto":{"type":"object","properties":{"revenue":{"$ref":"#/components/schemas/AdminRevenueDto"},"users":{"$ref":"#/components/schemas/AdminUserStatsDto"},"events":{"$ref":"#/components/schemas/AdminEventStatsDto"},"subscriptions":{"$ref":"#/components/schemas/AdminSubscriptionStatsDto"},"guests":{"$ref":"#/components/schemas/AdminGuestStatsDto"},"recentSignups":{"type":"array","items":{"$ref":"#/components/schemas/RecentSignupDto"}},"recentTransactions":{"type":"array","items":{"$ref":"#/components/schemas/RecentTransactionDto"}}},"required":["revenue","users","events","subscriptions","guests","recentSignups","recentTransactions"]},"CreateContactInquiryDto":{"type":"object","properties":{"name":{"type":"string","description":"Sender full name","example":"Jane Smith"},"email":{"type":"string","description":"Sender email address","example":"jane@example.com"},"subject":{"type":"string","description":"Subject (free-text, sourced from Sanity options)","example":"General Enquiry"},"message":{"type":"string","description":"Message body","example":"I have a question about pricing…"}},"required":["name","email","subject","message"]},"CreateSupportTicketDto":{"type":"object","properties":{"subject":{"type":"string","description":"The subject of the support ticket","example":"Cannot publish my event"},"category":{"type":"string","description":"The category of the support ticket","enum":["BILLING","EVENTS","TECHNICAL","CONTACT_INQUIRY","OTHER"],"example":"TECHNICAL"},"message":{"type":"string","description":"The initial message for the support ticket","example":"I am trying to publish my event but the button is not working."},"priority":{"type":"string","description":"The priority of the support ticket","enum":["LOW","NORMAL","HIGH","URGENT"],"example":"NORMAL","default":"NORMAL"}},"required":["subject","category","message"]},"ReplySupportTicketDto":{"type":"object","properties":{"body":{"type":"string","description":"The reply message body","example":"Thank you for reaching out. We are looking into this."}},"required":["body"]},"UpdateSupportTicketDto":{"type":"object","properties":{"status":{"type":"string","description":"The status of the support ticket","enum":["OPEN","IN_PROGRESS","RESOLVED","CLOSED"],"example":"IN_PROGRESS"},"priority":{"type":"string","description":"The priority of the support ticket","enum":["LOW","NORMAL","HIGH","URGENT"],"example":"HIGH"}}}}},"security":[{"bearer-auth":[],"x-org-id":[]}]}