Custom roles, link safety, and a billing audit
Custom roles now work end to end, with the organization's own database deciding what a member may do. Links BuildBase hosts are checked for reputation and can be killed at click time. And a systematic pass over billing fixed six real issues.
Added
6- Access controlCustom roles work end to end. The organization's own database is now authoritative for what a member may do, so the role you assign is what the API enforces, rather than the broader membership tier the person signed in with. On a self-hosted install that database is yours, which is the only place the answer can live.
- Access controlAn audit log for the organization itself, in organization settings: who was invited and at what role, who accepted or declined, who was removed, and when support impersonated the organization. Readable by owners and admins only.
- Access controlAn append-only security trail inside each organization now records every authorization denial, the full API token lifecycle, workspace membership changes, invitation rejections and revocations, secret rotations, and admin-API actions.
- SecurityLinks BuildBase hosts on your behalf are checked before a click goes through. A destination on the trusted list redirects straight away; anything else gets a reputation check and an interstitial that navigates itself once the verdict comes back safe.
- SecurityKill switches for hosted links. A single destination, or the tracking code covering every link in one sent message, can be blocked and refused at click time. That includes mail already sitting in an inbox: the message cannot be recalled, but the redirect can be refused.
- SecurityA malicious verdict blocks the destination and disables the message carrying it, rather than warning only the one reader who happened to click. Both are audited, and the operator alert carries a one-click reversal, because false positives are certain.
Improved
1- ConsoleThe console decides "may I do this" in one place instead of four, evaluating in the same order the server does, so the buttons and the API agree. A control you lack permission for is disabled with the reason on hover rather than hidden, because a control that vanishes cannot tell you what to ask for.
Fixed
14- Access controlA Slack incoming-webhook URL is a bearer credential: anyone holding it can post into the organization's channel. Any authenticated member could read it, whatever their permissions. The route now carries the guard it was missing.
- Access controlRevoking a permission could keep working for up to 60 seconds on any instance that had already cached it, including the one that made the change, so an admin who removed a permission watched the old one keep working. A change now reaches every instance immediately.
- ConsoleScreens and controls that reached data they did not gate on now check the permission they actually need, so a role with read access no longer sees write controls that answer 401.
- Access controlThe roles editor no longer offers permissions the API rejects, and the audit screen resolves who did what instead of showing raw identifiers and truncated rows.
- EmailPhone, SMS and in-message anchor links keep working in tracked mail. A scheme allowlist added alongside click-time blocking had turned tel:, sms: and relative links into a blocked page, including in mail that had already been delivered and previously worked.
- EmailOpen and click tracking links are signed with their own key rather than the one used for sign-in sessions. Existing links keep working, because verification falls back to the old key, which matters when a tracked link stays valid for a year.
- Short linksArchiving or deleting a short link actually stops the redirect, and a destination is validated when it is saved, so a javascript: or data: URL can no longer be stored and later emitted in a redirect.
- AuthenticationCloud-hosted auth domains serve the full security header set. The auth UI is a static export, so the headers configured for the self-hosted container were never served by the cloud deployment, which is the surface where custom auth domains are attached.
- BillingWebhook events for a failed payment, a payment needing extra verification, and a successful payment could be silently dropped depending on which Stripe API version sent them - so the dunning emails and the usage reset after payment did not always run.
- BillingCanceling a subscription could report the pre-cancellation state back for a few seconds right after the cancel succeeded, so a page that refetches on success looked like nothing had happened.
- BillingHitting a seat or trial limit returned a generic server error instead of a clear response an app could detect and use to prompt an upgrade.
- BillingA workspace on the free plan could get stuck there and never be able to upgrade to a paid one.
- BillingRapid concurrent checkouts could create more than one Stripe customer for the same workspace, and a subscription on the extra one could go unnoticed by our duplicate-subscription checks.
- BillingCheckout did not always prefill the buyer's own account email, so a trial could be started under a different address than the one on file.