Quickstart
This guide builds a minimal model:
- One organization
- One workspace
- One user
- One role
- One permission check
The examples use HTTP-style requests. Your deployment URL and authentication setup may differ.
1. Create an organization
POST /orgs
Content-Type: application/json
{
"id": "org-acme",
"name": "Acme"
}Organizations are the top-level customer boundary. In most SaaS products, this maps to an account, company, or customer.
2. Create a workspace
POST /workspaces
Content-Type: application/json
{
"id": "ws-acme-prod",
"orgId": "org-acme",
"name": "Acme Production"
}Workspaces are the most common scope for application access. A workspace is a subdivision inside an organization, such as an environment, project, or product account.
3. Create a user
POST /users
Content-Type: application/json
{
"id": "user-alice",
"email": "alice@example.com",
"name": "Alice"
}SaaSLayer Permissions lets you use caller-owned IDs for domain entities, so the user ID can match your auth provider or application database.
4. Add the user to the workspace
POST /workspaces/ws-acme-prod/members
Content-Type: application/json
{
"type": "user",
"id": "user-alice"
}Membership says where a principal belongs. Roles say what that principal can do.
5. Create a role
POST /roles
Content-Type: application/json
{
"id": "role-device-viewer",
"permissions": [
{
"effect": "can",
"action": "read",
"resource": "device",
"service": "device-service"
}
]
}Roles are permission bundles. Keep role names close to product responsibilities, not job titles. device-viewer is usually more reusable than intern.
6. Assign the role at workspace scope
POST /users/user-alice/roles
Content-Type: application/json
{
"roleId": "role-device-viewer",
"scope": "ws-acme-prod"
}Scoped role assignments grant the role only inside that scope. A global assignment has no scope.
7. Check a permission
POST /permissions/check
Content-Type: application/json
{
"userId": "user-alice",
"action": "read",
"resource": "device",
"service": "device-service",
"scope": "ws-acme-prod"
}Example response:
{
"data": {
"type": "permission-check",
"id": "user-alice:read:device",
"attributes": {
"allowed": true,
"effect": "can",
"reason": null,
"sources": []
}
}
}Next steps
After the first check works:
- Add a deny permission and confirm deny-overrides-allow behavior.
- Add a policy with a
whencondition for ownership or environment. - Use
/permissions/check-batchfor list pages that need multiple decisions. - Open the permission state endpoint for a user to see materialized access:
GET /users/user-alice/perm-state