Operator
Language EN TR NL

Thirteen surfaces, one set of rows.

The landing page shows five of these as tabs on one sheet, which is the argument rather than the layout: the door list, the baskets, the settlement and the reports are the same rows counted different ways, so they cannot disagree. Here is what each surface does, and, where it matters, where it stops.

It also sells things in boxes. An event, a menu and a shop run on the same tables, because the difference between a ticket and a shipment turns out to be almost entirely presentation: one asks an age at the door, one asks for an address, and one leads with a photograph. Everything below is true of all three unless it says otherwise.

Everything below is on Hibilet Instant Seat maps and whitelabel tenancy are on Pro

Events and tiers

An event is a catalogue with a basket behind it. Tiers are products in that catalogue with their own price, release size and on-sale window, which is why an early bird selling out needs no different kind of object from a table costing two thousand euros.

Three states, not a checkbox. Off means the link answers 404. Draft means anybody with the link can look and nobody can buy, because a page has to be looked at before it is published, and looking at a page means loading it. Live means selling. Refusal is doubled up: a hand-written request to buy from a draft gets the same answer the widget does.

Timed releases per product, with a coming soon state that is listed publicly and refused at the till Opening hours as a weekly timetable in a named timezone, so 23:00 is still 23:00 on both sides of a clock change A selling cutoff that defaults to two hours before the doors, filled in wherever nobody chose one Your own questions at checkout, per sale. Required or not, and only where they make sense
One night, five releases, capacity 1,000
TierReleasedSoldPriceStatus
Early bird300300 / 300€25.00Sold out
Advance400392 / 400€32.00On sale
Final release200186 / 200€38.0014 left
Late release100€44.00Opens 20:00
Guest list6048 / 60€0.00No fee

Extras, sizes and tables

"Large", "no bun", "extra cheese", "+1 drink with the ticket" are four names for one shape: a small decision about a single line, which may move the price and which the kitchen or the door has to be told about. So a choice belongs to the line, not to the product. Two burgers in one basket, one with cheese and one without, are two different things at two different prices.

A menu repeats itself, so a group of choices belongs to the sale and is attached to as many products as want it. Retyping "choose a size" per product is how a menu ends up with three different prices for a large.

Signed prices, the only place in the system where an amount may be negative, because "no bun" is worth less than the burger it comes out of Optional stock per option, so "+1 drink, 200 available" is an extra rather than a second ticket type to reconcile Folded into the line price at the moment the hold is placed, so the discount split, the tax, the receipt and the door all keep working untouched "From €34" wherever a required choice can only make the item dearer. A lamb that cannot be ordered without a €2 choice is not a €34 lamb
One sale's choices, attached to whichever products want them
GroupReads asDrawn asStock
Table sizePick exactly oneRadio
CloakroomUp to oneRadio, clearable400
Add-onsAs many as you likeCheckboxes
Welcome drinksPick twoCheckboxes200

The checkout

Everything else in this system is ordinary create-read-update-delete. Checkout is the one compound operation, and it either happens completely or not at all: the basket is locked, an expired hold is refused, the terms are read, stock is taken, the coupon is redeemed, tax is worked out on the post-discount share, the order code is minted and the ledger rows are posted, all in a single transaction.

A client may propose a quantity. It may never propose an amount. Prices, costs and tax rates are copied onto the line at the moment it is held, so a price change tomorrow cannot rewrite what somebody agreed to today.

Overselling is impossible rather than unlikely, because stock comes down under a condition, so two people buying the last ticket resolve correctly instead of both succeeding The terms are read inside the transaction, so an order cannot be sold under a document that moved a moment earlier, and a refusal names the document rather than a code The failure survives the rollback. Three declined attempts followed by one that worked is three rows and a finding, not one silent success The receipt is queued, not awaited, so a slow mail server is not ten seconds of spinner after the money has been taken
One basket's own timeline, oldest first, the one list here that is not newest-first
TimeWhat happenedIn itWorth
10:54:40opened€0.00
10:54:41item added, 3 × Advance3€179.70
10:55:28payment started3€179.70
10:56:04payment failed, card declined3€179.70
10:56:26— next basket —
10:56:32bought2€119.80

Tickets and the door

There is no ticket table. A line with a quantity of four is four tickets, and each of them gets a code and a square of its own, so a party can forward one each and arrive separately, and each forwarded mail names the chair its holder sits in.

The last characters of a ticket code are a signature, not a checksum. Without them the numbering makes every ticket on an order derivable from any one of them: a photographed square would hand somebody the rest of the party's codes, and the first of them to reach the door would be let in on another person's ticket.

A scan repeated is one ticket both times. What is admitted is a set of numbers, not a count. A count cannot say which, so one forwarded square would admit a whole party Half a party inside is still outstanding. A line settles only once its last ticket is in, because the door is still expecting the other three No order list on the door screen. The code is the authorisation; a browsable list of everyone's orders on a counter device is a different product with a different risk Refunding at the door is off unless you turn it on, per sale. Moving money should be granted deliberately, not held by everyone who ever worked a door

