Catalog, machine ingest, and admin endpoints (app package v0.2.0). Paths are relative to your deployment (for example https://budapest-night.vercel.app).
Pesti Est exposes JSON APIs for the public catalog, a machine ingest pipeline secured by INGEST_API_KEY, and browser session APIs for the admin console. Unless noted, request and response bodies use application/json with UTF-8.
Public routes are read-only and safe to call from browsers or edge caches (no secrets required).
Admin routes require an HTTP-only cookie set by POST /api/admin/login; use a browser or forward Cookie from the same origin.
Ingest is intended for servers, ETL jobs, or trusted partners — never expose INGEST_API_KEY in client-side code.
Prices are stored in HUF on providers and menu items; the web app reads currencyRates from GET /api/public/site for EUR/USD display.
Venue & event URLs (web app)
These paths are implemented by the Next.js app (not separate JSON resources). They open the venue sheet or a shareable full page and accept either a canonical slug or a legacy key.
/venue/{slug} — venue profile (query ?from=venues|events|… controls back navigation).
/venue/{slug}/full — full-page venue layout with share chrome.
/event/{slug} and /event/{slug}/full — timed event profiles.
Canonical slugs must not embed the wrong Budapest district (e.g. use budapest-park for the Ferencváros open-air park, not prov-budapest-park-ferencvaros). Set locales.en.slug on ingest; logic lives in src/lib/venueSlug.ts. Legacy prov-* URL segments still resolve but the app redirects to the canonical slug.
Timed events link to hosts via venueIds (internal ids). The venue UI lists upcoming events whose venueIds include that provider.
Public catalog (read)
These endpoints read from MongoDB when MONGODB_URI is configured; otherwise they fall back to built-in defaults where noted.
GET/api/public/providers
Auth: None
Returns Provider[]. Mongo _id is stripped from each object.
Query:locale — optional en | hu | es | it | he | ar (overlays locales[locale] on name, descriptions, and slug). Public venue links should use locales.en.slug when set, otherwise the canonical slug algorithm in getCanonicalVenueSlug() — not raw internal ids in marketing URLs.
503 if the database is not configured.
GET/api/public/events
Auth: None
Returns PublicNightEvent[] — timed concerts and ticketed shows (not venue listings). Each event includes venues (resolved host snapshots) and venuesResolved.
Query:locale (same as providers); upcoming=0 to include past/cancelled; borough to filter by district. Default: upcoming scheduled events only, sorted by startsAt.
Stored venueIds must reference existing prov-* ids. On ingest, venueLinks and district fields sync from the primary host.
GET/api/public/menu-items
Auth: None
Flat menu board for Eat & Drink: dishes and drinks with prices, each row linked to a host venue via venue (VenueLink).
Query:locale (optional, e.g. it — resolves item name and section titles from locales), tag (canonical menu tag), q (search name, venue, section, address, category), kind (food | drink | other), borough, categories (comma-separated), limit (max 500, default 120).
400 if tag is not a canonical tag. Empty catalog returns { items: [], providersWithMenu: 0, tourReadiness: {...} } when DB is missing.
Returns PublicMeetupGroup[]: each row includes venues and events resolved from live catalogs plus stored venueLinks / eventLinks snapshots. _id stripped.
503 if the database is not configured.
GET/api/public/locations
Auth: None
Returns a borough → neighborhoods map: Record<Borough, string[]>. If the locations collection is empty or DB is unavailable, the app falls back to static neighborhood lists from the codebase.
GET/api/public/site
Auth: None
Returns the marketing shell document for _id: "main", or merged defaults when missing. Includes currencyRates ({ hufPerEur, hufPerUsd }) used by the header currency switcher (HUF is canonical in Mongo provider/event documents).
Use INGEST_API_KEY for headless content management: read catalog and settings, bulk replace collections, patch singletons, upload images to ImgBB, and mirror everything the admin UI can change in MongoDB. Stored raster URLs in provider, meet-up, and site documents must be https:// on imgbb.com (e.g. i.ibb.co) or empty; other hosts are rejected.
Provider locales: root fields are English. Every provider upsert should include locales for hu, es, it, he, and ar (each with name, shortDescription, longDescription, slug). Also set locales.en.slug to the district-neutral canonical URL segment. Public reads accept ?locale= on providers and events. See src/lib/curator/localeIngestRules.ts and src/lib/curator/eventLocaleIngestRules.ts.
Menus (Eat & Drink): attach menu to an existing prov-* via provider + patch or upsert. Do not send menuTags or menu.venueLink. Specialist prompt: scripts/cursor-curator-menu-prompt.txt · rules: src/lib/curator/menuIngestRules.ts.
Timed events: use resource: "event" (not provider category Events). Upsert host venues first in the same operations array. Ticket tiers go in entryFees (HUF/EUR), not pricePerClass on the venue. Prompt: scripts/cursor-curator-events-prompt.txt.
GET/api/cron/curator
Auth: Bearer CRON_SECRET (Vercel Cron)
Optional automation: when CURATOR_ENABLED=true, runs Serper search → fetches an official page → OpenAI JSON → Zod validate → dedupe → Mongo provider upsert (same as ingest). Requires SERPER_API_KEY and CURATOR_OPENAI_API_KEY. Response JSON includes steps. Schedule in vercel.json.
401 if the bearer token does not match CRON_SECRET. Returns 200 with a descriptive body for skip/config errors so crons do not retry endlessly.
GET/api/ingest
Auth: Bearer INGEST_API_KEY or header X-Ingest-Key: <key>
Returns a compact JSON summary of ingest capabilities and limits (same authentication as POST /api/ingest).
POST/api/ingest/upload
Auth: Bearer INGEST_API_KEY or header X-Ingest-Key: <key>
Same behavior as POST /api/admin/upload, but for API clients: multipart/form-data with field file. Requires IMGBB_API_KEY on the server.
Success: { "url": string, "displayUrl": string }.
POST/api/ingest
Auth: Bearer INGEST_API_KEY or header X-Ingest-Key: <key>
Batch read + write operations for providers, timed events, meetup groups, site, and locations. Up to 100 operations per request. Each result may include data for successful reads or write metadata (e.g. { "replaced": 12 }, { "deletedCount": 3 }).
503 if INGEST_API_KEY is not set. 401 if the key is missing or wrong. 503 if MongoDB is unavailable.
Request: either a single operation object or { "operations": [ ... ] }.
provider + get + id → one provider or error provider not found.
meetupGroups + list / meetupGroup + get — same pattern.
site + get → SiteDoc (defaults merged if missing).
locations + list → raw Mongo rows { borough, neighborhoods }[].
events + list → NightEvent[].
event + get + id → one event or error.
Write actions
provider: upsert, patch, delete (by id). Menu patches recompute menuTags and menu.venueLink; linked events refresh host snapshots when the venue changes.
event: upsert, patch, delete. Every venueIds[] entry must exist before the event is saved. Ingest writes venueLinks and syncs district from venueIds[0]. Do not send venueLinks in payloads.
providers: upsertMany (bulk by id), replaceAll (clears collection then inserts array; max 2000 docs), deleteMany with ids: string[] (max 500 ids).
meetupGroup / meetupGroups: same as providers (including replaceAll / deleteMany).
site: patch (partial merge) or put with full document (replaces _id: "main").
locations: replace — deletes all rows, then inserts the provided array.
Response (JSON): per-operation results with optional data. HTTP 200 when every operation succeeded; 422 when any operation failed.
Malformed JSON or missing required fields (ingest, login).
401
Admin cookie missing/invalid, wrong admin password, or wrong ingest key.
422
Ingest: one or more operations failed (see results[].error).
500 / 502
Missing server env (ImgBB, admin password), or upstream API/upload errors.
503
Mongo not configured, or ingest key not configured on server.
Environment variables (server)
MONGODB_URI, optional MONGODB_DB
ADMIN_PASSWORD, optional ADMIN_SESSION_SECRET
INGEST_API_KEY — required for POST /api/ingest
Optional INGEST_BASE_URL — for local ingest scripts only (see scripts/ingest-listing-automation.cjs); not required on Vercel.
IMGBB_API_KEY — admin image upload
npm run vercel:env:push syncs Mongo, ImgBB, admin, session, ingest, optional NEXT_PUBLIC_IMG_BB_*, and optional curator keys (see scripts/sync-vercel-env.cjs). Run npm run env:generate locally to mint ingest/admin secrets into .env.local. See .env.example for the full list.