Workspace invitations, a notification inbox, and plans that say what they include
Invite people to a workspace by email before they have an account, give every member an in-app inbox that updates live with their own notification preferences, and see exactly what each plan includes. It ships in @buildbase/sdk 0.0.71; signOut() revoking the server session shipped in 0.0.70 the same week.
Added
8- WorkspacesInvitations. A workspace owner can invite someone by email before that person has an account. The invitee gets an email with a link, sees which workspace they are joining and as what role, and lands in it as a member on accept. The members screen lists pending invitations next to members, with resend and revoke, and an invitation that lapses is announced to the inviter in the inbox and by email. Pending invitations count toward the seat limit, so a workspace cannot invite past what it can hold. From @buildbase/sdk 0.0.71.
- NotificationsAn in-app inbox. Every notification your app sends a member now also lands in an inbox the SDK renders, updated live over a socket while the page is open, with read, seen and archive. A member who arrives from a push notification finds that item already marked read. From @buildbase/sdk 0.0.71.
- NotificationsPer-member preferences. Each member picks, per notification type, whether they want email, push, or neither; the workspace's own settings become the default for members who never chose. Delivery reads the member's choice first, then the workspace default. From @buildbase/sdk 0.0.71.
- Notificationsnotification.send() with a type you never set up now registers it. The first send of `order_placed` creates the notification type in the console, switched on, where you can rename it, pick its channels and let members manage it. Type names are normalised on the way in (`Order Placed` becomes `order-placed`; dots, underscores and colons are kept), so a send from code and the entry in the console always agree.
- BillingEvery plan now states what it includes. The catalog grew from 36 sold items to 163: every feature, limit and quota in the product has a row, each plan carries a value for it, and the plan version editor, the publish comparison and the public pricing endpoint print `Unlimited` and `None` rather than -1 and 0. Existing subscriptions keep the version they bought; a published version only changes what new subscribers get.
- BillingUsage against your plan, in the console. Monthly active users, emails sent, campaigns, storage and the other metered quotas show as a proportion of what the plan includes, on the screen where each one is counted, so a limit is visible before it is reached.
- WorkflowsStarter templates, on a Templates page in the console. Ten workflows in two tiers: three "Getting started" templates that each show one connection - an event from your app sends an email, an event posts to your backend, a follow-up waits for a click and stops when it comes - and seven ready-made automations: trial ending, payment failed, cancelled win-back, form to webhook, CRM sync, feature flags on a plan change, and an unsubscribe follow-up. Cloning one fills in your default sender and your copy of the email template, and tells you what is still blank before you publish.
- AuthenticationThe hosted sign-in page shows your organization's logo, and its default copy says BuildBase rather than CMS.
Improved
1- AuthenticationSigning out ends the session on the server. signOut() used to clear the browser's copy and leave the session itself valid until it expired, so a stolen token outlived the sign-out that should have killed it. From @buildbase/sdk 0.0.70 it revokes the session first, and signOut({ everywhere: true }) ends every session the user has.
Fixed
13- WorkspacesAdding a member to a workspace that is full answered a bare 402 with no way to tell why. It now answers with the code SEAT_LIMIT_REACHED, the current member count and the limit, whether the seat ran out on a direct add, an invitation, or an accept, and a blocked invitee is refused with a code of its own.
- WorkspacesA denied workspace request answered 401, which the SDK read as a lost session and signed the user out. It answers 403 now, so a viewer who opens a screen they may not use stays signed in. A workspace the organization creates for a new user also fires the workspace.created event, which it did not.
- AuthenticationSocial sign-in created an account for a new user even when the organization had public sign-up switched off. It is refused now, with the same message the email form gives.
- AuthenticationA magic-link budget of five sent four, and when it ran out the sign-in page still said "check your email" while nothing was sent. All five are sent now, and the next request is told the limit is reached - identically for an address with an account and one without, so the refusal reveals nothing about which addresses exist.
- AuthenticationA slow or unreachable BuildBase control plane was treated as a definite no: it reset an organization's passkeys once, and a console user signing in with Google was asked for consent on every visit. A degraded answer is now a non-answer, cached like one, and the consent prompt appears once.
- DomainsA custom domain was marked unverified on its first failed check, which a DNS hiccup could trigger. It takes four consecutive failures now, a domain that recovers is restored on its own, and a domain that Vercel no longer has is recognised as such rather than retried forever.
- BillingA downgrade could take effect before the period the customer had paid for ended. It now holds the paid plan until renewal reliably, ignores stale and out-of-order Stripe webhooks, and no longer processes another organization's events; trial status is reported correctly through the change.
- BillingIn the plan version editor a price of $1,000 or more displayed as 0.00, and the pricing preview printed -1 where a limit was unlimited. Both display correctly; no stored price was wrong.
- BillingOn a plan that caps how long credits and API keys may live, the console offered "never expires" and the server refused it. The forms now default to the plan's cap and say so.
- AudienceCSV import with "update existing contacts" ticked matched the contacts and then updated nothing. Existing contacts take the imported values now.
- SDKA JSON null in a request body arrived at the server as the string "null". It arrives as null; a field typed as text that used to accept the string now answers 400, as it should have.
- AuthenticationSocial sign-in buttons on the hosted pages show their names again. With three providers every label was cut off, and on a phone Google and GitHub both read "G".
- Self-hostingThe self-hosted quick start runs as written. The compose files required a Redis password that the environment template and the secret-generating command never created, so the first docker compose up stopped before starting anything.