Skip to content

Permissions

Hailer controls access to workspaces, workflows, activities, and features through a layered permission system. Understanding it is essential for building permission-aware integrations - and for predicting whether a call will succeed before you make it.

The layers

Permissions resolve through several interconnected layers:

  1. Workspace roles - base access level within a workspace (owner, admin, user, guest, inviter).
  2. Custom roles - granular permission sets built from specific permits.
  3. Workflow permissions - access to a workflow and its activities (admin, any, ownteam, own, none, guest).
  4. Phase permissions - access within a workflow phase (edit, view, none).
  5. Team membership - team-based access that intersects with the levels above.

Workspace roles

RoleWhat it grants
OwnerHighest privilege. Can delete the workspace and manage billing. Cannot be removed while sole owner.
AdminUser and workspace management. Cannot delete the workspace or manage billing. Full access to workflows, insights, and apps unless restricted.
UserStandard member. Creates and manages own content; access to workflows/activities via teams and workflow settings.
GuestLimited, often read-only access. Cannot reach most administrative features. Used for external collaborators.
InviterCan invite new users, with little other administrative privilege.

Workflow permissions

Applied per workflow, controlling access to its activities:

LevelMeaning
adminFull control over the workflow and every activity; overrides team and ownership restrictions.
anyAccess to all activities regardless of ownership (subject to phase permissions).
ownteamAccess limited to activities owned by teams the user belongs to.
ownAccess only to activities the user created or owns.
noneNo access; the workflow and its activities are hidden.
guestLimited, usually read-only guest access.

Phase permissions

Within a workflow, each phase refines access further:

LevelMeaning
editCan modify activities in the phase and move them (if allowed).
viewRead-only: can see activities and discussions, cannot modify.
noneActivities in the phase are hidden.

Custom role permits

Custom roles grant capabilities through named permits. Common ones:

  • Feed - feed.manage, feed.interact, feed.load
  • Discussion - discussion.start.private.any, discussion.start.private.publicTeam, discussion.message.add, discussion.message.add.attachment, discussion.message.remove.own, discussion.message.remove.others, discussion.read.history, discussion.manage, discussion.leave
  • Teams and groups - team.load, group.load, group.share, group.loadShared
  • Views - view.calendar, view.activities, view.apps, view.people

Manage custom roles and their permits with v3.network.role.*.

How permissions resolve

For a given action, Hailer checks, in order:

  1. Workspace role - owner / admin / user / guest / inviter?
  2. Custom role permits - which specific permits does the user hold?
  3. Workflow access - admin / any / ownteam / own for the workflow?
  4. Phase permission - edit / view / none in the relevant phase?
  5. Team membership - is the user on a team that owns the activity (for ownteam)?
  6. Activity ownership - does the user own the activity (for own)?

Higher roles inherit the capabilities of lower ones, and custom-role permits can grant access beyond the base role.

Checking permissions via the API

v3.activity.permissions returns a user's resolved permissions for a workspace:

javascript
const permissions = await client.request('v3.activity.permissions', [
  userId,        // user to check
  workspaceId,   // workspace context
  {
    details: true,                              // include detailed permission paths
    items: { workflowIds: ['workflowId'] },     // optionally scope to specific resources
  },
]);

const workspace = permissions[workspaceId];

// Workspace role
console.log('Is admin:', workspace.workspace.isAdmin);
console.log('Is owner:', workspace.workspace.isOwner);

// Workflow + phase permission
const workflow = workspace.workflow['workflowId'];
console.log('Workflow access:', workflow.hasAccess, workflow.permission);
console.log('Can edit phase:', workflow.phases['phaseId']?.permission === 'edit');

Pre-flight checks

Check before you act, rather than catching a permission error after the fact:

javascript
async function canEditActivity(userId, workspaceId, workflowId, phaseId) {
  const perms = await client.request('v3.activity.permissions', [userId, workspaceId,
    { items: { workflowIds: [workflowId] } }]);
  const workflow = perms[workspaceId].workflow[workflowId];
  return workflow?.hasAccess
    && ['admin', 'any', 'ownteam', 'own'].includes(workflow.permission)
    && workflow.phases?.[phaseId]?.permission === 'edit';
}

Team-based (ownteam) access

javascript
async function canAccessTeamActivity(userId, workspaceId, activity) {
  const perms = await client.request('v3.activity.permissions', [userId, workspaceId]);
  const workflow = perms[workspaceId].workflow[activity.process];

  if (['admin', 'any'].includes(workflow.permission)) return true;
  if (workflow.permission === 'ownteam') return perms[workspaceId].teams.includes(activity.team_account?.team);
  if (workflow.permission === 'own') return activity.uid === userId;
  return false;
}

Best practices

  • Enforce on the server. Client-side checks improve UX but are never a security boundary - the backend enforces permissions on every call regardless.
  • Cache briefly. Permission results are stable over short windows; caching for a few minutes cuts calls. Invalidate when roles or team memberships change.
  • Scope your checks. Pass items to v3.activity.permissions to resolve only the workflows or calendars you care about.
  • Least privilege. Request and grant only the access an integration actually needs.

Hailer Developer Documentation