Roles & permissions

The five firm roles, their full permission matrix, and how RLS enforces tenant isolation.

Kiravy has five firm-level roles plus a separate client-portal role for SME staff on the other side. This page is the full matrix — when you're about to invite someone, come here first.

The five firm roles

In order of decreasing power:

RoleDisplay labelWho it's for
partnerPartnerFirm owners. Full admin including billing and firm deletion.
managerManagerSenior staff who run the day-to-day. Same as Partner except can't delete the firm or change Partner rows.
seniorSenior accountantExperienced accountants doing client work. Only sees assigned clients.
juniorJunior accountantTrainees and junior staff. Same access as Senior today — the distinction is reserved for future approval workflows.
viewerView onlyTrainees observing, external auditors, partners on holiday who just want to look. Read-only on assigned clients.

The role enum lives in the database as partner / manager / senior / junior / viewer. UI shows the human-readable labels above. Earlier in some docs we use "Staff" as shorthand for Senior + Junior — that's the same group.

The visibility rule

RoleSees which clients
PartnerAll clients in the firm
ManagerAll clients in the firm
SeniorOnly clients they've been assigned to
JuniorOnly clients they've been assigned to
ViewerOnly clients they've been assigned to

Assignment is a per-client decision — see Set up your firm → Assigning staff and viewers for the how.

Full permission matrix

The cells say what each role can do on the firm side. Client portal users are covered separately at the bottom — they're not firm members.

FeaturePartnerManagerSeniorJuniorViewer
Clients — create, edit name / SSM / industry
Clients — archive / restore
Clients — delete permanently
Clients — assign staff to client
Transactions — view✓ all✓ all✓ assigned✓ assigned✓ assigned
Transactions — create / edit / soft-delete
Transactions — CSV import
Customers / Suppliers — view✓ all✓ all✓ assigned✓ assigned✓ assigned
Receipts — upload + review
AR / AP — view invoices, aging
AI assistant (client-level)
AI assistant (firm-level)
Engagements — open / close month-end
Engagements — review queue, categoriseview only
Tax deductibility — run AI check
Tax deductibility — override verdict
Reports — view P&L
Reports — export PDF / CSV
Firm settings — profile, logo
Firm settings — integrations (SQL Account agents)
Firm settings — team (invite / change / remove)
Firm settings — AI usage
Firm settings — billing
Pair SQL Account bridge agents
Delete firm permanently

Where "✓ all" appears in view rows, it means the role sees the data across every client. "✓ assigned" means they only see clients they've been explicitly assigned to.

Senior vs Junior — what's actually different today

Today: nothing functionally. Both have the same database-level permissions and pass canEdit checks identically.

The two are distinct in the enum because future features will lean on the split — for example, Junior categorises → Senior approves workflows. Until those land, treat them as interchangeable role names you can use to tag your team for HR / org-chart purposes.

What a Partner is and how you become one

A Partner is set at firm creation — the user who creates the workspace is automatically its Partner. There's no UI flow to change someone to Partner after the fact; the database policy (migration 0013) blocks it specifically.

If you need a second Partner: today, contact support. We'll add a Partner-promotion flow when there's a real reason to (e.g. someone leaves and a Manager needs to step up).

You can have multiple Partners in one firm — the database allows it. Just no self-service UI for the promotion yet.

The Client portal user

A completely separate identity. Lives in the client_portal_users table, not workspace_members. They:

  • Sign in at /portal/login (different from the firm-side /login)
  • See exactly one client — the one they're invited to
  • Can upload receipts and view their own data
  • Cannot see the firm dashboard, other clients, or any firm settings
  • Cannot see staff names or firm-internal AI conversations

Portal users come in two flavours: Owner and Staff (e.g. the SME owner vs an accounts-receivable clerk on their team). Distinction is mostly cosmetic today; future features (signoff on receipts, billing visibility) will lean on it.

See Client portal for the full surface.

How Kiravy enforces this — RLS

The role checks above aren't just UI bouncers. Every read and write goes through PostgreSQL Row-Level Security policies before the application even sees the data.

Two helper functions do the heavy lifting:

  • current_user_workspace_role() — returns the calling user's role in the current request's workspace. Drives "is this user a Partner / Manager?" checks.
  • is_assigned_to_client(client_id) — returns true if the user is either a Partner / Manager or explicitly assigned to that specific client. Drives "can this Senior see this client?" checks.

Every tenant table has policies like:

create policy "tenant_isolation" on customers for select
  using (is_assigned_to_client(client_id));

The upshot: even a buggy application query, or a forgotten WHERE clause, can't leak data across firms. The database refuses to return rows the user shouldn't see.

This is also how the SQL Account migration agent safely uses a service-role key — its server-side code does the role check explicitly before bypassing RLS for the upsert.

Practical scenarios

Adding a junior accountant

  1. Firm settings → Team → Invite member, role Junior
  2. After they accept: open each client they should work on → Settings → Team → Add assignment
  3. They now see only those clients in their list

Demoting a Manager to Senior

Click their role dropdown in Team. Confirmation modal warns "this is a downgrade." Their data stays attributed to them; they just lose firm-wide access.

Replacing a departing accountant

Don't delete their account — that orphans the audit trail. Instead:

  1. Deactivate them (button in Team → their row)
  2. They lose login access immediately
  3. Past transactions / receipts they created stay attributed to them
  4. Onboard the replacement separately
  5. If they ever come back, Reactivate restores access

An external auditor visiting

Invite them as View only, assign to the specific clients they need. They can browse, run reports, even export PDFs — but can't change a single number.

What's coming

  • Partner promotion / demotion via UI (today: support only)
  • Custom roles for firms that need different splits (post-MVP)
  • Per-role approval workflows — what makes Senior vs Junior actually differ
  • Audit log UI for Partners to see who did what

What's next