Release 1.72.0
Important Release Documents
Release Notes - Seatmap Platform
Version 1.72.0 - 2026-08-28
Release Focus: A release built around orphan seat prevention. A seat map can now be configured to stop a selection leaving a single seat with no available neighbour: the renderer shows a buyer which seats have to be taken together before they click, adjusts a selection that would strand one, and the booking endpoints refuse the same seats so what the buyer sees and what checkout enforces stay in step. The setting is managed from the editor, per organisation and per event, and takes effect within seconds. Alongside it, the admin renderer gains category and mixed-selection tools, section outlines finally honour their zoom settings, the schema editor fixes a run of configuration and background defects, and the Java services move to Spring Boot 4.1 on Java 25.
MetaTitle: Release 1.72.0: Orphan Seat Prevention - Seatmap.pro
MetaDescription: Seatmap 1.72.0 adds orphan seat prevention across the booking renderer and API, new category and selection tools, and moves the services to Spring Boot 4.1.
New in the booking renderer
Orphan seat prevention (SEAT-1196)
Seat maps can now be configured to prevent a selection from leaving a single unsold seat with no available neighbour.
Before any click, a dashed outline groups seats that can only be taken together, so a buyer can see which choices need a companion seat. Hovering a seat extends the outline over whatever else that seat would commit them to. The outlines follow the cart as the selection changes, and are restyled through theme.orphanGroup. Outlined seats stay selectable.
When a selection is turned down, the renderer reports it through the new onOrphanSeatsBlocked callback with the seats that would be stranded and a suggested adjustment. Every seat an accepted adjustment adds or releases goes through the regular onSeatSelect and onSeatDeselect callbacks, so per-seat integrations stay in sync; seat maps configured with a fixed group size keep reporting whole groups instead. When a deselection needs companions released, the adjustment releases as few seats as possible and keeps the remaining selection in the largest groups the row allows.
Seats separated by an aisle are not treated as neighbours. A row is measured against its own seat spacing, so a wider gap in the middle of a row ends the block: a seat beside an aisle is never held back by a seat on the far side of it, and an outline never spans the gap.
Releasing seats is never restricted, and a buyer can always return the map to the state it was in before their selection: releasing a seat that was already standing alone when they arrived is never blocked.
The option is off unless enabled for an organisation. Integrators enabling it should implement onOrphanSeatsBlocked and apply the suggested adjustment; without the callback every blocked selection is simply refused.
Orphan prevention on the programmatic cart methods (SEAT-1200)
The renderer methods that change the cart from integrator code — initCart, addSeatsToCart, removeSeatsFromCartByIds and removeSeatsFromCartByKeys — now report orphan prevention through a return value. Buyer clicks are still guided by onOrphanSeatsBlocked; these methods are synchronous and cannot wait for an answer, so they return ICartChangeResult with the seats the change would strand and the adjustment that would make it acceptable.
addSeatsToCart adds nothing when the batch would strand a seat, so a promotional bundle or a deep link cannot fill a cart that checkout will refuse; the result carries the seats to add alongside it if the integrator wants to extend the batch. Restoring a cart and releasing seats always go through and only report, because refusing either would take away seats the buyer already had. Read applied for whether the change reached the cart and orphaned for whether it strands a seat — a restore or a release reports both at once.
With orphan prevention off, all four methods report nothing and behave exactly as before, and existing integrations that ignore the return value are unaffected.
Reset seat and general admission categories independently (SEAT-1091)
The admin renderer gains clearSeatsCategory(seats) and clearGaCategory(sections) to remove the category from specific seats or general admission sections, and resetSeatsCategories() and resetGaCategories() to reset one side of the map without the other. resetCategories() is unchanged and remains the only call that also clears the shared category colour palette.
Clearing a seat category now removes just the category and leaves the rest of the seat’s data in place. The onboarding guide previously suggested setSeatsCategory(seats, -1) for this; use clearSeatsCategory(seats) instead.
Assign categories to several sections at once, and by name (SEAT-1092)
setGaCategory now takes a list of sections — objects, IDs, or section names — plus an optional colour, matching setSeatsCategory. Both setters also accept a name instead of a number as the category, so categories can be addressed as 'vip' rather than an index. A named category needs a colour, since only numeric categories fall back to the theme palette.
Two behaviour changes to note: setGaCategory(sections, 0) now assigns category 0 rather than clearing it — pass undefined to clear — and calling setSeatsCategory without a colour no longer discards the colour already registered for that category.
Select seats and general admission sections in one drag (SEAT-1094)
The admin renderer gains a selectMixed mode: renderer.setMode('selectMixed'). One drag selects seats and general admission sections together, reporting seats through onSeatsSelectionChange and onSeatsSelect and sections through onSectionsSelectionChange, exactly as the seat-only and section-only marquees already do. Seated sections are left out of the section results, so their seats are not also reported as a section selection.
select, selectRows and selectSections are unchanged.
New in the schema editor
Orphan seat prevention is configurable from the editor (SEAT-1199)
Orphan seat prevention can now be turned on and off from the editor, without support involvement. An organisation administrator sets the organisation-wide value under Settings > Organization, and a single event can override it from the event dialog.
Both controls offer three choices: Enabled, Disabled, and Inherit. Inherit removes the setting at that level so the value keeps following the level above it, and the option names the value it is currently inheriting, so it is always clear what Inherit resolves to. An event left on Inherit follows its organisation; an organisation left on Inherit follows the platform default, which is off.
A change is picked up by the booking endpoints and by newly loaded seat maps within seconds. A booking map that is already open keeps whatever value it loaded with until it is reloaded.
New for self-hosted deployments
Plain-http webhook targets for internal receivers (SEAT-1136)
Self-hosted deployments can now deliver webhooks to an internal receiver that is only reachable over plain http, by pairing the new operator-level setting seatmap.webhooks.allow-insecure-targets with seatmap.webhooks.allow-private-targets. An http target must resolve entirely to internal addresses; https targets and every other target check are unchanged, and the default is off.
Setting allow-insecure-targets without allow-private-targets stops the service at startup.
New in the sign-in API
One-time session code for SSO autologin (SEAT-1212)
The SSO autologin endpoint can now return a single-use session code instead of session tokens: set responseType to "code" in the autologin request and open the Editor with ?ssoCode={code} in the iframe URL. The Editor exchanges the code for a session automatically when it loads. A code is valid for a single exchange and expires after 60 seconds; request a fresh code each time you open the Editor. The existing token response remains the default and continues to work unchanged.
Changed behaviour
Orphan prevention configuration applies within seconds (SEAT-1203)
Turning orphan seat prevention on or off now takes effect within seconds instead of within an hour. The booking endpoints and every newly loaded seat map pick the new value up together, so what a buyer is shown and what checkout enforces stay in step. A booking map that is already open still keeps whatever value it loaded with until it is reloaded.
The setting is carried on the event price payload alone. It is no longer repeated in the seat map payload, which is cached for much longer and could therefore state a value the booking endpoints had already stopped applying.
Changed: the renderer now treats a price payload that carries no orphan-prevention value as on rather than off. A booking-service of this release states the value whenever it can determine it, so an integration whose renderer and backend are upgraded together sees no change. An integration that upgrades the renderer package on its own, against a booking-service from an earlier release that sends no value at all, will start applying orphan prevention to every event. To opt out entirely, set orphanPrevention: false in the renderer settings, which overrides the payload, or upgrade the booking-service alongside the renderer so that it states the value.
Changed: a seat selection that orphan prevention turns down is now completed with the adjustment the rule suggests, rather than refused. A buyer who picks a seat that would leave its neighbour on its own now gets both seats, and the cart callbacks report both. Removing a seat is not adjusted this way: a removal that would strand a seat is still turned down, rather than releasing further seats the buyer had already chosen, and so is a click under a group size above one, where the adjustment would push the selection past the group. A selection the rule cannot repair is refused as before. To decide any of this yourself, implement onOrphanSeatsBlocked: return false to turn the selection down and explain it in your own interface, or true to accept the adjustment on any of these paths. The programmatic cart methods are unchanged: they report the verdict and never adjust a selection themselves.
In section mode the renderer now measures the venue seat pitch on every load unless orphanPrevention is explicitly set to false in the renderer settings. Previously it decided from the seat map payload, and a stale value there could leave the measurement undone for the life of the session.
The event preview in the editor, and the standalone widget page, now say when orphan prevention adjusts a selection and when it turns one down, instead of leaving the change to happen without explanation.
Organisation and tenant API tokens are scoped to administrators (SEAT-1212)
Organization and tenant API tokens are provided to organisation administrators. Members without an administrator role in an organisation do not receive those values in sign-in responses, and the Organization token and Tenant token fields are hidden from them in Organization settings. Retrieve an organisation’s API token as an administrator.
What’s fixed in the booking renderer
General admission selection is cleared again (SEAT-1089)
Clicking empty space now clears a selected general admission sector or section, and reports the emptied selection through onSectionsSelectionChange. Clicking a second sector deselects the first, so the highlighted sector always matches the reported selection. Multi-select in selectSections mode still accumulates as before, and the cart is left alone — clearing a selection never removes a general admission entry from it.
Deselecting a sector no longer highlights every other section on the map.
Section states on rectangular general admission sectors and tables (SEAT-1093)
Selected, disabled and filtered states now render on sections drawn as plain rectangles, including general admission sectors and rectangular tables. The state is written to the outline element the renderer stylesheet targets, so setSectionSelection, disableSections and filterSections show on those shapes the same way they already did on circular, elliptical and polygonal ones. Section geometry — hit areas, zoomToSection targets and centroid labels — is unchanged.
A map that previously showed nothing for these calls on rectangular general admission sectors will now show the state. If a layout relied on that absence, set the state only on the sections that should display it.
Seat colours after locking or unlocking seats (SEAT-1122)
Locking, unlocking, filtering and unfiltering seats through the renderer API now repaint every layer, so seats take their new appearance immediately instead of only after the next zoom or pan. disableSectionsByIds and enableSectionsByIds repaint the same way and now also refresh the section metadata getSections reports.
These calls now go through the full renderer redraw, so onRedrawStart and onRedrawEnd fire for them as they already do for setSeatsState and setSeatsCategory.
Section outline visibility follows the zoom settings (SEAT-1201)
Section outlines now respect the zoom ranges configured for them. visibilitySettings.outlines.visible decides whether outlines are painted at the current scale, and visibilitySettings.outlines.selectable decides whether they respond to the pointer: outside the selectable range an outline no longer highlights under the cursor, and the click reaches the seats underneath instead. Previously outlines stayed painted and kept reacting to hover at every zoom level, which showed buyers a highlight on a section they could no longer click.
This applies with the shipped defaults, which show section outlines below scale 0.66 and hand over to individual seats above it. Integrators who want outlines at every zoom level should set visibilitySettings.outlines.visible to { from: 0, to: 20 }.
outlineVisibility now takes effect as documented. Each outline source — svg, shape, auto and fallback — can be set to always, eagle-only to show it while the whole venue is in frame, or hidden to leave it out entirely, so a venue can keep its background-bound sections for navigation while suppressing generated outlines. disableOutlinesInHelicopterView works again for the background-bound sections it has always covered, and is now a deprecated alias for outlineVisibility: { svg: 'eagle-only' }, which covers every source.
The zoom level that marks the eagle view is now set through eagleViewMaxZoom (default 0.5; 0.66 in the admin renderer). It carries what the value has always controlled: the scale below which the whole venue is in frame and outlines carry the navigation. The former name, seatSelectionMinZoom, did not gate seat selection — visibilitySettings.seats.selectable does — and stays available as a deprecated alias, honoured whenever eagleViewMaxZoom is absent, so existing configurations keep working unchanged.
Seat state overlays stay aligned after a container resize (SEAT-1073)
Resizing the renderer container — rotating a phone, collapsing a sidebar, or resizing the window — now repaints every layer, including the custom seat state overlay. State badges stay attached to their seats through the resize instead of keeping the pre-resize position until the next zoom or pan.
The outline around a protected seat group is visible again (SEAT-1211)
The dashed outline that marks a group of seats orphan prevention is protecting now appears once the map is zoomed in far enough to pick seats. It previously shared a container with the section outlines, which the renderer puts away at that zoom, so it was never drawn where a buyer could act on it.
The outline is no longer drawn across the whole venue. At that distance a group of two seats covers a few pixels and reads as a smudge rather than a boundary, so it is left out until the map is close enough for it to mean something.
What’s fixed in the booking API
Concurrent seat state changes answer 409 (SEAT-1197)
A concurrent modification of the same seats or general-admission group now answers with 409 Conflict instead of a generic server error. Write endpoints in the v2 booking and management APIs declare 409 in the OpenAPI document, so integrations can branch on it explicitly; the published error-format reference now shows the Problem Details body the API actually returns, including the errorCode and errors members.
What’s fixed in the schema editor
Organization and tenant configuration updates (SEAT-1217)
Organization and tenant updates now replace the stored configuration only when the request body carries a configuration object. A body that omits the configuration, or sends a null in its place, leaves the stored configuration unchanged. A configuration sent as anything other than a JSON object is now rejected with HTTP 400 instead of being accepted silently. Creating an organization now stores the configuration supplied with the request, which previously was not persisted. This applies to the organization update endpoint and to the admin organization and tenant create and update endpoints.
Schema Save for organizations whose configuration carries no settings (SEAT-1218)
Saving a schema now succeeds for organizations and tenants whose stored configuration carries no settings. Such a configuration is treated the same as no configuration at all, so no delete prohibition is applied. Organizations and tenants with a real configuration keep their existing prohibitDeleteSeats and prohibitDeleteSections behaviour.
Deleting a section whose seats carry price assignments (SEAT-1222)
Removing a section whose seats carry price assignments now returns the same message and list of affected seats that removing those seats individually already returned, so the save no longer ends in an error the editor cannot explain. Repeating the delete with force enabled completes and releases the assignments for the seats it removes.
Organisations that prohibit deleting seats keep that protection when the seats are removed as part of a section rather than one by one. When that protection blocks a delete, the reported list now names only the seats that are actually blocked rather than every seat carrying an assignment.
Extracting seats from a curved section keeps them in place (SEAT-1143)
Extracting selected seats into a new section keeps every seat exactly where it was drawn, the same way multi-section seat extraction already did. Seats extracted from a section with curve or stretch transformations previously snapped to their untransformed positions the moment they were extracted.
Venue city lookup requests a valid language (SEAT-1180)
When a venue is created or updated without an explicit language, the city behind its coordinates is now resolved in English. The lookup previously derived the language from the venue’s country, which produced city names in the wrong language for some countries and failed for others. City names for venues saved after the upgrade therefore come back in English; a venue may be listed under an English city name where it previously used a localized one.
Background artwork no longer opens a schema with unsaved changes (SEAT-1219)
Opening a schema whose background artwork carries a stored scale no longer marks the schema as edited: the Save button stays disabled and the unsaved-changes indicator stays clear until you actually change something. The background’s scale is now derived from the width the artwork occupies rather than kept as a second, separately rounded copy of the same measurement, so the editor and the stored schema always agree on the artwork’s size.
Backgrounds saved as a single SVG keep the size described by their stored transform, and backgrounds stored without an explicit size now open at their artwork’s own dimensions instead of collapsing to zero.
What’s fixed across the applications
A new release takes effect on the next page load (SEAT-1214)
The hosted renderer script and the editor application are served under fixed URLs, so browsers were free to keep an earlier copy and decide for themselves when to check for a newer one. A page embedding the renderer could keep running the previous build for a few hours after a release, and the same applied to the seatmap widget and to the editor’s Preview window. In the editor the effect reached further, because the entry document names the files the rest of the application loads, so a returning user could stay on the previous build for the better part of a day.
Both are now revalidated on each load, so a release reaches a page as soon as it is reloaded. Revalidation returns an unchanged response when the build has not moved, so this does not add a download to the common case. The editor’s versioned asset files, whose names already change with every build, are now cached for a year instead of not at all.
Integrators who put their own cache in front of our host should confirm it honours the revalidation, otherwise it will continue to hold the older build.
Performance
Faster selection and editing in large venues (SEAT-1171)
Editor state management was modernized: server data — settings, schemas, library, venues — now flows through a caching query layer, undo eligibility is declared per action instead of maintained in a central exclusion list, and the hot selection and deletion paths in large venues no longer perform quadratic scans.
Selecting, deleting, and marquee-selecting seats in very large schemas is noticeably faster, and switching organizations now reliably refreshes all cached lists.
Platform
Spring Boot 4.1 (SEAT-1192)
The Java services move to Spring Boot 4.1, which brings Spring Framework 7, Spring Security 7, Spring Data 4, Hibernate 7.4, Flyway 12 and Jackson 3. The upgrade picks up published upstream releases across the whole managed dependency set, and the service images run on Java 25.
Behaviour is unchanged: request and response payloads, API contracts, configuration keys and cache keys are the same as before. JSON parsing keeps accepting the same payloads it accepted previously, so no integrator needs to change what they send.
The published OpenAPI description gains the validation constraints already declared on the models — minimum lengths and numeric bounds — and fields carrying free-form JSON are now described as free-form objects. Endpoints, parameters and payload shapes are unchanged; integrators who generate clients from the specification will see these refinements in the generated models.
Affected products
- booking-client
- booking-service
- editor-client
- editor-service
- common-lib
- db-migrations
- deployment
Upgrade
Apart from the changes called out under Changed behaviour, this release is backwards compatible: no existing endpoint, renderer method or configuration value changes meaning.
Orphan seat prevention is off unless it is enabled for an organisation. Nothing about it applies to a deployment that leaves it alone.
Sign-in responses provide organisation and tenant API tokens to organisation administrators only. An integration that reads organizationToken or tenantToken from a non-administrator member’s sign-in response must obtain the value as an organisation administrator instead.
One database migration runs on startup. It clears organization and tenant configurations that are not JSON objects, so they read as no configuration. It creates and alters nothing, takes no long-lived lock, and needs no operator action. Deployments whose migration role is restricted per table must confirm it holds UPDATE on organization and tenant before upgrading.
Operators who maintain their own ingress definitions instead of using the chart’s booking.ingress.apiPaths must add /custom to the paths routed to booking-service, or the API explorer and its assets will not be reachable.
The client images now send revalidation headers on their entry documents. Operators running a CDN or caching proxy in front of editor-client or booking-client should confirm it forwards the revalidation, otherwise it will keep serving the previous build after an upgrade.
Building from source now requires JDK 25. Operators consuming the published images are unaffected.
Full details in the Deployment Guide.
Booking renderer
npm install @seatmap.pro/renderer@1.72.0
The buyer-facing guidance for orphan seat prevention needs renderer 1.72.0 or newer, so integrators embedding an older build should upgrade it before the option is enabled for their organisation.
New renderer surface in this release: the onOrphanSeatsBlocked callback and the orphanPrevention setting; ICartChangeResult, returned by initCart, addSeatsToCart, removeSeatsFromCartByIds and removeSeatsFromCartByKeys; theme.orphanGroup; eagleViewMaxZoom; clearSeatsCategory, clearGaCategory, resetSeatsCategories and resetGaCategories; the list form of setGaCategory; and the admin renderer’s selectMixed mode.
Deprecated, still honoured: seatSelectionMinZoom (use eagleViewMaxZoom), disableOutlinesInHelicopterView (use outlineVisibility), and setSeatsCategory(seats, -1) for clearing a category (use clearSeatsCategory(seats)).
Two calls change meaning: setGaCategory(sections, 0) now assigns category 0 rather than clearing it — pass undefined to clear — and setSeatsCategory called without a colour no longer discards the colour already registered for that category.
Additional Documentation
- Renderer API Changes — orphan prevention, the category calls,
selectMixed, and the outline visibility settings - Backend API Changes — orphan prevention on the lock and sale endpoints,
cartSeatIds, the 409 declaration, and the autologin session code - Deployment Guide