One order, four tickets, three arrivals

Order

K7M2-9PQR

Maassilo, Rotterdam · Friday 14 March, doors 20.00 · 4 × Advance

  1. 1K7M2-9PQR-1H4TC3In 20.14
  2. 2K7M2-9PQR-2Q9MC4In 20.14
  3. 3K7M2-9PQR-3H4TC5In 21.06
  4. 4K7M2-9PQR-4X2PC6Expected

The order code heads the mail and is the only reference anybody quotes. The highlighted three characters are the signature, and they are what stops a photographed square from handing over the rest of the party.

The confirmation, and the code on it

There is no separate ticket mail. The confirmation is what somebody holds up in a doorway, so it is ordered by what a person standing in a queue needs first: the code and its square, then when and where, then what they bought and what it cost. A receipt is read top to bottom once. A ticket is opened again under a torch with somebody waiting behind you.

The code is eight characters from an alphabet with the confusable ones taken out. No zero against O, no one against I or L, because this gets read aloud across a counter and typed by somebody standing up. Hyphens and case are stripped on the way in, so a sloppily typed code still finds the order.

The square encodes the code, and nothing else. Not a URL, not an internal id, not a signed token. The reader at the door is a keyboard scanner: it types what it reads into the same box a person types into, and anything cleverer arrives as something the lookup has never heard of It rides as an attached image, not as an embedded one. A data URI or an SVG is a blank rectangle in Outlook and in Gmail's web client, and a code that will not render is worse than a mail with no square at all, which is why the code is printed as text beside it either way A refunded order gets no square. It becomes a record rather than a ticket: the code is struck through, still legible because it is still the thing to quote, and visibly not a thing that opens a door. A partly refunded order keeps its square, because the rest of it is still admitted Resent, it is rebuilt rather than replayed. A stored copy drifts from the order it claims to describe, so "it never arrived" produces the same mail as the first one, including any refund since

The thirty an order code may use

23456789AB CDEFGHJKMN PQRSTVWXYZ

The five that are deliberately missing

0O1IL

Those five are the whole point. Zero and O are the same character to somebody reading a phone screen out to a colleague on a door. One, I and L are the same character to somebody typing it. Dropping them costs a little entropy and removes an entire category of support call.

Baskets, counted as journeys

A basket is a session, and a session is not a story. Somebody who fills a basket, wanders off and comes back two days later to buy is, as three unrelated rows, two abandonments and a sale: three numbers, all of them misleading. Chained together it is one customer who took three visits to decide, which is a completely different fact about the business.

An abandonment later paid for is not a loss, and somebody who put the same two tickets down twice cost you one sale rather than two. So what was lost is the largest attempt across the whole story, never the sum of the tries. Otherwise the most persistent shoppers read as the most expensive ones.

An open basket is never counted as abandoned. Nothing has been put down yet, and counting it makes every live shopper look like a loss Which attempt bought. "Converted on 3" means two abandonments were not two lost customers Two clocks, because they answer different questions: how long they stayed inside a basket, and how long after the last attempt this one started. Four attempts four minutes apart is not four attempts four months apart Winning one back is a coupon restricted to that person, not a code that works for whoever types it first, which is precisely the wrong property when you have just emailed it to one person

Where 1,867 baskets stopped, and what each step cost

Opened a basket

1,867

Held stock · 263 never added a line

1,604

Reached payment · 424 stopped here, holding €6,180

1,180

Bought

1,102

The widest drop is the only row worth an argument, so it is the only one drawn in the brand colour. Counts and money arrive together on purpose: "you lost 22.7% of baskets between the cart and the card" is a statistic, and "holding €6,180" is a decision. Every bar is measured against the top of the funnel rather than against the row above it, so what you see is the shape of the loss instead of four rows that always look the same.

Getting last year in

A dropped spreadsheet, read by a model, applied by a human. Nothing is created until somebody presses Approve, and what Approve creates is an inactive sale, so there is a second deliberate step before anything is on sale. Two decisions stand between a messy export and a live event, and both of them are yours.

Any value the model could not point at arrives blank and marked, never filled in with something plausible. Every proposed value shows the source cell beside it. A price nobody can source is a refund waiting to happen, so the screen would rather ask than guess.

