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 when condition for ownership or environment.
  • Use /permissions/check-batch for list pages that need multiple decisions.
  • Open the permission state endpoint for a user to see materialized access:
GET /users/user-alice/perm-state