Permissions
How Psalmly decides who can do what — roles, capabilities, and per-user overrides.
The four roles
Every member of a church is one of four roles:
- Owner — full access to everything, including billing and custom domains. There is usually one owner per church (the person who created it); others can be promoted later. Owners can do anything an admin can.
- Admin — full operational access: people, scheduling, giving, site content, kids check-in, community, settings. Admins cannot manage billing or custom domains (those are owner-only).
- Member — read access to scheduling, people, and site content, plus visibility into their own household's kids check-in. Members can join groups, sign up to serve, RSVP to events, give, etc., but cannot edit church-wide data.
- Visitor — a placeholder role with no permissions by default; used for people who attended but haven't joined the church.
These roles are the answer for ~95% of churches.
When roles aren't enough
Sometimes a church needs to grant one specific person one specific capability without promoting them to admin. Common examples:
- "Sarah runs kids check-in every Sunday but we don't want her in the giving data."
- "Mark posts our announcements but isn't a full admin."
- "Pat sets up our kids rooms + worker schedule each term."
For these, Psalmly layers capability-grained permissions on top of the role. The role gives a baseline; per-user overrides add or remove specific capabilities.
The capability catalog
Each capability is a single dotted string in the form
<area>.<verb> (or <area>.<noun>.<verb> for finer-grained ones):
These are the capabilities you can grant or revoke per person. They're the ones actually enforced at the data layer — granting one to a member who isn't an admin really does let them do that one thing. (Most admin areas stay on the four-role system; see below.)
| Area | Capability | What it gates |
|---|---|---|
| Giving | giving.read | View giving records and reports |
| Site content | site-content.write | Edit Site content (identity, about, staff, important links, ministry pages, theme) |
| Announcements | announcements.write | Publish church-wide announcements |
| Kids check-in | kids.checkin.write | Run check-in + pickup from the worker / admin UI |
| Kids check-in | kids.rooms.manage | Create rooms + schedule workers |
| Kids check-in | kids.pickup.override | Override a pickup-restriction block |
| Settings | settings.read | View settings pages |
| Settings | settings.domains.manage | Add/remove custom domains (owner only by default) |
How a permission resolves
Every server-side check (RLS policy or route handler) calls
has_permission(churchId, capability). The function looks at:
- Per-user override — if there's a
user_permission_overridesrow for(church, user, capability), itsgrantedflag wins.granted = trueis an explicit grant;granted = falseis an explicit revoke (deny). The override is the final word. - Role default — if no override, the user's church role is
looked up in
role_permissions. If the role has the capability by default, the answer is yes. - Otherwise — no.
Visitors have no role defaults — they're explicitly opted out of every capability until promoted.
How do I grant a member a specific capability?
- Admin sidebar → Directory.
- Click the person in the list, then Manage to open their profile.
- On their profile, click Manage access in the top-right.
- Find the capability in its group (Kids check-in, Giving, etc.).
- Click Grant. The optimistic update lands immediately; the subject's effective permission updates by their next page load.
To take it back, click Revoke (explicit deny — blocks even something the role would otherwise allow) or Reset (removes the override entirely; the role default applies).
When to use which
| You want to… | Use |
|---|---|
| Promote a long-term partner | Change their role to Admin |
| Give a focused volunteer one capability | Grant override on that capability |
| Take away a specific capability from an admin | Revoke override on that capability |
| Go back to "just the role default" | Reset the override |
Overrides are loud — they show up on the Access page with an "override grant" / "override revoke" tag so a future admin can see why this person can (or can't) do something specific. The audit log captures every grant/revoke/reset with the admin who made the change and when.
What stayed on the four-role system
Most of the admin UI still gates on the four-role enum (admin / owner can edit; member can read; visitor can do nothing). Only the capabilities in the catalog above are enforced at the finer level: kids check-in (run check-in, manage rooms, pickup override), site content, announcements, custom domains, viewing settings, and viewing giving records. Everything else keeps the simpler role check — scheduling, people management, creating funds
- editing gifts, community moderation, editing settings, billing, and the admin AI tools are all admin/owner-only and can't be handed to a single person via an override.
Refunds are a special case. Psalmly doesn't issue refunds itself —
they're done from the Stripe dashboard, and Psalmly only records
the result when Stripe sends the charge.refunded webhook. So there's
no in-app refund action to gate, and no per-user "can refund" control;
restrict refunds at the Stripe account level instead.
What's deferred
- Custom named roles ("Kids Coordinator" with a specific capability set as a reusable preset). The current model is per-user overrides; named-role presets are a future enhancement.
- Capability presets / bulk grants (e.g. "give every Sunday School teacher the kids capabilities at once"). For v1 you grant per-person.
- Per-resource scoping (e.g. "can only refund gifts < $500" or "can publish pages but not the homepage"). All capabilities are church-wide today.
More topics