We have eaten 133,800 baskets out of somebody else's system and reconciled seventeen months of them to €0.00 in two currencies, after two earlier attempts that looked obviously right and were short by €4,837 What could not be honestly reconstructed is recorded as missing rather than left to be noticed. Nothing in the old data said when an abandoned basket actually died, so it does not claim to know The history comes with it. Somebody who bought three times over a year before the migration is one customer with three purchases afterwards, not three strangers Send an export before you commit to anything. You get back your repeat buyers, what was recoverable and what it was worth, and how many of those people you may lawfully contact. It is built on your numbers, not a demo set
A review screen, before anybody presses Approve
FieldProposedRead from
NameKollektiv NachtSheet 1, B4
Doors14 Mar, 20:00Sheet 1, C4
Capacity1,000Sheet 1, E4
Advance priceBlank, askNothing said a price
VenueMaassiloMatched in the directory
What it createsInactive saleA second step before selling

Money

The charge is created on your own account with your own payment provider, the money lands in your balance, and our fee comes out of it. Your business is the name on the buyer's statement, which is what somebody redirected to "the merchant's payment page" expects to see. We hold one secret key and it is the platform's; there is no credential of yours anywhere in this system.

Coming back from the payment page is not proof of payment. That address is a plain link anybody can type, so nothing is settled on arrival. It also covers the shopper who pays and closes the tab: they still get their order and their receipt, because none of it depended on the browser coming back.

The hold survives the redirect. Three minutes is not enough to type a card number into an unfamiliar page on a phone in a queue, so opening a payment pushes the clock out to the payment window rather than adding a second clock to keep in step Paid, and the order cannot be given, where the last ticket went while they were paying, is refunded in full and recorded, rather than hoped away A refund is a new transaction, never an edit. The money moved twice, and an audit trail that says otherwise is not one. Each line comes back at its own tax rate, less its share of the discount The record that money is owed back and the work that returns it both land, or neither does. The reversal is queued inside the same transaction that writes the refund, because a crash in the gap between them is a shopper who was told they were refunded and never was Cancelling a sale stops selling first, in its own step, so no order can be taken after the list of orders to refund has been read
What a payment attempt can end as, and why each is its own word
StateWhat it meansThe basket
OpenCreated; they have not finishedHeld
CompletedPaid, and settled behind itOrdered
ExpiredClosed unpaid. Nobody was declinedGiven its own clock back
CancelledThey came back and cancelled. The only one they choseLeft alone
FailedPaid, and settlement refused it. Refunded in fullUntouched

Settlement, and the ledger under it

One immutable row per line of money that moved. Everything that reports a figure reads it, and nothing else does. A refund is a negative row, so every report is a plain sum and no query has to remember to subtract. That was the bug it was built to fix: refunds used to be written as positive rows of a different kind, and a sale refunded to zero still reported its full takings.

Append-only, enforced rather than agreed. It is the one table here that cannot be edited or soft-deleted, and that is deliberately what lets the others stay editable. A correction is a compensating row.

Currency on the row, with the conversion frozen beside it. Reporting used to convert at read time, so the same historical month answered differently depending on the day you asked Measured or reconstructed is marked on every row, so a report can tell a real zero from a rebuilt one Never summed across currencies. A chart takes one currency or it takes counts; a sale taking euros and pounds is drawn twice rather than converted once Seventeen months rebuilt to €0.00 in both currencies, after two earlier attempts that looked obviously right and were short by €4,837
LineSoldSalesCouponsNet sales
Early bird300€7,500.00€0.00€7,500.00
Advance392€12,544.00€384.00€12,160.00
Final release186€7,068.00€0.00€7,068.00
Tables and extras5€9,640.00€0.00€9,640.00
Your payment provider, on 612 card payments−€698.52
Settles to your own balance931€36,752.00€384.00€35,669.48

Reports, and the box you ask them from

A closed set of reports, and one box that picks between them. The model reads the question and chooses which report to run on which sale over how many days, then puts the resulting figures into a sentence having been shown the figures and nothing else. Every number in that sentence is then checked back against them, and a sentence carrying one that is not there is thrown away. You get the figures reported plainly instead, and the card says so.

Findings have no model in them at all. Patterns worth acting on are format strings over named queries, so two people running them on the same data get the same words, and each detector has a floor below which it says nothing rather than guessing.

The same functions as the Reports screens, through the same permissions. A figure in a chat and the same figure on a screen are one query run twice The evidence is stored with the answer, each figure beside the function that produced it. A merchant who cannot trace a number cannot act on it Counts and money come back together. "You lost 41% of baskets" is a statistic; "…holding €9,048" is a decision Anything outside the set is answered with the set, as buttons that fill the box with a whole question, rather than with a plausible sentence about nothing
What can be asked, exactly
ReportAnswers
TakingsWhat a sale has taken, per currency, net of refunds
MethodsHow the money arrived, and what share each took
FunnelWhere baskets stop: opened, held stock, reached payment, bought
Audience · OriginsWho bought, and the countries and cities they gave
Losses · Lost salesWhat is being given up on, what was in it, and whether it is getting worse
Pace · When · ProductsHow long people took, when they bought, which tiers get put back
Pulse · LiveToday against yesterday, and what is sitting in a basket this second
AdvicePatterns worth acting on, with the evidence behind each

