Skip to content

Venue Module Backend API

Related documentation: Venue Module Backend · Backend Modules · Frontend Venues · Error Contract

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.

Require:

  1. Authorization: Bearer <JWT>
  2. x-org: <company_id>

May require:

  1. X-Internal-API-Key: <VENUE_INTERNAL_API_KEY>

Every tenant route should fail closed with:

  • JWT validation
  • tenant context enforcement
  • module gate for venue
  • permission-aware route handling
  • 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

These routes are backed by current handlers and repositories in the live runtime.

  • 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_id links
  • POST /internal/companies/{companyId}/purge

    • Status: live
    • Body: { mode, force, tombstoneCompanyId } (default tombstone 00000000-0000-0000-0000-000000000001)
    • Purpose: delete company-scoped rows; with force=true, repoint shared promoter-linked rows to tombstone before delete. Idempotent.
  • 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 / search name matching as tenant GET /api/v1/venues when 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
  • 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 / search is set, uses the same ranked name search as Artists directory lists (client sortBy is ignored while searching)
  • GET /internal/admin/venues and GET /internal/admin/venues/sleepingexternal 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: data unchanged, candidates in optional meta.external.
    • Callers should pass search, city, and iso2. The promoter offer wizard picks country and city before the venue, and a bare venue name searched across a whole country returns noise.
    • iso2 accepts one or many countries: repeated query keys (iso2=CL&iso2=ID) and/or a comma list (iso2=CL,ID,SG).
  • 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_type catalog
    • 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-Admin is the browser-facing BFF
  • Venue remains the runtime source of truth
  • the integer venues.id is the stable venue identity for these flows; it is a JSON number in every payload
  • 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 (alias search), status, and city. When q is set, results are ranked by name match quality (exact → prefix → whole word → contains → trigram similarity) instead of created_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, and technicalSpecs
  • 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 (alias search), country, page, limit. data is the claimable Kisum rows, each with a per-caller hasPendingRequest flag.
    • External failover (2026-07-28): when the search comes up short, third-party candidates are attached under an optional meta.external block. data is deliberately unchanged, so callers that predate the failover keep working untouched.
      • meta.external.statusok | 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 carry source, externalId, name, localName, venueType, address/coordinate fields, websiteUrl, phone, capacityHint, alreadyInKisum (+ kisumVenueId).
      • meta.external.attributionmust be displayed by the caller whenever items is non-empty (OpenStreetMap data is ODbL).
    • Boundary: candidates are not claimable. They stay outside data because they are unverified suggestions that must still go through POST /api/v1/venues/creation-requests + admin approval — merging them into data would 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 no q never calls out. Provider failures are logged and degrade to status: "unavailable" — they never fail the request.
    • Disabled by default (VENUE_EXTERNAL_DIRECTORY_ENABLED); when off, meta.external is 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/unpublished audit entry. Stored in the dedicated venue_marketplace_profiles table.
  • 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
  • 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, and venueId
    • 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, and marketplace
  • 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
  • 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: expiresAt is supported for hold blocks; maintenance and blackout are valid block statuses
    • Phase 3: also accepts title, blockKind (setup/load_in/show/curfew/load_out/teardown/maintenance/generic), workOrderId, and a recurrence rule (frequency daily|weekly, interval, count) that expands into conflict-checked child blocks linked by parentBlockId
  • POST /api/v1/availability/check

    • Status: live (Phase 3)
    • Permission: venue.availability.view
    • Purpose: explain per-space availability for a {spaceIds, startAt, endAt} window — returns available + conflicts[] (each with kind block|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/ics route 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
  • 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 carries eventId (existing venue event row to link) and/or promoterEventId (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, SyncBookingEvent resolves the linked venue_events row in this order — eventId (explicit link) → promoterEventId (upsert on (company_id, promoter_event_id)) → placeholder shadow keyed on booking_id. The resolved event id is then back-linked onto the booking row so subsequent reads expose eventId without re-syncing.
    • Status enum (free-text status column, 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 (a requires_approval booking cannot reach confirmed until approval_status = approved), applies cancellation context when cancelling (cancellationReasonCode, cancellationFeeCents, refundPolicy in the PATCH body), and records a status_change row 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 + optional person: legal name, country, city, email, phone, website, contact, plus raw attributes passthrough)
    • 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.

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 — set packageType and operational riskFlags (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 unblocks confirmed.

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 with source=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 to counter_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 confirmed is 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
  • 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)
  • 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

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.

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 (promoterEventId set, isPlaceholder = false), OR
  • a placeholder for an off-platform promoter (isPlaceholder = true, promoterEventId nil) that can later be reconciled to a real promoter event via POST /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 POST are 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), OR
      • placeholder = true plus placeholderPromoterName (and optionally placeholderPromoterEmail, 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 accepts placeholderPromoterName, placeholderPromoterEmail, placeholderNotes.
    • Never mutates promoterEventId or isPlaceholder — promotion happens via link-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 promoterEventId values 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/:promoterEventId before persisting refs when strict validation is required. Resolver is live on Promoters (2026-05-27): accepts publicEventId UUID or Mongo _id; auth X-Internal-API-Key = Promoters INTERNAL_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
  • 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_approval so it can be sent for signature
    • System role: contract-specific approve-before-send gate; appends an approved_for_send activity row to version history and sets status to approved
    • 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
  • 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/income machine source=venue_deposit mode
  • 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} machine source=venue_deposit mode
  • 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 income machine mode, and Venue should prefer Finance status once financeRecordId exists

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_startedin_progressblockedreadycomplete), and may be created blank, from inline tasks, or by cloning a reusable template’s tasks at create time.

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: name required; optional eventId / spaceId / bookingId for assignment; optional templateId clones the template’s tasks when no inline tasks array is provided; optional readinessState, dueAt, inline tasks[]
  • 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 tasks array; 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

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} when PROMOTER_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.

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). ApplyTrigger materializes 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 from dueAt; 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[]) when FinanceHTTPReportingClient is configured (matched by financeRecordID or reference venue-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 FinanceHTTPDepositSyncPOST /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 by eventType, spaceId, and dealModel; 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 optional bookingId/eventId/spaceId links; ?kind= filter on list.
  • POST /api/v1/documents/upload — multipart upload to S3 (50MB cap + ValidateDocumentUpload policy); returns { key, url, fileName, contentType, size } for follow-up POST /documents metadata row.
  • GET /api/v1/documents/expiring?days=N — documents with expiresAt within 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 via ValidateProfileMediaUpload); returns { key, url, fileName, contentType, size }. The returned url is stored on venue marketplace media[] rows or space media[] rows (first item = hero image). Saving the venue marketplace profile also syncs the first media[] URL to venues.hero_image_url for promoter marketplace cards.

