A subscription analytics platform where almost nothing is ours, and every boundary is
ParlayBI gives a hotel and casino operator one place to see how the whole property is performing, and the same place to build next year's budget. Casino floor, hotel, food and beverage, labour and financials, drawn from source systems that were never designed to speak to each other.
The data belongs to the operator. The dashboards are drawn by a vendor. Credentials are checked by another. What we built is everything in between.
- 2 analytics vendors behind one login
- 1 identity artefact, issued by us
- 0 vendor secrets reachable from a browser
- 0 code changes to publish a new dashboard
- 0 application changes to onboard a property
These are structural facts about the build rather than measured outcomes, which is why they are the ones on this page.
What the platform does
A hotel and casino runs on a stack of specialised systems that grew up separately. Room revenue in the property management system, gaming revenue in the slot system, covers and check averages in the point of sale, labour in scheduling and payroll. Consolidating them into a defensible view of the property is a project in itself, and one most operators of this size cannot justify.
Income statement, slot floor analysis, hotel guest segments, outlet covers and check averages, overtime and staffing, and day-level profitability. Each drawn from the operator's own warehouse and rendered inside the product.
A home page carrying five headline measures against the same days a year earlier, coloured by whether the movement is favourable. It exists to answer whether anything is wrong today, without interpretation.
Not a dashboard showing a budget produced elsewhere. Interactive screens on which finance builds next year for any profit centre, with trailing twelve month context beside every assumption and an earnings bridge that moves as the assumptions do.
An administrator can put a new dashboard into the product's navigation, with its own label, icon and placement, without a release.
The marketing pages carry live, interactive dashboards rather than screenshots, served to visitors who have not signed in.
- Client
- ParlayBI
- Sector
- Business intelligence, gaming and lodging
- Market
- Hotel and casino operators, multi-property by design
- Product
- Subscription analytics and budget planning platform
- Users
- Executive leadership, finance, department heads, and external stakeholders on a curated view
- Our scope
- Application architecture, the service layer, the embedding and authorisation model, the budget planning workbench, and the second vendor integration
The one fact that shaped everything
We hold almost none of this, and we answer for all of it.
The warehouse belongs to the operator. Every dashboard is drawn by a visualisation vendor reading that warehouse directly. Credentials are validated by an identity provider. A second vendor renders the operational screens a read-only dashboard cannot.
Four components, none of them ours, each holding or touching the most commercially sensitive numbers in a customer's business.
An operator does not care which vendor rendered a chart. They care that their figures did not reach another operator, that a number entered this morning is the number the board sees this afternoon, and that a department head given a curated login cannot reach the rest of the business.
Every one of those is decided in the space between components rather than inside any of them. That space is the product, and it is the whole of what we were responsible for.
The decisions that mattered
If you read nothing else on this page, read this.
No vendor secret, no vendor token and no vendor credential ever reaches a browser.
A provider confirms who somebody is. We decide, on every request, what they may see.
That removes a reconciliation and creates a correctness problem, which is answered by four rules rather than by hoping.
Not a second authentication design.
The backend is the only client
The challenge
To render a private dashboard, something has to prove to the vendor that the request is legitimate.
The direct path hands that proof to the browser. A token, or in the worst version the credential that mints one. It works, it is faster to build, and on the day it ships it is indistinguishable from the alternative.
Anything that reaches a browser is readable, storable and replayable by whoever obtains it. On most products that is a bounded problem. On this one, the embedded layer carries the most sensitive data in the customer's business, so a client-side credential is not a small mistake. It is the whole dataset.
There was a second temptation in the same place. The credential returned by the identity provider is convenient to keep: pass it to the front end, forward it to the vendor, reuse it later.
What we did
Every vendor secret stays on the server. The credentials, the grant generation and the logic around both live exclusively in the service layer. The front end receives none of them, and gains no capability by inspection.
The browser receives a scoped, expiring grant. Not a credential. A grant already limited to one user and one view, valid for minutes rather than hours. Intercepted, it opens one thing for one person and then stops.
The identity provider's answer is discarded immediately. It exists on our server for as long as it takes to answer whether the person is who they claim to be, and is then thrown away. It is never stored, never forwarded, and never sent to a vendor.
Every hop is encrypted, front end to service, service to provider, service to vendor, with protocol downgrade refused.
And renewal happens behind the user. Short-lived grants are a deliberate security decision that creates an operational one, because an analytics session is longer than any sensible token lifetime. Somebody opens a dashboard, filters it, and returns to it twenty minutes later. The service re-mints before the current grant lapses, so the view never goes stale in front of the person using it.
The blast radius of a stolen grant is one user, one view, a few minutes. That is the whole of it, and it is a sentence a security reviewer can check.
There is no credential of any kind reachable from the client, which removes an entire class of question from a customer's security review rather than answering it.
Users never encounter authentication. If they notice the auth flow, something has already gone wrong, and an expiry error mid-analysis reads as a broken product rather than as a security feature.
Token issuance and failure are monitored as a first-class measure, so a problem surfaces to us before it surfaces to a customer.
Identity is not authorisation
The challenge
Federated sign-on answers identity well and authorisation barely at all. It confirms that a user is valid. It does not know that this particular user may open the revenue view, and only for one property.
Getting that wrong on a platform like this means one operator seeing another operator's numbers. It is the failure mode that does not announce itself, because coarse permissions look like a working system right up until an audit or a support ticket.
And the decision has to hold for somebody who never touches the interface. Hidden navigation is a presentation choice. It is not a boundary.
What we did
The provider answers one question and stops. Is this person who they claim to be. Everything about what they may then do stays with us.
Our own session is the only identity in the system. Once the provider confirms the person, the service issues its own session carrying identity and role, held so the browser cannot read it, and time-boxed.
Every request is authorised on the server, against the role resolved at that moment rather than at sign-in. A role change takes effect on the next request rather than at the end of a session.
Scope is applied when the grant is minted, so a user limited to one property cannot widen their own reach from the client. The restriction travels inside the grant rather than being requested politely alongside it.
And a limited user sees a coherent product rather than a locked one. Menus and modules are assembled from what that person is entitled to, so a department head or an external stakeholder meets a curated portal rather than a full product with doors that refuse them. An empty module says so plainly instead of returning an error.
Bypassing the interface achieves nothing. A request made directly to the service meets the same check the interface met, because the interface was never the check.
One operator cannot reach another's figures, which on a multi-property platform is the failure that ends a contract rather than generating a ticket.
Permission management is one place. Role and scope are resolved from a single model, so a change is made once rather than in each vendor.
A curated login is a product decision rather than a compromise. Giving an external stakeholder a deliberately limited window is something the platform does well rather than something it tolerates.
The plan writes where the report reads
The challenge
Most operators build next year in a spreadsheet and report this year in a system. The two are detached by construction, so the budget has no systematic benchmark against trailing performance, no automatic roll-up to earnings, and no agreement with the actuals it will later be measured against.
The obvious fix is to build the planning tool inside the reporting product. That removes the reconciliation and immediately creates a harder problem.
A planning tool that writes to the reporting warehouse is a tool that can be wrong at scale. One careless bulk action rewrites a year of assumptions across a chart of accounts, and every dashboard downstream agrees with it, confidently, because they are reading what was just changed.
What we did
Reporting and planning share the warehouse. Budget screens read and write the operator's own warehouse through the service layer, and on save the financial figures behind the dashboards are recomputed. The summary and the divisional views show the figures the planner has just entered, without a refresh cycle or an overnight batch.
And four rules make a write safe to allow.
An account cannot drive itself. Circular references in the forecasting configuration are refused rather than resolved.
Reach is stated before it is applied. A bulk action shows exactly how many account settings it will write, before it writes any of them.
Human edits survive automation. A month somebody set by hand is marked and retained through subsequent auto-fill, so a deliberate override is never silently replaced by an average.
Nothing writes by accident. Every save is preceded by an explicit confirmation. There is no page in the module that commits on a keystroke.
Around all of that sits context rather than a blank grid. Each assumption is shown against its trailing twelve month position, and an earnings bridge walks from the trailing figure through revenue, cost of sales, wages and other movements to the budgeted figure, so the effect of a change is visible while it is being made.
Budget and actuals cannot diverge, because they are the same source. The reconciliation that consumes a finance team's week does not exist rather than being made faster.
A bad assumption is visible immediately. The earnings effect appears as the number is typed rather than after a roll-up.
Deliberate overrides are protected, which is the difference between a planning tool a finance team trusts and one they keep a spreadsheet beside.
The expensive mistake is difficult to make, which on a tool that writes to the reporting warehouse is worth more than any feature in the module.
The second vendor is an adapter
The challenge
The visualisation layer is read-only by design. It renders what the warehouse holds, and for every dashboard in the product that is the right tool.
One area of the product is not a dashboard. It needs structured input, validation and write-back, which is an application rather than a report. That was built on a low-code platform and embedded into the portal.
Adding a second vendor reopened every problem the first one had already closed. A different trust model, a different token format, a different lifetime, and a different way of receiving user context. The default behaviour was exactly the failure already eliminated once: a user who had signed in, opened four dashboards without a prompt, and then reached this screen would be asked to authenticate again, by a vendor they had never heard of.
Single sign-on is only credible if it is unconditional. A product where four modules load silently and the fifth asks who you are does not read as one product with a small gap. It reads as two products stitched together.
What we did
Our own session stayed the only identity in the system. Both vendors are downstream of it and neither is ever an authority. Neither is aware the other exists.
Embed issuance was normalised behind one internal contract. A single service turns this user and this resource into a signed, expiring grant. The two vendors are two implementations behind that one interface rather than two parallel authentication paths.
The second vendor's credential joined the first on the server. The front end gained no new capability and received nothing beyond a scoped, expiring grant, so decision one holds unchanged for the second vendor.
Authorisation crosses the vendor boundary. Role and property scope are resolved once from our own model and injected into both grants, so a restricted user is restricted identically whichever platform is rendering.
Two expiry clocks are tracked rather than one. The vendors issue grants with different lifetimes, so background renewal is handled per vendor and neither surface goes stale mid-session.
And the second vendor reads the same warehouse. Nothing is copied into a second store, so the single source of truth survives the addition.
One login covers both platforms and the user never learns there are two. No vendor branding, no vendor navigation, no vendor login screen.
A permission change takes effect in both at once, from one place.
A third vendor is an adapter rather than an architecture. The interface exists, the pattern is established, and the security properties are inherited rather than rebuilt.
And the same argument extends to the operational screens themselves. Because they are assembled in a low-code builder, changes to that area do not consume application release capacity, which is the same economic argument that motivates self-service dashboard publishing elsewhere in the product.
Which criteria this work is judged on
Three of the five trust services criteria carry the weight here, and the first one is unusual as a lead.
Security
This is the criterion the whole architecture is an answer to, which is rarer than it sounds. On most platforms security is the floor beneath the interesting decisions. Here it is the interesting decision, because the product is a controlled window onto somebody else's most sensitive numbers and every pane of glass in it was made by somebody else.
Every secret held server-side. Grants scoped to one user and one resource, expiring in minutes. Authorisation resolved on the server on every request rather than at sign-in. The identity provider's answer discarded within seconds of being received. Transport encrypted on every hop with downgrade blocked. Authentication failure monitored as an operational measure rather than discovered from a support ticket.
Processing integrity
The criterion asks whether the system does what it claims, completely and accurately. On a reporting product that is a question about the pipeline. On a product that also writes next year's budget into the reporting warehouse, it is a question about every save.
Circular references refused. Reach stated before a bulk action applies. Manual edits marked and preserved through automation. Every write preceded by an explicit confirmation. Financial tables recomputed on save so the plan and the report cannot drift apart.
Those are not usability niceties. They are the controls that make a planning tool safe to point at a reporting warehouse.
Confidentiality
Multi-property is the whole market, so the boundary between one operator's figures and another's is the product's central promise rather than a configuration detail. Scope resolved from our own model and carried inside the grant is what makes that promise structural rather than procedural.
It also covers the boundary inside a single operator. A department head or an external stakeholder receives a curated portal rather than a restricted one, which is a confidentiality control expressed as a product decision.
The control set was designed against the criteria rather than mapped to them afterwards, so an organisation preparing for an attestation is documenting controls that already exist rather than retrofitting them under time pressure.
That is a statement about how the platform was built. It is not a claim to hold an attestation, and where an operator or the platform itself needs one, the work is theirs to complete with an auditor.
The constraints we designed around
Every architecture defers something. Building for every constraint on day one is not prudence, it is waste. The work is knowing which to answer up front, which to answer when they become real, and which belong to somebody else.
The login screen is the first thing anybody sees
The challenge. Every standard sign-on option evaluated was built around redirects. A user would be inside a carefully designed interface one moment and looking at a generic hosted login screen the next.
That architecture assumes people are content bouncing between screens. For an internal tool, perhaps. For a product a prospect is evaluating, it meant the first impression of the application was a page we could not style, could not fix and could not debug.
What we did. Built the sign-in interface ourselves and kept the provider behind the service layer. Credentials post to our API, which verifies them programmatically. Every error state, from a wrong password to an expired invitation, is written and styled by us rather than inherited.
No redirect at any point. The user stays on one domain in one design system for the whole session, and the one screen everybody sees is a screen we control.
The public site shows real dashboards to strangers
The challenge. The marketing site carries live, interactive dashboards rather than screenshots, served to anonymous visitors through the same embedding pipeline as the paid product. That is unusual in this category and it is a large part of why a prospect believes the product is real.
It also points the pipeline at the open internet.
What we did. The demonstration views are served from a separate dataset rather than from any customer's warehouse, and the anonymous path issues the same scoped, expiring grants as the authenticated one rather than a relaxed variant of them.
A prospect interacts with the genuine product before speaking to anybody, and no customer's figures are involved in that. The two surfaces share a pipeline without sharing data.
Structure comes from the warehouse, not from the code
The challenge. Every operator has a different chart of accounts, different divisions, different departments and different job titles. Meeting that with application code produces a branch per customer and a codebase that becomes a museum of past clients.
What we did. Property, division, department and job names are read from the operator's warehouse at runtime. Nothing is hardcoded to a single property or a single chart of accounts.
The same deployment serves a multi-property operator, and onboarding a further property is a data exercise rather than a development one. It is also what makes the implementation timeline the client markets achievable at all, because the work concentrates on mapping source systems into the warehouse rather than on rebuilding the application.
New reporting cannot wait for a release
The challenge. Customer requests for new dashboards are continuous and individually small. Answering each one through a release cycle consumes the capacity that would otherwise build the product.
What we did. A management console through which an administrator publishes any available view into the product's navigation, with its own label, description, icon and placement. Registered dashboards are rendered by generic viewer pages, so a self-published view looks and behaves exactly like a built-in one.
New reporting reaches users the moment the view exists. Ongoing requests stop competing with product development for the same weeks, which is a cost decision expressed as an architecture.
The planner sees named employees
The challenge. Budgeting salaried labour honestly requires knowing what is currently established, so the screen shows current headcount by job and the people in those posts before the planner sets next year's figure.
That is personal data about identifiable staff, sitting inside a finance tool, and it is the kind of detail that is easy to add without deciding to.
What we did. Raised it as a boundary to be decided by the operator rather than assumed by the software, since employment data handling belongs to the employer.
The question is on the table rather than discovered later. A platform that has not noticed this is a platform that will handle it carelessly.
The impact, depending on your job
If you run the business
Two surfaces, one pipeline, is a commercial asset rather than an engineering detail. A prospect interacting with genuine dashboards before speaking to sales is a shorter sales cycle, and it is only affordable because the marketing site and the product are the same application.
The budget module is the separation from a reporting tool. Analytics platforms are compared on dashboards and won on whether finance can plan inside them. A planning workbench that writes to the same warehouse the reporting reads is difficult to answer with a dashboard library.
Adding a vendor no longer costs an architecture. The capability the product can absorb is now limited by commercial judgement rather than by what the authentication model can carry.
If you run engineering
The embed model is the part worth your attention. The backend as the only client, grants rather than credentials, and the identity provider's answer discarded within seconds of arrival.
Then the separation of identity from authorisation, with scope resolved server-side per request and carried inside the grant rather than requested alongside it.
Then the write path, where four rules make a planning tool safe to point at a reporting warehouse.
And the adapter, which is what turned a second vendor from a second authentication design into one implementation behind an existing interface.
If you run a delivery team
Differences between operators are data rather than branches. Structure is read from the warehouse at runtime, so there is no per-customer fork to maintain.
New dashboards reach the product without a release, so a continuous stream of small customer requests does not consume delivery capacity.
The operational screens are assembled in a builder rather than in the application, for the same reason.
And the vendor interface means an integration is a known shape of work rather than a discovery exercise.
If you own the numbers
Onboarding a further property is a data exercise, so the cost of the next customer does not rise with the number already connected.
New reporting does not consume release capacity, which converts an unbounded stream of customer requests from a delivery cost into an administrative task.
Preparing for an attestation is documentation rather than remediation, which for a platform selling to operators with their own audit obligations is measured in quarters and in consultant fees.
A vendor can be replaced. Because both are behind one interface and neither holds authority, a commercial decision about a supplier is not also an architectural one.
Talk to the client, not just to us
Everything on this page is our account of our own work, which is worth roughly what you would expect.
So we would rather introduce you. If you are seriously evaluating us, we will arrange a reference call with a client who has been through a build like this one, and you can ask them the questions you would rather not ask us.
Built with
We name the layers rather than the suppliers on the customer data path, because a supplier map is our client's exposure rather than our credential. Full detail available under NDA.
Next.js and React with TypeScript, serving both the public site and the authenticated product from one application
FastAPI in Python, holding authentication, role resolution, the dashboard catalogue, embed grant issuance, the budget engine and lead capture
A SQL warehouse owned by the operator, holding the operational and financial tables and the logic that recomputes financials after a save
An embedded analytics platform, reached only through the product and never directly by a user
A low-code application platform, embedded through the same grant model and reading the same warehouse
A hosted identity provider for credential validation, with the application issuing its own time-boxed session
A client-side library for the trailing-twelve-month graphs and the earnings bridge on the budget screens
Managed email for lead notification and acknowledgement, and object storage for dashboard icons and thumbnails
More case studies
View all case studiesInvestor Relations for ASX
Investor relations for ASX-listed companies, where the tenants are competitors.
Read case studyIQEX
An AI generation platform where every action is priced at the moment it happens.
Read case studyElloMed
A clinic that can tell you where every patient is, and who owes what.
Read case studyBuilding a product that embeds somebody else's platform?
Tell us what you're building, and we'll tell you honestly how we'd approach it.