The shop you drop on your own site

Two lines of HTML put the checkout on your page. It is a custom element behind a shadow root, and the boundary is the point: your stylesheet cannot reach in and break the checkout, and the checkout cannot reach out and restyle your site. Colours come through as ordinary custom properties, so you theme it with a stylesheet rather than by stringifying colours into HTML.

Every size decision asks the space it was given, not the window. That is what makes the same build correct in a 380px sidebar on a 27-inch monitor and full screen on a phone. One component answering the same question twice, rather than two builds and a redirect that guesses from a user agent.

Designed at 360px and allowed to grow. It is a phone in a queue, on data, one-handed — two steps, each one screen The basket and the extras open over the menu, never as a step, so closing puts you back exactly where you were reading It says who is selling, assembled from your billing profile. In much of Europe a requirement rather than a nicety, and a widget inside somebody else's page cannot assume the host said it for you The way back with nothing on the device (cleared storage, a different phone, a webview that blocks it) is the order code from the confirmation mail, with no session at all No site of your own gets you a page. It is art-directed from a closed vocabulary the model picks between: layout, palette, typeface, texture, emphasis. The register then changes halfway down on purpose, because a checkout converts by looking like every other checkout and nobody enters a card number under stencil caps Several dates at one address. A tour, a festival weekend or a season gets a page that is deliberately not the shop, because a shop with no basket and nothing to buy is harder to explain than a second small page. A date with no published link is listed without one rather than given a link that 404s
Container width, and what the same component becomes
GivenWhat it draws
under ~48remOne column, sticky action bar, basket as a sheet
~48rem and upTwo columns, basket rail always visible, no action bar
~56rem and upProducts in two columns as well
No site of your ownA hosted page, art-directed above the shop and deliberately ordinary at it, because nobody enters a card number under stencil caps

Running it as your own platform

A tenant is a whitelabel environment and an operational division at once: a franchise, an overseas subsidiary, a venue group. It owns its name, logo and base theme, the identity its mail comes from, and its own staff. Every scoped row in the system is stamped with which tenant it belongs to, by the database rather than by the code that happened to write it.

Being the wrong kind of account and asking for a row you cannot see are two different refusals, on purpose. The first says so; the second answers as though the row does not exist, because a permission error would confirm that it does.

Door staff are assigned per sale, not to a whole tenant. The person on the door in Berlin has no business scanning a Hamburg ticket, and a new endpoint is closed to them by default Signing in as somebody else is strictly downward, only into accounts that could sign in themselves, with a strip of screen saying so for as long as it lasts, and the log names whoever started the chain, not the last hop No passwords anywhere. Staff get a six-digit code by mail; door staff get a single-use link, because they are temporary, on a shared device, with nothing to remember Published terms are frozen. Editing one writes a new version chained to the old, and every sale stays on the version it was sold under. What a shopper agreed to is not editable after the fact
Who sees what
RoleReaches
Platform ownerEvery tenant, and the tenants themselves
Tenant adminOne tenant, everything inside it
OrganiserOne tenant, their own rows inside it
Door staffOne tenant, and only the sales they are assigned to
ShopperTheir own orders, including everything they bought before signing in

Seat maps Pro

A venue is one document that stores generators rather than seats: a block says "38 rows of 26, 50cm apart, bowed by 60cm, lettered from the front". A 90,000-seat bowl is a couple of thousand of those, and a few hundred kilobytes, against a seat table that would dwarf every order ever placed and exist only to be read back in one lump and drawn.

What a seat costs and whether it is free are never in the plan. Both are facts about one night rather than about the room: availability changes minute to minute, and a price differs between a matinee and a Saturday. A plan is reused across a season, so a price written into it would be a season-long lie that only surfaces in front of a shopper.

Where it is today. Draw the room, assign seats, print the right chair on the right ticket, and see which of the house went. A shopper choosing their own seat is the piece still being wired to the till, and we will say so before you sign anything "Not on sale" and "sold out" read differently. On a half-open house that is the difference between "the balcony went" and "the balcony was never opened" A seat taken mid-basket is reported, not silently dropped. A basket that quietly shrinks between looking at it and paying is the one failure that turns a checkout into a support ticket The building is computed, never drawn twice. Decks, risers, fascias and walls come from the seats themselves, which is what makes "what does this seat see" answerable at all
One map, four questions, and what each is allowed to write
AskingWrites
Where do these people sitThe order's seats
Which seats is this ticket sold forThe product's seat list
Which of these chairs have goneNothing at all
Where would you like to sitThe basket's holds, the seam still being wired

See it against a season of your own numbers.

Thirty minutes. Send last year's export and we will send back your repeat buyers, what was recoverable and what it was worth, and how many of those people you could lawfully contact.