Your first hundred customers are individuals: one person, one unit, one account. Then a property management company calls wanting 60 units across 14 buildings, a regional manager who sees three of them, technicians from an outside contractor who can service any unit but read no billing data, and a corporate account that survives when the regional manager quits. If your database says every device has one owner_user_id, you are about to spend a quarter rewriting the core of your platform while that customer waits.
Permissions decide which customers you can sell to and how expensive your next migration is. Here is how to think about the model before it hardens.
Start with the tenancy hierarchy, not the roles
The mistake is designing roles first. Roles are easy to change later; the object hierarchy they attach to is not. A workable default for connected hardware is four levels:
- Organization — the billing and contract entity. Everything else hangs off it. A consumer account is simply an organization with one member, which keeps you from maintaining two separate code paths.
- Site or group — a building, a vehicle fleet, a floor, a customer of your customer. This is the level most permissions actually apply to, and it is the one teams forget to build.
- Device — the physical unit, identified by the credentials it received during provisioning on the production line, not by whoever first paired it.
- User — a person, with a membership record linking them to an organization at a given role. A person can belong to several organizations; a technician who works for a service company will.
The critical rule is that a device belongs to an organization, never directly to a user. Users get access through membership. Once you accept that, transferring a unit, replacing an admin, and adding a second person to an account all become the same operation instead of three special cases.
Roles people actually need
Four to five roles cover almost every B2B deployment. More than seven and your customers will not understand the picker.
| Role | Can do | Cannot do |
|---|---|---|
| Owner | Billing, contract, delete the organization, transfer ownership | Nothing — but there should be at least two |
| Admin | Invite users, create sites, move devices, change settings | Change billing or delete the org |
| Technician | Run diagnostics, push firmware, calibrate, close service tickets | See billing, invite users, export customer data |
| Viewer | Read dashboards, export reports | Change any device state |
| Service partner | Scoped technician rights on specific sites, time-limited | See the rest of the customer's fleet |
Underneath, store granular permissions (device.reboot, firmware.deploy, billing.read) and define roles as bundles of them, so the custom role a customer eventually asks for is a configuration change rather than a release. The split between an operator view and a service view is worth reading about separately in the context of a user app versus a technician app, because the permission model and the UI split are related but not the same decision.
Invitations and domain claiming
Invite by email address to a role and a scope, with an expiring token, whether or not the invitee already has an account. Two details save enormous support time: let an admin see and revoke pending invitations, and offer domain claiming, so that once a company verifies a DNS record, anyone signing up with that email lands in the existing organization as a pending member. Shadow accounts are the most common cause of the ticket that begins "my colleague can't see any of our devices."
Devices change hands
Hardware gets resold, moved between facilities, returned, and repossessed. Build an explicit transfer operation early: it removes the device from the source organization, wipes or archives its historical data according to your retention policy, revokes cached tokens on the device itself, and re-enrolls it into the destination organization. Doing this properly is a compliance question as much as an engineering one, because the buyer of a used unit should not inherit the previous owner's usage history — a point worth working through alongside privacy compliance for connected product data.
The related trap is decommissioning. Give admins a retire action that keeps the record but takes the unit out of operational views and out of seat-based billing, and make sure your fleet management tooling distinguishes retired from silently offline.
Audit logs
An audit log answers three questions: who changed this setting, who accessed this customer's data, and what did our own support staff do while impersonating an account. Log the actor, the target object, the before and after values, the source IP, and whether the action was taken through impersonation. Keep it append-only and separate from application logs. Enterprise buyers ask for 12 months of retention as a matter of course, and building support impersonation as an audited, expiring grant rather than a superuser flag is a decision you will not regret.
SSO and SCIM for enterprise buyers
Above roughly 200 seats, procurement will require single sign-on through SAML or OIDC against Okta, Entra ID, or Google Workspace, and often SCIM provisioning so that an employee removed from the corporate directory loses access to your platform automatically. Retrofitting SSO into an authentication layer that assumed passwords is a multi-month project; leaving room for an external identity provider from the start is a week. At minimum, do not make the email address the primary key of a user, keep authentication separate from authorization, and store an external identity ID field even if it stays empty for years — the same reasoning behind user accounts and sign-in for a product app.
The decisions that are painful to reverse
Some choices are cheap now and brutal later. Data isolation is the big one: a shared database with an org_id column on every table is fine for most products, but you must enforce the filter in one place, not in every query. Add row-level security or a repository layer that refuses to build a query without a tenant scope. A single missing WHERE clause in a reporting endpoint is how cross-tenant data leaks happen.
Others worth deciding early: whether a user can belong to multiple organizations (say yes), whether region-pinned data storage is possible for customers who demand it, and whether your B2B customer portal and internal admin tools share one permission engine. They should — two engines drift, and the drift is always a security bug.
Projects House builds connected-product platforms where the permission model is designed against the sales motion, not retrofitted after the first enterprise deal stalls. Describe the customers you expect through the contact form and we will walk through the tradeoffs with you.