Permission catalog
Synq has two permission scopes:
- System (
AdminPermission) — granted to Synq staff or system admins on a Synq deployment. Used for routes under/admin/.... - Organization (
IdentityOrgPermission) — granted to org members via roles. Used for routes under/identity/organizations/.../....
A user can be a system admin without being a member of any organization, and vice versa. They are independent.
Built-in org roles
Every org has three built-in roles:
| Role | Permissions | Notes |
|---|---|---|
| Owner | All permissions, including OwnershipTransfer and OrgDelete. | Exactly one per org. Transfer ownership to change. |
| Admin | Everything except OwnershipTransfer and OrgDelete. | Operational admin. |
| Member | Read-only across most surfaces. No Edit permissions, no API key management. | Default for invited users. |
Custom roles can be created via the org roles API with any subset of
IdentityOrgPermission.
IdentityOrgPermission
| Permission | What it grants |
|---|---|
org.read | Read the org metadata. Implied by every member. |
org.edit | Update org metadata (name, description, icon, etc.). |
org.delete | Soft-delete the org. Owner-only. |
org.members.view | List members, view audit log. |
org.members.edit | Invite, remove, change roles. Cannot self-demote below Admin. |
org.ownership.transfer | Transfer ownership. Current owner only. |
org.brands.view | List and read brands. |
org.brands.edit | Create, update, delete brands. |
org.apps.view | List and read apps under the brand. Sees clientId, not clientSecret. |
org.apps.edit | Create, update, delete apps. Rotate secrets. |
org.connections.view | List BYO connections (no plaintext config — ever). |
org.connections.edit | Configure BYO credentials. |
org.scopes.view | List brand-defined scopes. |
org.scopes.edit | Create, update, delete brand-defined scopes. |
org.webhooks.view | List webhooks and delivery logs. |
org.webhooks.edit | Create, update, delete webhooks. Rotate secrets. |
org.tokenGates.view | List token gates. |
org.tokenGates.edit | Create, update, delete token gates. |
org.billing.view | View invoices and current plan. |
org.billing.edit | Change plan, manage payment methods. |
org.users.view | Search users associated with this brand. |
org.apiKeys.view | List org-owned API keys. |
org.apiKeys.edit | Create, rotate, revoke org-owned API keys. |
Recommended custom role shapes
- Developer —
org.brands.view,org.apps.edit,org.scopes.edit,org.webhooks.edit. Cannot manage members or billing; can ship. - Billing —
org.billing.view,org.billing.edit. - Support —
org.members.view,org.users.view,org.brands.view,org.apps.view,org.connections.view,org.webhooks.view. Read-only across the board. - Security auditor —
org.members.view(for the audit log),org.connections.view,org.apiKeys.view. Read but cannot modify.
AdminPermission (system)
Most app developers will never see admin permissions. They exist for Synq operators.
| Permission | What it grants |
|---|---|
admin.users.view | Read any user record. |
admin.users.edit | Update or delete any user. Used for support. |
admin.orgs.view | Read any org. |
admin.orgs.edit | Update or delete any org. Manage plans. |
admin.plans.edit | Manage Stripe products and tiers. |
admin.roles.edit | Manage built-in role definitions. |
Permission checking in your backend
Permissions appear in the access token’s scope claim only when
your App explicitly requested them. Most product apps don’t request
org-permission scopes (they’re dashboard concerns, not user-app
concerns).
If your app is a dashboard or admin tool, request the permissions you need:
scope=openid profile email org.members.view org.brands.editThen check in your backend:
function requireOrgPermission(perm: string) {
return (req, res, next) => {
const granted = (req.user.scope ?? '').split(' ')
if (!granted.includes(perm)) {
return res.status(403).json({
error: { code: 'forbidden', message: 'missing permission', details: { missing: perm } },
})
}
next()
}
}
app.post(
'/api/admin/brands',
authMiddleware,
requireOrgPermission('org.brands.edit'),
createBrandHandler,
)Inheriting org permissions through API keys
An org API key inherits the scopes its creator was granted. When you
create an API key, you can restrict it further by passing a scopes
array; the key will be capped by the smaller of (creator’s permissions,
explicit scope list).
This lets you give a CI bot a subset of an admin’s permissions without making the bot itself an admin.
Mental model summary
- Org permissions live in the access token’s
scopeclaim. Your backend reads them and gates routes. - Most product apps don’t request org permissions — those are dashboard concerns.
- Custom roles combine any subset of org permissions.
- API keys inherit their creator’s permissions, capped by an
optional explicit
scopesarray. - System (
admin.*) permissions are for Synq operators, not app developers.