Venue Module Backend API
Related documentation: Venue Module Backend · Backend Modules · Frontend Venues · Error Contract
Status
Section titled “Status”This page documents the practical route contract for Backend-Kisum-Venues.
- It is not yet a generated OpenAPI export.
- Some routes are live and repository-backed now.
- Some routes are still registered but placeholder-only or generic stub responses.
- Import flows are command/migration based and are not browser API routes.
- This page is the source-of-truth endpoint map for both current and pending Venue routes.
Auth modes
Section titled “Auth modes”Tenant-scoped user routes
Section titled “Tenant-scoped user routes”Require:
Authorization: Bearer <JWT>x-org: <company_id>
Internal machine routes
Section titled “Internal machine routes”May require:
X-Internal-API-Key: <VENUE_INTERNAL_API_KEY>
Required direction
Section titled “Required direction”Every tenant route should fail closed with:
- JWT validation
- tenant context enforcement
- module gate for
venue - permission-aware route handling
Runtime route groups
Section titled “Runtime route groups”Health
Section titled “Health”GET /health
Purpose:
- infrastructure health probe for deployment/runtime checks
Role in the system:
- tells load balancers, containers, and operators that the Venue service process is alive
- does not carry venue business meaning
Current implemented endpoints
Section titled “Current implemented endpoints”These routes are backed by current handlers and repositories in the live runtime.
Internal company hard-delete (machine)
Section titled “Internal company hard-delete (machine)”-
GET /internal/companies/{companyId}/delete-preview- Status: live
- Primary caller: platform orchestrator (Core/Admin hard-delete job)
- Auth:
X-Internal-API-Key/X-Service-Token - Purpose: count all Venue-owned rows for a Core company UUID; blockers include sleeping venues and shared
promoter_event_idlinks
-
POST /internal/companies/{companyId}/purge- Status: live
- Body:
{ mode, force, tombstoneCompanyId }(default tombstone00000000-0000-0000-0000-000000000001) - Purpose: delete company-scoped rows; with
force=true, repoint shared promoter-linked rows to tombstone before delete. Idempotent.
Internal Base-facing venue reads
Section titled “Internal Base-facing venue reads”-
GET /internal/venues- Status: live
- Primary caller: trusted internal services such as
Backend-Kisum - Purpose: list Venue-owned venue records without direct Postgres coupling
- System role: gives other Kisum backends read-only access to venue inventory that Venue owns
- Current contract: same ranked
q/searchname matching as tenantGET /api/v1/venueswhen a search term is present
-
GET /internal/venues/{id}- Status: live
- Primary caller: trusted internal services
- Purpose: fetch one Venue-owned venue record by Kisum internal venue id
- System role: supports service-to-service reads where another backend needs authoritative venue detail
-
GET /internal/venues/{id}/inquiry- Status: live
- Primary caller: trusted internal services
- Purpose: fetch venue inquiry-oriented detail from the Venue service instead of duplicating venue lookup logic elsewhere
- System role: keeps inquiry/detail consumption inside the Venue boundary rather than copying venue state into Base/Admin databases
Internal Admin-facing venue routes
Section titled “Internal Admin-facing venue routes”-
GET /internal/admin/venues- Status: live
- Primary caller:
Backend-Kisum-Admin - Purpose: list normal managed venue records for Admin control-plane screens
- System role: gives Admin a Venue-owned runtime source of truth for venue management without direct DB reads
- Current contract: when
q/searchis set, uses the same ranked name search as Artists directory lists (clientsortByis ignored while searching)
-
GET /internal/admin/venuesandGET /internal/admin/venues/sleeping— external failover (2026-07-28)- These are the two routes the Promoters BFF proxies for its marketplace venue picker, so the failover lives here too, with the same contract as the tenant list:
dataunchanged, candidates in optionalmeta.external. - Callers should pass
search,city, andiso2. The promoter offer wizard picks country and city before the venue, and a bare venue name searched across a whole country returns noise. iso2accepts one or many countries: repeated query keys (iso2=CL&iso2=ID) and/or a comma list (iso2=CL,ID,SG).
- These are the two routes the Promoters BFF proxies for its marketplace venue picker, so the failover lives here too, with the same contract as the tenant list:
-
GET /internal/admin/venues/sleeping- Status: live
- Primary caller:
Backend-Kisum-Admin - Purpose: list imported sleeping venues that are still under Kisum holding ownership
- System role: separates takeover inventory from active tenant-managed venue inventory
-
GET /internal/admin/venues/{venueId}- Status: live
- Primary caller:
Backend-Kisum-Admin - Purpose: fetch one venue by its integer
id - System role: preserves Admin’s stable numeric venue identity for legacy/sleeping venue workflows
-
POST /internal/admin/venues- Status: live
- Primary caller:
Backend-Kisum-Admin - Purpose: create a new venue record through Venue service APIs
- System role: keeps venue creation inside the Venue domain instead of Admin writing Venue tables directly
-
PATCH /internal/admin/venues/{venueId}- Status: live
- Primary caller:
Backend-Kisum-Admin - Purpose: update one venue through its integer
id - System role: supports Admin editing against Venue truth while preserving the numeric-id operational contract
-
DELETE /internal/admin/venues/{venueId}- Status: live
- Primary caller:
Backend-Kisum-Admin - Purpose: delete/deactivate a venue through Venue-owned persistence rules
- System role: keeps destructive venue-state changes inside the Venue backend boundary
-
GET /internal/admin/venue-types- Status: live
- Primary caller:
Backend-Kisum-Admin - Purpose: list the imported
venue_typecatalog - System role: lets Admin use the same Venue-owned venue-type lookup that the import/runtime uses
-
POST /internal/admin/venues/{venueId}/takeover-approvals- Status: live
- Primary caller:
Backend-Kisum-Admin - Purpose: create a takeover approval request for a sleeping venue
- System role: keeps sleeping-venue claim workflow state inside Venue, not inside Admin
-
GET /internal/admin/venues/{venueId}/takeover-approvals- Status: live
- Primary caller:
Backend-Kisum-Admin - Purpose: list takeover approval records for one sleeping venue
- System role: lets Admin inspect approval state from Venue truth instead of reconstructing workflow state elsewhere
-
GET /internal/admin/venues/{venueId}/takeover-audit- Status: live
- Primary caller:
Backend-Kisum-Admin - Purpose: list takeover audit entries for one venue
- System role: keeps workflow auditability inside Venue so Admin can render the lifecycle without direct database reads
-
GET /internal/admin/venues/{venueId}/reconciliation- Status: live
- Primary caller:
Backend-Kisum-Admin - Purpose: fetch the latest post-activation reconciliation summary for imported venue artifacts
- System role: makes imported-record carry-over visible after sleeping-venue activation and closes the import-to-runtime handoff
-
POST /internal/admin/venues/{venueId}/activate- Status: live
- Primary caller:
Backend-Kisum-Admin - Purpose: activate a sleeping venue into active ownership/runtime state after approval
- System role: closes the import-to-takeover lifecycle inside the Venue module by transferring ownership and persisting audit/reconciliation state
Boundary rules:
- these are internal service routes, not browser routes
Backend-Kisum-Adminis the browser-facing BFF- Venue remains the runtime source of truth
- the integer
venues.idis the stable venue identity for these flows; it is a JSON number in every payload
Venues
Section titled “Venues”-
GET /api/v1/venues- Status: live
- Primary caller:
Frontend-Kisum-Venues - Purpose: list the current tenant company’s venue records
- System role: drives the venue app shell and tenant-scoped venue discovery
- Current contract: supports
page,limit,q(aliassearch),status, andcity. Whenqis set, results are ranked by name match quality (exact → prefix → whole word → contains → trigram similarity) instead ofcreated_at DESC.
-
POST /api/v1/venues- Status: live
- Primary caller:
Frontend-Kisum-Venues - Purpose: create a venue record for the current tenant
- System role: establishes the operational venue entity the tenant will manage
- Current contract: accepts operating metadata through
operatingHours,restrictions, andtechnicalSpecs
-
GET /api/v1/venues/{id}- Status: live
- Primary caller:
Frontend-Kisum-Venues - Purpose: fetch one venue by Kisum internal id
- System role: supports venue detail screens and downstream venue-scoped workflows
-
PATCH /api/v1/venues/{id}- Status: live
- Primary caller:
Frontend-Kisum-Venues - Purpose: update venue master data
- System role: lets Venue own venue profile/operational master data instead of Base/Admin or frontend-local state
- Current contract: updates the same operating metadata fields exposed on create
-
DELETE /api/v1/venues/{id}- Status: live
- Primary caller:
Frontend-Kisum-Venues - Purpose: soft-delete a venue record
- System role: keeps venue archival/deletion semantics inside the Venue backend
-
GET /api/v1/venues/sleeping- Status: live (external failover added 2026-07-28)
- Primary caller:
Frontend-Kisum-Venues - Purpose: browse/search the claimable sleeping inventory a tenant can take over
- Current contract:
q(aliassearch),country,page,limit.datais the claimable Kisum rows, each with a per-callerhasPendingRequestflag. - External failover (2026-07-28): when the search comes up short, third-party candidates are attached under an optional
meta.externalblock.datais deliberately unchanged, so callers that predate the failover keep working untouched.meta.external.status—ok|disabled|skipped|unavailable. This exists so a caller can tell “looked and found nothing” from “did not look” and “the provider is down”; without it an outage renders as an empty directory.meta.external.items— always an array, never null. Candidates carrysource,externalId,name,localName,venueType, address/coordinate fields,websiteUrl,phone,capacityHint,alreadyInKisum(+kisumVenueId).meta.external.attribution— must be displayed by the caller wheneveritemsis non-empty (OpenStreetMap data is ODbL).
- Boundary: candidates are not claimable. They stay outside
databecause they are unverified suggestions that must still go throughPOST /api/v1/venues/creation-requests+ admin approval — merging them intodatawould let a caller act on a suggestion as if it were a venue. - The upstream is only queried when internal results fall below
VENUE_EXTERNAL_DIRECTORY_MIN_INTERNAL_RESULTS(default 5) and the query is at least 3 characters; browsing with noqnever calls out. Provider failures are logged and degrade tostatus: "unavailable"— they never fail the request. - Disabled by default (
VENUE_EXTERNAL_DIRECTORY_ENABLED); when off,meta.externalis omitted entirely.
-
GET /api/v1/venues/{id}/marketplace/PATCH /api/v1/venues/{id}/marketplace- Status: live (Phase 2)
- Purpose: read / upsert the venue marketplace publish profile (publish + featured flags, headline, public description/notes, media URL refs). Toggling publish writes a
published/unpublishedaudit entry. Stored in the dedicatedvenue_marketplace_profilestable.
-
GET /api/v1/venues/{id}/audit/GET /api/v1/spaces/{id}/audit- Status: live (Phase 2)
- Purpose: edit/publish audit trail for a venue or space (from
venue_audit_log); venue/space create/update/delete record entries.
Boundary rules:
- v1 product contract is effectively
1 company = 1 venue - route design should still leave room for future multi-venue support
Spaces
Section titled “Spaces”-
GET /api/v1/spaces- Status: live
- Primary caller:
Frontend-Kisum-Venues - Purpose: list spaces for the current tenant, optionally filtered by venue
- System role: spaces are the operational scope unit used by bookings, assignments, and calendar views
- Current contract: supports
page,limit,q,status, andvenueId - Response includes structured operating profile fields when present:
capacityModels,setup,pricing,amenities,equipment,accessibilityNotes,ageRestriction,curfew,soundLimitDb,technicalNotes,media,floorplans,marketplace
-
POST /api/v1/spaces- Status: live
- Primary caller:
Frontend-Kisum-Venues - Purpose: create a new space within a venue
- System role: models the actual rentable/operational subunits of a venue
- Current contract: accepts base fields plus
capacityModels,setup,pricing,amenities,equipment,accessibilityNotes,ageRestriction,curfew,soundLimitDb,technicalNotes,media,floorplans, andmarketplace
-
GET /api/v1/spaces/{id}- Status: live
- Primary caller:
Frontend-Kisum-Venues - Purpose: fetch one space by Kisum internal id
- System role: supports detail/configuration screens for a single space
- Response includes the structured operating profile used by
/settings/spaces/[id]
-
PATCH /api/v1/spaces/{id}- Status: live
- Primary caller:
Frontend-Kisum-Venues - Purpose: update a space’s master/operational data
- System role: keeps the operational scope definition inside Venue truth
- Current contract: accepts the same structured operating profile fields as create
-
DELETE /api/v1/spaces/{id}- Status: live
- Primary caller:
Frontend-Kisum-Venues - Purpose: remove or archive a space record
- System role: allows the Venue module to own the lifecycle of rentable spaces
Boundary rules:
- spaces behave like lightweight BU-style operational scopes
- frontend filtering and manager visibility are expected to be built around spaces
Availability
Section titled “Availability”-
GET /api/v1/availability- Status: live
- Primary caller:
Frontend-Kisum-Venues - Purpose: list availability blocks for one or more spaces
- System role: powers calendar availability views and conflict evaluation inputs
-
POST /api/v1/availability/blocks- Status: live
- Primary caller:
Frontend-Kisum-Venues - Purpose: create an availability block such as a hold, maintenance window, or manual block
- System role: lets Venue operators reserve or restrict space usage before a booking exists
- Current contract:
expiresAtis supported forholdblocks;maintenanceandblackoutare valid block statuses - Phase 3: also accepts
title,blockKind(setup/load_in/show/curfew/load_out/teardown/maintenance/generic),workOrderId, and arecurrencerule (frequencydaily|weekly,interval,count) that expands into conflict-checked child blocks linked byparentBlockId
-
POST /api/v1/availability/check- Status: live (Phase 3)
- Permission:
venue.availability.view - Purpose: explain per-space availability for a
{spaceIds, startAt, endAt}window — returnsavailable+conflicts[](each withkindblock|booking,reason,refId, window). Read-only.
-
GET /api/v1/availability/expiring-holds- Status: live (Phase 3)
- Purpose: holds about to auto-release within
?withinMinutes=(default 1440). Notification dispatch itself lands in Phase 14.
-
GET /api/v1/availability/blocks/{id}/changes- Status: live (Phase 3)
- Purpose: “what changed” history for a block, from
venue_change_log.
-
GET /api/v1/calendar.ics- Status: live (Phase 3)
- Purpose: iCalendar (RFC 5545) export of the tenant’s bookings + availability blocks for external calendar subscription. The frontend exposes it via the
/api/calendar/icsroute handler so the browser can download with the session attached.
-
PATCH /api/v1/availability/blocks/{id}- Status: live
- Primary caller:
Frontend-Kisum-Venues - Purpose: update an existing availability block
- System role: lets operators correct or move blocks without delete-and-recreate churn
-
DELETE /api/v1/availability/blocks/{id}- Status: live
- Primary caller:
Frontend-Kisum-Venues - Purpose: delete an availability block
- System role: reopens the blocked timeslot for future booking/availability use
Boundary rules:
- same-space conflicts should be prevented
- simultaneous bookings across different spaces are valid
- holds, maintenance, blackout, and manual block semantics belong here
- expired hold blocks no longer participate in conflict checks
Bookings
Section titled “Bookings”-
GET /api/v1/bookings- Status: live
- Primary caller:
Frontend-Kisum-Venues - Purpose: list bookings for the tenant venue/spaces
- System role: provides the core commercial-operational booking workload for the venue app
-
POST /api/v1/bookings- Status: live
- Primary caller:
Frontend-Kisum-Venues - Purpose: create a booking tied to one or more spaces and a Market counterparty reference
- System role: establishes the primary venue commercial/operational record before contracts, deposits, and event operations
- Current contract: accepts either
marketCompanyId,marketPersonId, or both; optionally carrieseventId(existing venue event row to link) and/orpromoterEventId(canonical promoter event ref) so the booking arrives already wired to its event
-
GET /api/v1/bookings/{id}- Status: live
- Primary caller:
Frontend-Kisum-Venues - Purpose: fetch one booking by Kisum internal id
- System role: supports booking detail, follow-up workflow, and downstream contract/finance/ops screens
-
PATCH /api/v1/bookings/{id}- Status: live
- Primary caller:
Frontend-Kisum-Venues - Purpose: update booking status only (lifecycle transition)
- System role: controls confirmation-sensitive transitions, including the handoff point into Finance workflow when a booking becomes
confirmed - Current contract: on confirm,
SyncBookingEventresolves the linkedvenue_eventsrow in this order —eventId(explicit link) →promoterEventId(upsert on(company_id, promoter_event_id)) → placeholder shadow keyed onbooking_id. The resolved event id is then back-linked onto the booking row so subsequent reads exposeeventIdwithout re-syncing. - Status enum (free-text
statuscolumn, no DB CHECK):inquiry,tentative,quote,proposal_sent,counter_offer,hold_option_1..hold_option_5(five hold tiers),pending_finance,pending_contract,confirmed,rejected,no_show,completed,rescheduled,cancelled,closed. Hold tiers 3–5 occupy the space window for conflict purposes exactly like tiers 1–2. - Phase 4: every transition runs through
TransitionBooking, which enforces the approval gate (arequires_approvalbooking cannot reachconfirmeduntilapproval_status = approved), applies cancellation context when cancelling (cancellationReasonCode,cancellationFeeCents,refundPolicyin the PATCH body), and records astatus_changerow in the activity log.
-
PUT /api/v1/bookings/{id}- Status: live
- Permission:
venue.booking.manage - Primary caller:
Frontend-Kisum-Venues(booking edit sheet) - Purpose: edit the operational fields of a booking —
marketCompanyId,marketPersonId,title,spaceIds,startAt,endAt - System role: full-edit path that deliberately excludes status (owned by
PATCH), finance/event linkage, slots, and pricing so the lifecycle and handoff stay centralized. Re-runs the availability/booking conflict check (ignoring the booking’s own row). Terminal bookings (cancelled/closed) are frozen — edit returns 409.
-
GET /api/v1/bookings/{id}/counterparty- Status: live
- Permission:
venue.booking.view - Primary caller:
Frontend-Kisum-Venues(booking detail counterparty card) - Purpose: resolve the rich counterparty profile behind a booking — the local venue customer overlay (venue-private name / party type / risk / notes) merged with the canonical Market party detail (
company+ optionalperson: legal name, country, city, email, phone, website, contact, plus rawattributespassthrough) - System role: the Market lookup wiring for booking detail. Market enrichment via
MarketHTTPResolver.GetPartyByID(GET/companies/{id}//people/{id}) is best-effort — an unset Market internal config or a 404 degrades to overlay-only (marketLinked:false) and never fails the read. Market stays the source of truth for canonical party fields; the Venue overlay stays the source of truth for venue-private fields.
Booking CRM & lifecycle (Phase 4)
Section titled “Booking CRM & lifecycle (Phase 4)”All guarded by venue.booking.view (reads) / venue.booking.manage (writes); primary caller Frontend-Kisum-Venues booking detail.
GET|POST /api/v1/bookings/{id}/activity— read the CRM timeline (notes, status changes, system events, communications) / add an internal note. Status changes and other lifecycle actions auto-write activity rows attributed to the acting user.GET|POST /api/v1/bookings/{id}/attachments+DELETE …/attachments/{attachmentId}— internal document references (insurance, permit, rider, invoice, …). Reference rows only; managed S3 upload is deferred.GET|POST /api/v1/bookings/{id}/communications— inbound/outbound counterparty communication log.POST /api/v1/bookings/{id}/quote— set quote amount/currency/expiry and move to a quote-stage status (quote|proposal_sent|counter_offer).POST /api/v1/bookings/{id}/commercial— setpackageTypeand operationalriskFlags(risk flags auto-flag the approval gate).POST /api/v1/bookings/{id}/approval— flag the booking for approval (approval_status = pending).POST /api/v1/bookings/{id}/approval/decide— body{ decision: "approve" | "reject" }— record the approval decision; approval unblocksconfirmed.
Backed by migration 000023_booking_lifecycle.sql (commercial/lifecycle columns on venue_bookings + venue_booking_activity / _attachments / _communications).
Marketplace & promoter booking path (Phase 5)
Section titled “Marketplace & promoter booking path (Phase 5)”Promoter marketplace bookings arrive through the Promoters BFF (POST /api/venues/bookings) which tags them source=marketplace on the Venue POST /api/v1/bookings. The venue operator then triages them from the inbox.
GET /api/v1/bookings/inbox— operator inbox: bookings withsource=marketplace, newest first.POST /api/v1/bookings/{id}/request-info— log an operator request-for-more-info to the promoter (outbound communication + activity; no status change).POST /api/v1/bookings/{id}/propose-alternate— record a counter-proposal of alternate date/spaces and move the booking tocounter_offer.GET /api/v1/reports/marketplace-conversion?fromAt&toAt— marketplace booking funnel grouped by status (view-count instrumentation is a Promoters-side follow-up).
Backed by migration 000026_booking_source.sql (venue_bookings.source).
Boundary rules:
- bookings may include multiple spaces
- included spaces define operational visibility
- transition to
confirmedis a Finance-handoff boundary - Venue orchestrates the booking state; Finance owns the resulting finance truth
- the booking ↔ event link uses canonical promoter event identity whenever available; placeholder events are last-resort fallbacks for off-platform promoters
Customers / overlays
Section titled “Customers / overlays”-
GET /api/v1/customers/search- Status: live
- Primary caller:
Frontend-Kisum-Venues - Purpose: search canonical counterparties for booking/customer selection
- System role: lets the venue app look up shared-party records without making Venue a second master-data system
- Current contract: merges Market company/person search results with tenant-local overlay matches;
EnsureCustomer(internal ensure path) fuzzy-matches Market search before create and reuses an existing overlay when market id or normalized name matches
-
POST /api/v1/customers/{id}/overlays- Status: live
- Primary caller:
Frontend-Kisum-Venues - Purpose: create venue-specific overlay data for a selected shared party
- System role: stores local notes, risk, preferences, and operational context while Market stays canonical
- Current contract: supports overlay creation for either company-level or person-level Market references
-
PATCH /api/v1/customers/{id}/overlays/{overlayId}- Status: live (Phase 8) — previously NotImplemented; now the service has no NotImplemented endpoints
- Purpose: update an overlay (name, partyType, riskLevel, notes,
preferences,riskProfile)
-
GET|POST /api/v1/customer-overlays/{overlayId}/tasks+PATCH …/tasks/{taskId}- Status: live (Phase 8) — CRM tasks & reminders linked to a customer overlay (
venue_customer_tasks)
- Status: live (Phase 8) — CRM tasks & reminders linked to a customer overlay (
-
GET /api/v1/customer-overlays/{overlayId}/metrics- Status: live (Phase 8) — venue-side LTV / repeat-booking metrics (booking/confirmed/cancelled counts, total value, last booking). Finance-derived LTV is a follow-up.
-
GET /api/v1/customer-overlays/{overlayId}/timeline- Status: live (Phase 17) — unified timeline aggregating bookings, invoice requests, contracts (by market company), security incidents, and booking communications for the overlay’s counterparty.
Backed by migration 000027_customer_crm.sql (overlay preferences/risk_profile JSONB + venue_customer_tasks).
Boundary rules:
- Artists owns canonical party identity
- Venue stores venue-specific overlays only
Mixed live and pending target endpoints
Section titled “Mixed live and pending target endpoints”These routes matter to the module contract. Events, deals, contracts, deposits, and operations (incl. templates) are now live and repository-backed. Reports remain generic stubs pending Phase 7.
Events
Section titled “Events”Ownership boundary (read this first). Canonical event truth lives in the Promoter module, not in Venue. A venue_events row is either:
- a reference to a real promoter-owned event (
promoterEventIdset,isPlaceholder = false), OR - a placeholder for an off-platform promoter (
isPlaceholder = true,promoterEventIdnil) that can later be reconciled to a real promoter event viaPOST /api/v1/events/{id}/link-promoter, mirroring the sleeping-venue takeover pattern.
Venue stores venue-side operational state on top of that reference (title, description, eventType, status, start/end, spaceIds, payload, plus placeholder promoter context when applicable). Venue never invents promoter master data.
-
GET /api/v1/events- Status: live
- Intended purpose: list venue event references in this company
- System role: surfaces the operational event roster — booking-confirm sync, placeholder reconciliation, and explicit
POSTare the three creation paths that populate it - Filters:
status,venueId,bookingId,promoterEventId,placeholder(only|exclude),fromAt,toAt,page,limit
-
POST /api/v1/events- Status: live (strict validation)
- Intended purpose: create a venue-side event reference
- System role: lets a tenant attach a venue event for either a real promoter event or an off-platform promoter without granting Venue authorship of event identity
- Required body shape: must carry exactly one of
promoterEventId: the canonical promoter event id (real on-platform promoter), ORplaceholder = trueplusplaceholderPromoterName(and optionallyplaceholderPromoterEmail,placeholderNotes) for an off-platform promoter
- Rejects both-set, neither-set, and missing-placeholder-name with
400/422
-
GET /api/v1/events/{id}- Status: live
- Intended purpose: fetch one event record with placeholder + promoter linkage
- System role: feeds operations, deposits, and finance-status views
-
PATCH /api/v1/events/{id}- Status: live
- Intended purpose: update venue-side operational fields on an event record
- System role: keeps venue-side operational state inside Venue while never silently mutating promoter linkage
- Allowed mutations:
title,description,eventType,status,startAt,endAt,spaceIds,payload,businessUnitId,bookingId. On placeholder rows, also acceptsplaceholderPromoterName,placeholderPromoterEmail,placeholderNotes. - Never mutates
promoterEventIdorisPlaceholder— promotion happens vialink-promoter.
-
POST /api/v1/events/{id}/link-promoter- Status: live
- Intended purpose: promote a placeholder venue event to a canonical promoter-event reference once the previously off-platform promoter joins Kisum
- System role: the reconciliation hook between the venue-side placeholder and the Promoter module’s canonical event identity
- Body:
{ "promoterEventId": "..." } - Rejects non-placeholder rows and rejects
promoterEventIdvalues already linked to a different venue event in the same company
Pending phase intent:
- Promoter HTTP client (Venue Phase 3): call
GET {PROMOTERS_BASE}/internal/events/:promoterEventIdbefore persisting refs when strict validation is required. Resolver is live on Promoters (2026-05-27): acceptspublicEventIdUUID or Mongo_id; authX-Internal-API-Key= PromotersINTERNAL_API_KEY. Today Venue still trusts caller format unless the client is wired.
-
GET /api/v1/deals- Status: live
- Intended purpose: list venue-side commercial deal records
- System role: should become the read-model for venue commercial structures before contract execution
-
POST /api/v1/deals- Status: live
- Intended purpose: create a commercial deal structure
- System role: captures venue-specific pricing/commercial terms before contract generation
-
GET /api/v1/deals/{id}- Status: live
- Intended purpose: fetch one deal structure
- System role: supports review and downstream contract generation
-
PATCH /api/v1/deals/{id}- Status: live
- Intended purpose: update a deal structure
- System role: keeps venue-side commercial negotiation state inside Venue
Pending phase intent:
- commercial terms persistence
- booking-to-deal linkage
- contract-generation input modeling
Contracts
Section titled “Contracts”-
GET /api/v1/contracts- Status: live
- Intended purpose: list venue contract records
- System role: becomes the canonical read surface for venue-rental agreement workflow state
-
POST /api/v1/contracts- Status: live
- Intended purpose: create a draft contract record
- System role: starts Venue-owned contract lifecycle state
-
GET /api/v1/contracts/{id}- Status: live
- Intended purpose: fetch one contract record
- System role: supports draft review, signer assignment, and signature-status visibility
-
PATCH /api/v1/contracts/{id}- Status: live
- Intended purpose: update draft contract metadata
- System role: preserves Venue ownership of the rental-contract lifecycle
-
POST /api/v1/contracts/{id}/generate- Status: live
- Intended purpose: generate a contract document from booking/deal/template data
- System role: bridges structured venue data into a signable agreement artifact
-
POST /api/v1/contracts/{id}/approve-send- Status: live
- Intended purpose: approve a contract that is
pending_approvalso it can be sent for signature - System role: contract-specific approve-before-send gate; appends an
approved_for_sendactivity row to version history and sets status toapproved - Guard:
venue.contract.manage
-
POST /api/v1/contracts/{id}/send-for-signature- Status: live through local adapter
- Intended purpose: send a contract to Adobe Sign
- System role: initiates backend-mediated signature orchestration
-
POST /api/v1/contracts/{id}/resend- Status: live through local adapter
- Intended purpose: resend a contract for signature
- System role: supports operational recovery in the signature workflow
-
GET /api/v1/contracts/{id}/signature-status- Status: live
- Intended purpose: fetch the signature state of a contract
- System role: lets Venue surface contract execution progress to the product UI
Boundary rules:
- Venue owns contract lifecycle state
- Adobe actions are backend-mediated only
- frontend must never hold Adobe credentials
- the current runtime persists signature state locally through an adapter boundary; real Adobe upstream sync is still pending
Deposits / finance visibility
Section titled “Deposits / finance visibility”-
GET /api/v1/deposits- Status: live
- Intended purpose: list venue deposit workflow records or operational finance visibility rows
- System role: gives Venue operators a finance-linked operational view without making Venue the finance source of truth
-
POST /api/v1/deposits- Status: live
- Intended purpose: create deposit expectation or workflow metadata
- System role: tracks venue-side deposit orchestration inputs and creates the linked Finance income invoice through Finance
POST /api/incomemachinesource=venue_depositmode
-
PATCH /api/v1/deposits/{id}- Status: live
- Intended purpose: update deposit workflow metadata
- System role: lets Venue manage operational deposit progress while syncing the linked Finance income invoice through Finance
PATCH /api/income/{id}machinesource=venue_depositmode
-
GET /api/v1/events/{id}/finance-status- Status: live
- Intended purpose: show operational finance status for an event/booking
- System role: exposes Finance-derived visibility to Venue users while Finance remains the persistence truth
Boundary rules:
- Venue may expose operational finance/deposit state
- Finance owns obligations, invoices, payments, settlements, and accounting truth
- deposit invoice sync is now real through Finance
incomemachine mode, and Venue should prefer Finance status oncefinanceRecordIdexists
Operations
Section titled “Operations”Venue-side operational readiness for events. Checklists may be assigned to an event, a space, and/or a booking, may carry an explicit readinessState (not_started → in_progress → blocked → ready → complete), and may be created blank, from inline tasks, or by cloning a reusable template’s tasks at create time.
Event-day command center (Phase 9)
Section titled “Event-day command center (Phase 9)”A per-event operations document plus an incident log, guarded by venue.booking.{view,manage}.
GET /api/v1/events/{id}/ops/PUT /api/v1/events/{id}/ops— read / upsert the command-center document:runOfShow,staffing,suppliers,securityPlan,doorNotes,guestVipNotes,productionChecklist,barChecklist,cleaningChecklist,handoverNotes,closeout. Returns an empty default doc when none saved.GET|POST /api/v1/events/{id}/incidents+PATCH /api/v1/events/{id}/incidents/{incidentId}— event incident log.
Backed by migration 000028_event_operations.sql (venue_event_ops + venue_incidents).
-
GET /api/v1/operations/checklists- Status: live
- Intended purpose: list checklist instances
- System role: becomes the venue-operations execution surface after a booking is accepted
- Filters:
eventId,spaceId,bookingId,status,readiness,page,limit
-
POST /api/v1/operations/checklists- Status: live
- Intended purpose: create a checklist instance
- System role: starts operational readiness workflows owned by Venue
- Body:
namerequired; optionaleventId/spaceId/bookingIdfor assignment; optionaltemplateIdclones the template’s tasks when no inlinetasksarray is provided; optionalreadinessState,dueAt, inlinetasks[]
-
GET /api/v1/operations/checklists/{id}- Status: live
- Intended purpose: fetch one checklist with its task list and readiness state
- System role: feeds the venue-side execution view
-
PATCH /api/v1/operations/checklists/{id}- Status: live
- Intended purpose: update checklist metadata, progress, readiness
- System role: tracks how prepared the venue is to execute an event
- Task-list semantics: the task list is replaced only when the body explicitly includes a
tasksarray; partial metadata updates leave existing tasks untouched
-
GET /api/v1/operations/templates- Status: live
- Intended purpose: list reusable checklist templates for this company
- System role: lets venues encode standard prep flows once and clone them per event
-
POST /api/v1/operations/templates- Status: live
- Intended purpose: create a checklist template with reusable tasks
-
GET /api/v1/operations/templates/{id}- Status: live
- Intended purpose: fetch one template with its tasks
-
PATCH /api/v1/operations/templates/{id}- Status: live
- Intended purpose: update template metadata and replace the template task list
Integration hardening (Phase 15)
Section titled “Integration hardening (Phase 15)”Guarded by the existing venue.venue.{view,edit} keys (no Auth reseed). Backed by migration 000036_integration_hardening.sql (venue_handoff_attempts).
GET /api/v1/integration/handoffs(+?status=) — the cross-service handoff ledger (target finance/adobe/marketplace/promoter, operation, attempts, last_error, external_id) for observability of cross-service failures.POST /api/v1/integration/handoffs/{id}/retry— re-queue a failed handoff (next natural handoff re-executes it).GET /api/v1/integration/resolve-promoter-event?promoterEventId=— validate a promoter event reference;{resolved:false}whenPROMOTER_INTERNAL_*is unset (best-effort).
Engine internals: IntegrationService.EnsureHandoff is the idempotent (idempotency_key per company) + retried (Retry, 3 attempts) primitive that outbound handoffs adopt; a succeeded attempt short-circuits without re-calling. File storage policy: ValidateDocumentUpload enforces allowed content types (PDF/PNG/JPEG/WebP/SVG/ZIP/text/JSON/DOCX/XLSX) + a 50MB cap on document create.
Notes / flagged externals: POS (bar/F&B) — needs vendor + creds/contract (not started). Promoter resolver — needs PROMOTER_INTERNAL_BASE_URL/_API_KEY. Two-way calendar sync — flagged (iCal export already exists, Phase 3). Cross-repo Finance aggregation endpoints are deferred until volume outgrows Venue-side aggregation. Adoption of EnsureHandoff into the existing Finance/Adobe/marketplace handoffs is the remaining wiring step.
Automation & notifications (Phase 14)
Section titled “Automation & notifications (Phase 14)”Guarded by the new venue.automation.{view,manage} keys (Auth reseed required). Backed by migration 000035_automation.sql (venue_reminders, venue_comments, venue_notifications, venue_automation_rules).
GET|POST /api/v1/reminders(+?due=true,PATCH …/{id}status,POST …/{id}/dispatch) — reminders across kinds booking/hold/contract/deposit/maintenance/low_stock/task; channel in_app/email/sms.GET|POST /api/v1/comments— entity-attached internal comments;mentions[]fan out to the per-user notification inbox.GET /api/v1/notifications(+PATCH …/{id}/read) — in-app inbox ({items, unread}); recipient resolved from the access context user id.GET|POST /api/v1/automation-rules(+PATCH …/{id}) — rules engine (trigger: booking_confirmed/hold_created/contract_sent/deposit_due/low_stock/maintenance_due; actions: create_checklist/reserve_inventory/request_deposit/schedule_reminders/notify).ApplyTriggermaterializes schedule_reminders/notify and records intents + a notification for the cross-module actions.
Notes / flagged externals: email delivery uses the Notifier boundary — when SES_AWS_* + EMAIL_FROM are set, DI wires SESNotifier (AWS SES) with embedded HTML templates (booking_confirmed, deposit_due, hold_expiring, contract_reminder, cancellation_notice) via SendTemplate. Reminder dispatch uses templates when the rule action schedules email. Otherwise DI falls back to noopNotifier; in-app notifications + reminders work fully. SMS remains unimplemented. Deep cross-module automation (auto reserve inventory / request deposit / create checklist) is a documented follow-up.
Finance workspace — payment schedule & invoices (Phase 7)
Section titled “Finance workspace — payment schedule & invoices (Phase 7)”Guarded by the existing venue.booking.{view,manage} keys (no Auth reseed). Backed by migration 000034_finance_workspace.sql (venue_payment_schedule_items, venue_invoice_requests). Venue-side source of payment intent + status; real Finance writes stay behind a no-op handoff.
GET|POST /api/v1/bookings/{id}/payment-schedule+PATCH|DELETE …/{itemId}— payment schedule items (kind: deposit/balance/milestone/final; amount/paid cents;dueAt;status). Overdue is auto-derived fromdueAt; partial/paid derived from paid vs amount.GET|POST /api/v1/bookings/{id}/invoice-requests+PATCH …/{invoiceId}— invoice requests with discrepancy states (requested/sent/partial/paid/overdue/disputed/refunded/written_off/cancelled).GET /api/v1/bookings/{id}/finance-summary— rollup: scheduled/paid/outstanding/overdue/invoiced cents, open invoice count,hasDiscrepancy, plus live Finance income rows (financeIncomeStatuses[]) whenFinanceHTTPReportingClientis configured (matched byfinanceRecordIDor referencevenue-booking:{id}).
Notes / Finance handoffs: when FINANCE_INTERNAL_BASE_URL + FINANCE_INTERNAL_API_KEY are set, FinanceHTTPHandoff implements FinanceHandoff + InvoiceHandoff via POST /api/income on Finance (machine bearer + x-org), wrapped in IntegrationService.EnsureHandoff (idempotency ledger). Cancellation refunds post ACCRECCREDIT credit notes via ProcessCancellationRefund (POST /api/credit-notes, idempotency key venue:cancellation-refund:{bookingId}) when a booking transitions to cancelled and a paid amount exists. Policy lines resolve from the booking slot-package snapshot first, then the venue marketplace profile. Otherwise DI keeps the no-op stubs. Deposit sync remains FinanceHTTPDepositSync → POST /api/vendor/invoices. Live Xero status pull uses Finance income rows on the finance-summary enrichment path when configured.
Contracts, templates & documents (Phase 6)
Section titled “Contracts, templates & documents (Phase 6)”Guarded by the existing venue.contract.{view,manage} keys (no Auth reseed required). Backed by migration 000033_contract_documents.sql (venue_contract_templates, venue_clauses, venue_documents). Built as a dedicated DocumentService so the existing contract/Adobe orchestration is untouched.
GET|POST /api/v1/contract-templates+PATCH …/{id}— contract templates keyed byeventType,spaceId, anddealModel; reference clauses by id (clauseIds).GET|POST /api/v1/clauses+PATCH …/{id}— reusable clause library (code/title/body/category) for venue-specific terms.GET|POST /api/v1/documents+PATCH …/{id}— typed document storage (kind: insurance/permit/license/floorplan/invoice/rider/contract/other) with optionalbookingId/eventId/spaceIdlinks;?kind=filter on list.POST /api/v1/documents/upload— multipart upload to S3 (50MB cap +ValidateDocumentUploadpolicy); returns{ key, url, fileName, contentType, size }for follow-upPOST /documentsmetadata row.GET /api/v1/documents/expiring?days=N— documents withexpiresAtwithin N days (default 30), for permit/insurance expiry tracking.
Profile media uploads (venue & space galleries)
Section titled “Profile media uploads (venue & space galleries)”Guarded by venue.venue.edit OR venue.space.edit (either permission is sufficient). Reuses the same S3 client as document uploads; objects land under files/venues/{companyId}/media/{timestamp}_{filename}.
POST /api/v1/media/upload— multipart image upload only (image/jpeg,image/png,image/webp; 10MB cap viaValidateProfileMediaUpload); returns{ key, url, fileName, contentType, size }. The returnedurlis stored on venue marketplacemedia[]rows or spacemedia[]rows (first item = hero image). Saving the venue marketplace profile also syncs the firstmedia[]URL tovenues.hero_image_urlfor promoter marketplace cards.
Notes / flagged externals: Adobe Sign agreement send + status sync + signed-snapshot storage stay behind the SignatureProvider stub — requires ADOBE_* creds (client id/secret, integration key, base URL) to wire the real flow. Contract version side-by-side diff and approve-before-send are live in Frontend-Kisum-Venues (ContractVersionsPanel, approve-send route).
Team, security & permissions (Phase 12)
Section titled “Team, security & permissions (Phase 12)”Guarded by venue.team.{view,manage} (Auth reseed required). Venue team members reference Auth user ids; Auth stays identity truth. Backed by migration 000032_team_security.sql.
GET|POST /api/v1/team/members+PATCH …/{id}— venue team members with venue role + space scope (spaceIds).GET|POST /api/v1/team/shifts+DELETE …/{id}— shift scheduling; optionalhourlyRateCentsfor staffing cost rollup.GET|POST /api/v1/security/incidents+PATCH …/{id}— venue security incident log.GET /api/v1/settings/approval-routing+PATCH— per-company approval routing rules (actions:confirm_booking,send_contract,request_invoice,cancel_booking,refund; roles:gm,sales,ops,production,bar,security,finance,maintenance). Defaults apply when no row exists.GET /api/v1/settings/access-audit— sensitive-action audit log (confirm/cancel booking, finance summary view, invoice request, etc.).
Backed by migration 000038_phase17_completion.sql for approval routing + access audit; 000032 for team/shifts/incidents.
Inventory, F&B, bar & assets (Phase 11)
Section titled “Inventory, F&B, bar & assets (Phase 11)”Guarded by venue.inventory.{view,manage} (Auth reseed required). On-hand = SUM(movement.quantity) (movement type sets the sign), reserved = active reservations, available = on-hand − reserved. Backed by migration 000030_inventory.sql.
GET|POST /api/v1/inventory/items+GET|PATCH /api/v1/inventory/items/{id}— items (categories: bar/F&B/production/furniture/cleaning/security/merchandise/other; assets carryassetStatus).GET /api/v1/inventory/low-stock— items at/below reorder threshold.GET|POST /api/v1/inventory/locations— stock locations (optionally space-scoped).GET|POST /api/v1/inventory/movements— purchase/transfer/consumption/wastage/adjustment/return/checkin/checkout; filtersitemId/eventId.GET|POST /api/v1/inventory/reservations+PATCH …/{id}— event-level reservations.GET /api/v1/inventory/usage-report— consumption + cost per item (optionaleventId).
Maintenance & facilities (Phase 10)
Section titled “Maintenance & facilities (Phase 10)”Work orders + inspection templates, guarded by the new venue.maintenance.{view,manage} keys (require the Auth permission migration to be re-run). Backed by migration 000029_maintenance.sql (venue_work_orders + venue_inspection_templates).
GET|POST /api/v1/maintenance/work-orders— list (filtersstatus/spaceId/assetId/priority) / create work orders (priority, status, assignee, vendor, space/asset, due date, cost, recurrence, optional availability-block link).GET|PATCH /api/v1/maintenance/work-orders/{id}— fetch / update; moving todonestampscompleted_at.GET /api/v1/maintenance/work-orders/overdue— open work orders past due (SLA/overdue alerting).GET /api/v1/maintenance/cost-report— total cost + open/overdue/completed counts.GET|POST /api/v1/maintenance/inspection-templates+PATCH …/{id}— reusable inspection checklists.
Reports
Section titled “Reports”All four reports share the same query contract: fromAt, toAt (RFC3339; default = last 30 days), bucket (day | week | month; default month), plus optional venueId, spaceId, businessUnitId, marketCompanyId, marketPersonId.
Each Finance-derived report carries a financeLinked boolean and, when false, a financeMessage explaining that the totals reflect Venue data only.
-
GET /api/v1/reports/utilization- Status: live
- Intended purpose: report booked + blocked minutes per (bucket, space) so operators can see how much of each space’s time was used vs free
- System role: pure Venue truth — joins
venue_bookings(status in tentative/hold/pending/confirmed) andvenue_availability_blocks(excluding expired holds) againstvenue_spacesfor the space name. No Finance call.
-
GET /api/v1/reports/revenue- Status: live
- Intended purpose: merge Venue-side deposit amounts with Finance-derived invoiced + paid totals per bucket
- System role: gives Venue users a reporting view while Finance remains the settlement/invoice truth. Returns per-bucket rows + totals;
financeLinked = falsewhen the Finance HTTP client is not configured, with Venue deposit numbers still populated.
-
GET /api/v1/reports/profitability- Status: live (Finance + Venue inventory)
- Intended purpose: report revenue minus cost per bucket as
net = revenue − cost. Finance supplies income/bill amounts; Venue addsinventoryCostCentsfrom consumption/wastage stock movements (itemcost_cents × quantity). - System role: exposes derived commercial health. Rows include
inventoryCostCentseven when Finance is unlinked;financeLinked = falsewhen the Finance HTTP client is not configured (Finance columns empty, inventory cost still populated).
-
GET /api/v1/reports/customers- Status: live
- Intended purpose: group bookings by Artists party id (company or person), enriched with the most-recent overlay display name; report booking / confirmed / cancelled counts + last-booking timestamp per customer
- System role: supports relationship and booking analysis while Market remains the canonical party system. Top-level totals additionally include Finance invoiced + paid (company-wide) when the Finance client is configured.
-
GET /api/v1/reports/sales-funnel- Status: live (Phase 13) — booking pipeline counts (inquiry/quote/hold/confirmed/completed/lost) over the window. Venue-only.
-
GET /api/v1/reports/cancellations- Status: live (Phase 13) — cancelled/no-show/rejected counts + cancellation-reason-code breakdown. Venue-only.
-
GET /api/v1/reports/staffing- Status: live (Phase 17) — hours and headcount by team role over the window; optional labour cost when
venue_shifts.hourly_rate_centsis set. Query:fromAt,toAt, optionalvenueId/spaceId.
- Status: live (Phase 17) — hours and headcount by team role over the window; optional labour cost when
Frontend reports surface CSV export client-side (ReportExportButton); PDF + a combined executive dashboard are follow-ups.
Implementation notes:
- Aggregations run as ad-hoc SQL inside
internal/venue/repositories/reports_postgres.go; no materialized views (deferred until real load justifies the refresh strategy). - Finance enrichment uses
FinanceHTTPReportingClient(internal/venue/service/finance_reporting_http.go) which callsGET /api/incomeandGET /api/billswith the workspace internal Bearer +x-orgheader (same pattern as the existing deposit-sync client), and applies thefromAt/toAtwindow client-side because Finance does not expose date-range filters today. - The Finance
customerIdspace is not the same as the Artists party id space; per-row Finance enrichment of the customers report is intentionally out of scope. Finance totals appear at the report-total level only.
Data-shape rules
Section titled “Data-shape rules”Venue type linkage
Section titled “Venue type linkage”Venue rows should use:
venue_type_id
which links to:
venue_type(venue_type_id)
type_legacy_id should not remain the long-term name for this column.
Legacy venue compatibility
Section titled “Legacy venue compatibility”Venue rows should preserve:
uuid- the TEXT identifier that was the primary key before migration
000042; still unique, so references written before the swap keep resolving
- the TEXT identifier that was the primary key before migration
external_source+external_source_id- where the row came from and the source record’s own id; unique together where both are set
unique_idname-city-country, enforced unique on live rows
Geo compatibility
Section titled “Geo compatibility”Venue rows may retain:
- raw
country - raw
city - raw
country_iso2
but should prefer canonical Market ids when available:
country_idcity_id
Promoter venue intake — no ownership check (2026-07-28)
Section titled “Promoter venue intake — no ownership check (2026-07-28)”Two intake paths exist, and they answer different questions. Do not merge them.
| Path | Question it answers | Proof of ownership | Admin approval |
|---|---|---|---|
POST /api/v1/venues/creation-requests | Who owns this venue? | Required | Required |
POST /internal/venues/from-external | Where is this show happening? | No | No |
The second backs the promoter offer wizard. A promoter selecting a venue is naming a location, not claiming a business, so ownership is simply not the question — demanding proof there would stall the offer with no benefit. The row is created sleeping + claimable under the holding company, identical in shape to an imported catalogue venue, and the real operator claims it later through the existing takeover flow, which is where ownership is actually decided.
- Body is the candidate object from
meta.external.items, unchanged. - Returns
{ venue, created }. - Idempotent on
(external_source, external_source_id): two promoters picking the same OpenStreetMap record get the same venue withcreated: false. An existing row is returned untouched and never re-stamped, so a venue that has since been claimed and edited is not reset by a later external lookup. - Internal key only;
Backend-Kisum-Promotersproxies it asPOST /venues-network/marketplace/venues/from-external.
External-origin venue creation requests (2026-07-28)
Section titled “External-origin venue creation requests (2026-07-28)”VenueCreationSpec accepts optional external prefill: externalSource, externalSourceId,
externalAttribution, addressLine, postalCode, region, latitude, longitude,
websiteUrl, capacityHint. Stored in the existing requested_payload JSONB — no migration.
Ownership rules:
- Approval stamps
venues.external_source,external_source_id,external_source_attribution,external_source_synced_at(migration000040). The partial unique index on(external_source, external_source_id)is the duplicate guard: two tenants cannot both import the same external record. If that write fails, the approval fails — an approved venue must never exist without its lineage. - These columns are distinct from the legacy
source_system/source_collection/source_mongo_object_idtrio, which describes the Mongo/China sleeping-venue import. capacityHintis advisory and is never written to a venue or space capacity column. External capacity (OSMcapacity) is typically a seated or sports-configuration figure, not the concert configuration a booking decision needs.- When coordinates are present and city/region/country are blank, they are resolved by one reverse-geocode call using the same Nominatim + BigDataCloud client as the China import. Submitted values always win; a geocoder outage never blocks submission.
Nullable city rule
Section titled “Nullable city rule”city and city_id may be NULL.
The API and frontend must not assume all venues have a city.
Non-API import contract
Section titled “Non-API import contract”Slot commercial packages + settlement (2026-06-29)
Section titled “Slot commercial packages + settlement (2026-06-29)”Templates — GET|POST /api/v1/commercial-package-templates, PATCH|DELETE /api/v1/commercial-package-templates/:id (venue.space.{view,edit}).
Slot packages — GET|POST /api/v1/booking-slots/:slotId/packages, PATCH|DELETE /api/v1/booking-slot-packages/:id, POST /api/v1/booking-slots/:slotId/packages/from-template. List booking slots embeds packages[]; pass ?visibility=public to filter marketplace-safe packages (Promoters BFF sets this on read).
Booking create (slot-based) — when slotIds is present, body must include slotPackageSelections: [{ slotId, packageId }] with matching length. Marketplace source=marketplace rejects internal packages. Response includes slotPackageSelections, indicativePriceCents; totalPriceCents mirrors indicative for v1 compat.
Settlement — GET|PUT /api/v1/bookings/:id/settlement, POST /api/v1/bookings/:id/settlement/submit (venue.booking.{view,manage}). Sales inputs (v1 manual): fbGrossCents, fbNetCents, ticketGrossCents, ticketNetCents. SubmitSettlement hands off through FinanceHTTPHandoff when Finance internal creds are configured (else noop stub).
Design spec: Frontend-Kisum-Venues/docs/superpowers/specs/2026-06-29-slot-commercial-packages-design.md. Migration: 000031_slot_commercial_packages.sql.
Legacy import is handled by:
- SQL migrations
cmd/import-china-venues(readsjson/china-venues.json)
not by public HTTP routes.
China JSON import writes:
venuessleeping rows (source_system = china_json)- optional
venue_aliasesforname_en,address_zh,region_zh venue_source_snapshotswith the raw JSON payload- English
address_linefrom Nominatim + BigDataCloud reverse geocode (GCJ-02 → WGS84 first)
Imported inventory rules:
- rows land under a Kisum holding company
- rows are
sleepingandclaimable - takeover is a separate workflow phase