Public API Resources
The public API reads Events, Artists, Venues, Event Series and Series-scoped Editions. A collection read with filters is sometimes called discovery: it finds records without requiring their public IDs in advance. Detail reads address an already identified record. Both use the same anonymous HTTP, validation and cache conventions described in Public API Foundation.
Routes And Traversal
Section titled “Routes And Traversal”All paths below begin with /v1. These seventeen GET operations also support HEAD and browser preflight. /health and /ready are the two separate operational reads.
| Resource | Collection And Detail | Related Collections |
|---|---|---|
| Event | /events, /events/{eventId} | /events/{eventId}/artists, /venues, /event-series and /editions, each beneath that Event path. |
| Artist | /artists, /artists/{artistId} | Events through /events?artistId=…. |
| Venue | /venues, /venues/{venueId} | Events through /events?venueId=…. |
| Series | /event-series, /event-series/{seriesId} | /event-series/{seriesId}/events for direct-event Series; /event-series/{seriesId}/editions for edition-based Series. |
| Edition | The Series-scoped collection above; /event-series/{seriesId}/editions/{editionId} | /events and /venues beneath the Edition path. Venues are its official Venues. |
An addressed missing/inactive resource or invalid ancestry returns 404. An eligible empty collection returns 200 with []. A filter on an unknown/inactive context instead returns an empty successful Event collection. Edition identities must belong to the addressed active Series. Direct Events and Editions are exclusive Series structures; a traversal that does not apply is not an empty substitute for the other structure.
Collection Controls
Section titled “Collection Controls”Collections default to limit=20 and allow at most 50. Follow links.next against the same API origin until null, preserving its effective parameters. All fields and operators are allowlisted; unsupported parameters, duplicate scalars and invalid combinations fail before read admission. There are no exact totals, offset controls or fuzzy search.
| Collection | Filters | Ordering |
|---|---|---|
| Event | One effective context: artistId, venueId, seriesId, or seriesId with editionId. Also nameContains, cityContains, countryContains, startLocalDateGte, startLocalDateLt, startInstantGte, startInstantLt, temporalPhaseCode, administrativeStateCode. | startLocalDate (default), -startLocalDate, startInstant, -startInstant, updatedAt. |
| Artist | nameContains. Independent of Event membership; no Event-count filters or sorts. | name (default), -name, updatedAt. |
| Venue | nameContains, locality, countryCode, statusCode. | name (default), -name, updatedAt. |
| Series | nameContains, categoryCode, structureCode, continuityCode. | name (default), -name, updatedAt. |
| Edition | Addressed Series; dispositionCode, programDateGte, programDateLt. | programStart (default), -programStart, updatedAt. |
Each of these collections also supports updatedSince with explicit sort=updatedAt and fields containing updatedAt. Filters combine with AND. Detail routes accept field selection rather than collection controls. Parent-scoped Event traversals reuse the Event controls but reject caller-supplied context overrides. Series filters/sorts share their meaning with the main application’s Series index; public pagination, projections and refresh remain separate transport contracts.
Artist, Venue and Series name matching is a case-insensitive literal substring: surrounding whitespace is ignored, % and _ are ordinary characters, and no accent folding is added. Venue locality uses case-insensitive equality after whitespace trimming; country code and status are equality filters. Event text filters preserve the main Event query’s original/normalized LIKE semantics, including % and _ wildcards. Event city/country filters use the canonical primary active-Venue city/country selection independently; they do not mean any linked Venue or a country code. Individual text bounds do not guarantee every maximum-length combination fits the aggregate request budget.
Event local-date ranges compare the organizer’s start date, with an inclusive lower and exclusive upper bound. Exact-start bounds compare UTC instants with zero through six fractional digits, normalized to six; date-only Events have no instant and are excluded by exact-start filtering/sorting. Calendar order keeps timed Events before date-only Events on the same date in both directions. Public ID ascending breaks ties. Temporal phase uses the request’s wall clock; cancellation is a separate administrative state and remains readable.
Edition date ranges select program windows that overlap the requested calendar range in the Edition’s authored reference time zone. DATE ends include the final authored day; timed ends are exclusive. Any range excludes TBA windows. Program ordering compares local start date and exact timed start, with DATE last on a shared day and TBA last in both directions, then public ID ascending. The Edition window is independent of its program Events’ schedules. Time-zone/DST and repeated-hour boundaries retain the canonical domain meaning.
Name order is bytewise, with ascending public ID ties. Event Edition-membership traversal uses sort=seriesName: Series name/ID followed by Edition name/ID. Other compact relationship reads use sort=name. The parent-scoped Edition collection has its own program/refresh controls; official-Venue and reverse-membership routes do not inherit standalone refresh filters.
Fields And Bounded Relationships
Section titled “Fields And Bounded Relationships”fields replaces the default top-level field set; publicId always remains. Composite objects are atomic, and dotted paths are unsupported. Selected absent nullable values are null; unselected fields are omitted; selected empty collections are []. Public adapters explicitly allowlist output, keeping internal IDs, storage locators, provenance, authoring timestamps and revisions private.
| Resource | Collection Defaults | Rich Detail Adds |
|---|---|---|
| Event | Identity, slug, name, schedule, administrative state, profile media, Artist/Venue previews. | Description, exact marker, event type, digital locations, links, ticket links, direct-Series and Edition-program previews. |
| Artist | Identity, slug, name, profile media. | Description, links, exact marker. |
| Venue | Identity, slug, name, operational status, public location, profile media. | Description, capacity, contact, permitted coordinates, links, exact marker. |
| Series | Identity, slug, name, category, structure, continuity, profile media. | Description, links, ticket links, exact marker and the applicable direct-Event or Edition preview. |
| Edition | Identity, slug, name, disposition, program window, location summary, attendance mode, profile media. | Parent Series reference, description, links, ticket links, exact marker, program and official-Venue previews. |
The rich fields can also be selected on collections. Event include=artists,venues upgrades those selected previews to their existing richer child projections by one level. Selecting an include without its relationship field is invalid. Other families use their defined nonrecursive previews rather than arbitrary nested inclusion.
Each selected preview uses { items, isComplete, href }, with at most five items. isComplete=false means the prefix is incomplete; follow href and subsequent links.next to traverse the collection. Even a complete preview supplies its relationship URL. A selected Series relationship that does not apply to the actual structure is omitted, including when structureCode itself was not selected.
Events expose direct-Series and Edition-program memberships separately. Edition-program items retain the Edition’s public identity, disposition and program window plus its actual Series reference. They are grouped by Series ordering without duplicating a recursively nested Series resource. The corresponding routes allow Events with dozens of memberships to remain fully traversable.
Official Venue membership belongs to the Edition and does not imply program Event membership. Event context for a Series spans its supported memberships; /event-series/{seriesId}/events specifically traverses a direct-event Series. Do not substitute one operation for the other.
Complete URL collections contain at most 25 HTTP(S) links; selected persisted overflow or invalid data produces a sanitized error, never silent truncation. Profile media follows ready-media selection, including the established legacy Artist rule. Exact Venue coordinates appear only when publication permits them. Galleries, private authoring data, global Edition discovery and optional additional Event sorts are excluded from this contract.
Independent Change Ownership
Section titled “Independent Change Ownership”updatedAt is the resource’s own public marker, stored and returned with PostgreSQL microsecond precision. Compare canonical timestamp strings without a JavaScript Date round trip. Incremental reads are inclusive, ordered by exact marker and public ID; overlap and deduplicate retries. Advance a saved checkpoint only after every page succeeds and retain the previous checkpoint on empty results or failure.
| Marker Owner | Meaningful Changes Covered |
|---|---|
| Event | Event scalars/schedule/state, its owned Artist/Venue/URL relationships, supported reverse Artist/Venue authoring paths, and eligible profile-media changes. |
| Artist | Artist scalars, bounded links and eligible selected profile media. Event associations retain their Event ownership. |
| Venue | Venue public scalars/location/contact/links and eligible profile media. Event associations retain their Event ownership. |
| Series | Series public scalars/links/profile media, direct-Event membership, and creation/removal of owned Editions. |
| Edition | Edition scalars/program window/links/profile media, program membership and official-Venue membership. |
Supported main-app writers lock the relevant owner before comparing public content and stamp meaningful changes atomically. Public no-ops retain markers; existing authoring revision/concurrency rules remain independent. Migrations initialize retained rows at a cutover rather than reconstructing historical edits; seeds stamp their baseline after content and memberships are composed. This contract covers the inspected application writers, not arbitrary SQL mutations.
Related content can change a representation without advancing its root marker. In particular, Series/Edition-owned membership insertion/removal, lifecycle changes and selected parent/window edits can change an Event response while Event.updatedAt stays stable. Artist/Venue edits, Edition children and clock-derived schedule/program classifications have the same independent-ownership consequence. A response ETag validates the entire selected representation, including related data, pagination and next links.
Use If-None-Match for an exact saved URL and reuse its saved body after 304. Periodically reconcile the complete relevant collection: removals, records leaving filters and delayed commits are not a lossless change feed. Neither an incremental absence nor a 304 advances a commit watermark. Cache delivery time is not data progress. See the foundation’s refresh contract.
Consumer Examples
Section titled “Consumer Examples”For an edition-based Series, follow these steps using actual returned public IDs:
GET /v1/event-series?nameContains=Festival&fields=name,structureCode,updatedAt&sort=updatedAtGET /v1/event-series/{seriesId}/editions?programDateGte=2026-01-01&programDateLt=2027-01-01GET /v1/event-series/{seriesId}/editions/{editionId}GET /v1/event-series/{seriesId}/editions/{editionId}/events?fields=name,scheduleGET /v1/event-series/{seriesId}/editions/{editionId}/venuesFor a direct-event Series, select its /events traversal. Artist collection reads can be followed by /events?artistId=…. Event rich details provide reverse membership preview links. Follow returned links instead of inferring completeness from an item count.
The platform-fetch example in apps/wavemap-public-api/examples/resources.mjs reads Artist, Series and Edition collections with independent markers and selects the correct structure for Event traversal. It reuses collection.mjs for same-origin continuation, exact-URL conditional caches, omitted credentials, redirect rejection and a 100-page safety ceiling. No SDK or import-time network operation is introduced.
node apps/wavemap-public-api/examples/resources-server.mjs http://127.0.0.1:6002 "Your Series Name" "Your Artist Name"Supply the actual local API origin and names present in its database. The executable prints discovery results, one selected program and conditional-revalidation results. Its focused mocked-fetch tests verify request construction, exact timestamp preservation, cached 304 handling and structure selection. The existing Venue/browser example and disposable listener workflow are described in Public API Foundation.
Read Bounds And Verification
Section titled “Read Bounds And Verification”| Read Shape | SQL Statements | Maximum Concurrent Statements |
|---|---|---|
| Rich Event page with all four relationship previews | 8 | 3 |
| Rich standalone Artist or Venue page | 3 | 2 |
| Rich Series or Edition page with applicable previews | 7 | 2 |
Compact name resolution can add one statement where needed while remaining inside the service’s eight-statement ceiling. Empty/sparse selections avoid unnecessary hydration. Limits are applied before child media enrichment, including per-parent sentinels; a sentinel owner is never hydrated. The maximum rich Event fixture returns 11,801 SQL rows. These are fixture-backed structural bounds, not production latency, byte-size or database-scan guarantees.
Collection admission reserves the full 1,024-character cursor in the normalized absolute URL and counts default/continuation parameters before SQL. This deliberately rejects some long combinations even when a short cursor or empty result might fit. It prevents accepted pages from emitting links that exceed the configured URL or parameter limits.
The public app’s test/database files cover injected reads and mounted restricted-PostgreSQL HTTP behavior by resource; seriesPreviewReads.test.ts, seriesPreviewsHTTP.test.ts, eventMembershipReads.test.ts and eventMembershipsHTTP.test.ts own relationship bounds and complete traversal. Backend public-content/media tests own writer locks, markers and rollback. continuationCapacity.test.ts owns HTTP continuation admission; resourceConsumer.test.ts owns the new consumer example. Mocked tests and real database tests prove different boundaries.
OpenAPI generation uses the same request/response schemas and complete resource-owned operation declarations. See the generated v1 reference and consumer guide. The Development CloudFront/origin and reader boundaries are covered in Public API Runtime. Documentation publication, representative capacity/cost measurements, compatibility commitments and reuse terms remain release checkpoints.