Allows guests to cancel their RSVP via a DELETE endpoint using their guestToken. Frontend shows cancel button in RsvpBar and clears local storage on success. Includes unit tests, integration tests, and E2E spec. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
375 lines
9.6 KiB
YAML
375 lines
9.6 KiB
YAML
openapi: 3.1.0
|
|
info:
|
|
title: fete API
|
|
description: Privacy-focused event announcements and RSVPs
|
|
version: 0.1.0
|
|
license:
|
|
name: GPL-3.0-or-later
|
|
identifier: GPL-3.0-or-later
|
|
|
|
servers:
|
|
- url: /api
|
|
|
|
paths:
|
|
/events:
|
|
post:
|
|
operationId: createEvent
|
|
summary: Create a new event
|
|
tags:
|
|
- events
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/CreateEventRequest"
|
|
responses:
|
|
"201":
|
|
description: Event created successfully
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/CreateEventResponse"
|
|
"400":
|
|
description: Validation failed
|
|
content:
|
|
application/problem+json:
|
|
schema:
|
|
$ref: "#/components/schemas/ValidationProblemDetail"
|
|
|
|
/events/{token}/rsvps/{rsvpToken}:
|
|
delete:
|
|
operationId: cancelRsvp
|
|
summary: Cancel RSVP
|
|
description: |
|
|
Permanently deletes an RSVP identified by the RSVP token.
|
|
Idempotent: returns 204 whether the RSVP existed or not.
|
|
tags:
|
|
- events
|
|
parameters:
|
|
- name: token
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
description: Event token (UUID)
|
|
- name: rsvpToken
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
description: RSVP token (UUID) identifying the attendance to cancel
|
|
responses:
|
|
"204":
|
|
description: >
|
|
RSVP successfully cancelled (or was already cancelled).
|
|
No response body.
|
|
"500":
|
|
description: Internal server error
|
|
|
|
/events/{token}/rsvps:
|
|
post:
|
|
operationId: createRsvp
|
|
summary: Submit an RSVP for an event
|
|
tags:
|
|
- events
|
|
parameters:
|
|
- name: token
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
description: Public event token
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/CreateRsvpRequest"
|
|
responses:
|
|
"201":
|
|
description: RSVP created successfully
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/CreateRsvpResponse"
|
|
"400":
|
|
description: Validation failed (e.g. blank name)
|
|
content:
|
|
application/problem+json:
|
|
schema:
|
|
$ref: "#/components/schemas/ValidationProblemDetail"
|
|
"404":
|
|
description: Event not found
|
|
content:
|
|
application/problem+json:
|
|
schema:
|
|
$ref: "#/components/schemas/ProblemDetail"
|
|
"409":
|
|
description: Event has expired — RSVPs no longer accepted
|
|
content:
|
|
application/problem+json:
|
|
schema:
|
|
$ref: "#/components/schemas/ProblemDetail"
|
|
|
|
/events/{token}/attendees:
|
|
get:
|
|
operationId: getAttendees
|
|
summary: Get attendee list for an event (organizer only)
|
|
tags:
|
|
- events
|
|
parameters:
|
|
- name: token
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
description: Public event token
|
|
- name: organizerToken
|
|
in: query
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
description: Organizer token for authorization
|
|
responses:
|
|
"200":
|
|
description: Attendee list
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/GetAttendeesResponse"
|
|
"403":
|
|
description: Invalid organizer token
|
|
content:
|
|
application/problem+json:
|
|
schema:
|
|
$ref: "#/components/schemas/ProblemDetail"
|
|
"404":
|
|
description: Event not found
|
|
content:
|
|
application/problem+json:
|
|
schema:
|
|
$ref: "#/components/schemas/ProblemDetail"
|
|
|
|
/events/{token}:
|
|
get:
|
|
operationId: getEvent
|
|
summary: Get public event details by token
|
|
tags:
|
|
- events
|
|
parameters:
|
|
- name: token
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
description: Public event token
|
|
responses:
|
|
"200":
|
|
description: Event found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: "#/components/schemas/GetEventResponse"
|
|
"404":
|
|
description: Event not found
|
|
content:
|
|
application/problem+json:
|
|
schema:
|
|
$ref: "#/components/schemas/ProblemDetail"
|
|
|
|
components:
|
|
schemas:
|
|
CreateEventRequest:
|
|
type: object
|
|
required:
|
|
- title
|
|
- dateTime
|
|
- timezone
|
|
properties:
|
|
title:
|
|
type: string
|
|
minLength: 1
|
|
maxLength: 200
|
|
description:
|
|
type: string
|
|
maxLength: 2000
|
|
dateTime:
|
|
type: string
|
|
format: date-time
|
|
description: Event date and time with UTC offset (ISO 8601)
|
|
example: "2026-03-15T20:00:00+01:00"
|
|
timezone:
|
|
type: string
|
|
description: IANA timezone of the organizer
|
|
example: "Europe/Berlin"
|
|
location:
|
|
type: string
|
|
maxLength: 500
|
|
|
|
CreateEventResponse:
|
|
type: object
|
|
required:
|
|
- eventToken
|
|
- organizerToken
|
|
- title
|
|
- dateTime
|
|
- timezone
|
|
properties:
|
|
eventToken:
|
|
type: string
|
|
format: uuid
|
|
description: Public token for the event URL
|
|
example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
|
|
organizerToken:
|
|
type: string
|
|
format: uuid
|
|
description: Secret token for organizer access
|
|
example: "f9e8d7c6-b5a4-3210-fedc-ba9876543210"
|
|
title:
|
|
type: string
|
|
example: "Summer BBQ"
|
|
dateTime:
|
|
type: string
|
|
format: date-time
|
|
example: "2026-03-15T20:00:00+01:00"
|
|
timezone:
|
|
type: string
|
|
description: IANA timezone of the organizer
|
|
example: "Europe/Berlin"
|
|
|
|
GetEventResponse:
|
|
type: object
|
|
required:
|
|
- eventToken
|
|
- title
|
|
- dateTime
|
|
- timezone
|
|
- attendeeCount
|
|
properties:
|
|
eventToken:
|
|
type: string
|
|
format: uuid
|
|
description: Public event token
|
|
example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
|
|
title:
|
|
type: string
|
|
description: Event title
|
|
example: "Summer BBQ"
|
|
description:
|
|
type: string
|
|
description: Event description (absent if not set)
|
|
example: "Bring your own drinks!"
|
|
dateTime:
|
|
type: string
|
|
format: date-time
|
|
description: Event date/time with organizer's UTC offset
|
|
example: "2026-03-15T20:00:00+01:00"
|
|
timezone:
|
|
type: string
|
|
description: IANA timezone name of the organizer
|
|
example: "Europe/Berlin"
|
|
location:
|
|
type: string
|
|
description: Event location (absent if not set)
|
|
example: "Central Park, NYC"
|
|
attendeeCount:
|
|
type: integer
|
|
minimum: 0
|
|
description: Number of confirmed attendees (attending=true)
|
|
example: 12
|
|
|
|
CreateRsvpRequest:
|
|
type: object
|
|
required:
|
|
- name
|
|
properties:
|
|
name:
|
|
type: string
|
|
minLength: 1
|
|
maxLength: 100
|
|
description: Guest's display name
|
|
example: "Max Mustermann"
|
|
|
|
CreateRsvpResponse:
|
|
type: object
|
|
required:
|
|
- rsvpToken
|
|
- name
|
|
properties:
|
|
rsvpToken:
|
|
type: string
|
|
format: uuid
|
|
description: Token identifying this RSVP (store client-side for future updates)
|
|
example: "d4e5f6a7-b8c9-0123-4567-890abcdef012"
|
|
name:
|
|
type: string
|
|
description: Guest's display name as stored
|
|
example: "Max Mustermann"
|
|
|
|
GetAttendeesResponse:
|
|
type: object
|
|
required:
|
|
- attendees
|
|
properties:
|
|
attendees:
|
|
type: array
|
|
items:
|
|
$ref: "#/components/schemas/Attendee"
|
|
example:
|
|
- name: "Alice"
|
|
- name: "Bob"
|
|
|
|
Attendee:
|
|
type: object
|
|
required:
|
|
- name
|
|
properties:
|
|
name:
|
|
type: string
|
|
minLength: 1
|
|
maxLength: 100
|
|
example: "Alice"
|
|
|
|
ProblemDetail:
|
|
type: object
|
|
properties:
|
|
type:
|
|
type: string
|
|
format: uri
|
|
default: "about:blank"
|
|
title:
|
|
type: string
|
|
status:
|
|
type: integer
|
|
detail:
|
|
type: string
|
|
instance:
|
|
type: string
|
|
format: uri
|
|
additionalProperties: true
|
|
|
|
ValidationProblemDetail:
|
|
allOf:
|
|
- $ref: "#/components/schemas/ProblemDetail"
|
|
- type: object
|
|
properties:
|
|
fieldErrors:
|
|
type: array
|
|
items:
|
|
type: object
|
|
required:
|
|
- field
|
|
- message
|
|
properties:
|
|
field:
|
|
type: string
|
|
message:
|
|
type: string
|