Notes / flagged externals: Adobe Sign agreement send + status sync + signed-snapshot storage stay behind the SignatureProvider stubrequires 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).

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; optional hourlyRateCents for 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.

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 carry assetStatus).
  • 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; filters itemId/eventId.
  • GET|POST /api/v1/inventory/reservations + PATCH …/{id} — event-level reservations.
  • GET /api/v1/inventory/usage-report — consumption + cost per item (optional eventId).

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 (filters status/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 to done stamps completed_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.

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) and venue_availability_blocks (excluding expired holds) against venue_spaces for 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 = false when 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 adds inventoryCostCents from consumption/wastage stock movements (item cost_cents × quantity).
    • System role: exposes derived commercial health. Rows include inventoryCostCents even when Finance is unlinked; financeLinked = false when 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_cents is set. Query: fromAt, toAt, optional venueId/spaceId.

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 calls GET /api/income and GET /api/bills with the workspace internal Bearer + x-org header (same pattern as the existing deposit-sync client), and applies the fromAt / toAt window client-side because Finance does not expose date-range filters today.
  • The Finance customerId space 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.

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.

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
  • external_source + external_source_id
    • where the row came from and the source record’s own id; unique together where both are set
  • unique_id
    • name-city-country, enforced unique on live rows

Venue rows may retain:

  • raw country
  • raw city
  • raw country_iso2

but should prefer canonical Market ids when available:

  • country_id
  • city_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.

PathQuestion it answersProof of ownershipAdmin approval
POST /api/v1/venues/creation-requestsWho owns this venue?RequiredRequired
POST /internal/venues/from-externalWhere is this show happening?NoNo

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 with created: 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-Promoters proxies it as POST /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 (migration 000040). 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_id trio, which describes the Mongo/China sleeping-venue import.
  • capacityHint is advisory and is never written to a venue or space capacity column. External capacity (OSM capacity) 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.

city and city_id may be NULL.

The API and frontend must not assume all venues have a city.

Slot commercial packages + settlement (2026-06-29)

Section titled “Slot commercial packages + settlement (2026-06-29)”

TemplatesGET|POST /api/v1/commercial-package-templates, PATCH|DELETE /api/v1/commercial-package-templates/:id (venue.space.{view,edit}).

Slot packagesGET|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.

SettlementGET|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 (reads json/china-venues.json)

not by public HTTP routes.

China JSON import writes:

  • venues sleeping rows (source_system = china_json)
  • optional venue_aliases for name_en, address_zh, region_zh
  • venue_source_snapshots with the raw JSON payload
  • English address_line from Nominatim + BigDataCloud reverse geocode (GCJ-02 → WGS84 first)

Imported inventory rules:

  • rows land under a Kisum holding company
  • rows are sleeping and claimable
  • takeover is a separate workflow phase