--- /dev/null
+# Public API
+
+## Base URI
+
+https://alttp.localhorst.tv/api
+
+
+## Episodes
+
+### List/Search
+
+GET /episodes
+
+Parameters:
+- after: Date
+- before: Date
+- event[]: Integer
+- eventInvert: Boolean; switches event[] to exclusion filter
+- game[]: String
+- gameInvert: Boolean; switches game[] to exclusion filter
+- limit: Integer; default 100
+- offset: Integer
+- reverse: Boolean
+
+Response: Array of Episode objects with following fields:
+- channels: Array of Channel objects
+- comment: String; supplementary information, may be empty, not localised
+- confirmed: Boolean
+- crew: Array of EpisodeCrew objects
+- estimate: Integer; approximate runtime in seconds
+- event: Object of type Event
+- ext_id: String or null; if externally managed, reference to foreign identifier
+- game: String or null
+- id: Integer
+- players: Array of EpisodePlayer objects; with user if available
+- raceroom: String or null; URL of race room if available
+- start: Date; includes delay, if any
+- title: String
+
+Channel fields:
+- id: Integer
+- languages: Array of Strings; 2 letter codes
+- pivot.accept_comms: Boolean; if the channel is looking for comms for this episode
+- pivot.accept_tracker: Boolean; if the channel is looking for trackers for this episode
+- short_name: String; abbreviated title, if any
+- stream_link: String; URL of stream
+- title: String
+
+EpisodeCrew fields:
+- channel_id: Integer or null; channel may be unset for tracking role
+- confirmed: Boolean
+- id: Integer
+- name_override: String; optional alias to show instead of username
+- role: String; one of "commentary", "setup", "tracking"
+- stream_override: String; optional URL of stream source to prefer over user's stream_link
+- user: Object of type User or null
+- user_id: String or null; user's Discord ID if available
+
+EpisodePlayer fields:
+- name_override: String; optional alias to show instead of username
+- stream_override: String; optional URL of stream source to prefer over user's stream_link
+- user: Object of type User or null
+- user_id: String or null; user's Discord ID if available
+
+User fields:
+- discriminator: String; 4 digits for pre-migration Discord accounts, "0" otherwise
+- id: String; Discord user ID
+- stream_link: String; regular stream URL if known
+- username: String
+
+
+## Events
+
+### List/Search
+
+GET /events
+
+Parameters:
+- after: Date
+- before: Date
+- exclude_ids[]: Integer
+- limit: Integer
+- order: String; currently just "recency"
+- phrase: String; matches title
+- with[]: String; currently just "description"
+
+Response: Array of Event objects with following fields:
+- banner: String; host-relative URL to banner image (discord event layout) or empty
+- corner: String; host-relative URL to corner image (bottom left background) or empty
+- description: optional object of type Content or null; only present if with[]=description was requested, may be null if event has no description
+- end: Date or null
+- external_schedule: String or null; if externally managed, this contains the reference
+- game: String or null
+- id: Integer
+- name: String
+- racetime_category: String or null
+- short: String; shorter version of title, may be empty
+- start: Date or null
+- title: String
+
+Content fields:
+- description: String; HTML part
+- short: String; plain text
+- title: String
+- translations: Array of ContentTranslation objects
+
+ContentTranslation fields:
+- description: String; HTML part
+- locale: String; regional code for which this translation is applicable
+- short: String; plain text
+- title: String
+
+### Single
+
+GET /events/<name>
+
+Parameters: none
+
+Response: Event object, same fields as Search with description