A consistent rule across the public portal APIs: the link's own org id is the authority for everything read downstream, queries re-stamp that org id even though the service client bypasses row-level security, and a link with no tenant binding is refused rather than served.
Also called: another builder's data showed up · multi tenant safety · data leak
A link that cannot name its account is refused, not guessed
Two public routes used to fall back to one particular company's account whenever they could not work out whose portal was being asked for, so an unauthenticated request could come back wearing another builder's name, logo and copy — and an unrelated settings screen rendered it too. Both refuse now. A customer seeing a competitor's brand on their own project is not something a builder gets to explain away.
Every read is re-stamped with the account the link belongs to
The link's own account is the authority for the project, the invoices, the change orders and the branding, and each query re-applies it even though the server key could reach past the database's own isolation rules. Defence in depth, because a project identifier that collides across two builders should be a nothing event rather than a disclosure.
A password on a link protects the data, not only the page
When a customer sets a password, the build progress feed behind their dashboard demands the same signed session — milestones, photos and the payment schedule are not readable from the link address alone. Protecting the page and leaving the data it loads open is the shape of most portal password features, and it protects nobody.
Public endpoints carry a budget sized to a real customer
The link is the credential and there is no login to throttle against, so every public action gets a per-address request budget: about sixty reads a minute on the billing lists, far fewer on sign-in and approval attempts. Sized so a customer refreshing their dashboard never notices, and a script hammering a leaked link does.
- 1The project endpoint refuses with an error when the link has no org id
- 2The config endpoint requires either a link id or a signed-in user and 400s otherwise
- 3The change-orders query re-applies the link's org id as defence in depth
- 4Passworded links additionally require a valid session token on the project feed
Two public routes fell back to one particular company's account whenever they could not work out whose portal was being requested, so an unauthenticated request could return another builder's branding and copy, and an unrelated settings screen could render it too. Both refuse now rather than guessing. Every public portal route resolves the account from the link and serves nothing at all if it cannot, because a customer seeing another company's name on their own project is not something a builder gets to explain away.
- Default-tenant fallbacks silently serving one builder's data to another
- Public endpoints readable without any tenant binding
- Build progress on a passworded link readable from the link id alone
See it on your own jobs
Twenty minutes, your numbers, no slide deck. We’ll build one of your real buildings in front of you and send you the estimate link at the end — yours to keep either way.
or keep browsing features →