---
title: Permission Metadata
description: Define access control with permission sets — object CRUD, field security, tab visibility, and row-level security
---

# Permission Metadata

A **Permission Set** defines what a user can do within the application. It controls object-level CRUD operations, field-level visibility, tab/app access, and row-level security policies.

## Basic Structure

```typescript
const salesUserPermission = {
  name: 'sales_user',
  label: 'Sales User',
  isProfile: false,

  objects: {
    account: {
      allowCreate: true,
      allowRead: true,
      allowEdit: true,
      allowDelete: false,
      viewAllRecords: false,
      modifyAllRecords: false,
    },
    opportunity: {
      allowCreate: true,
      allowRead: true,
      allowEdit: true,
      allowDelete: true,
      viewAllRecords: false,
      modifyAllRecords: false,
    },
    report: {
      allowCreate: false,
      allowRead: true,
      allowEdit: false,
      allowDelete: false,
      viewAllRecords: true,
      modifyAllRecords: false,
    },
  },

  fields: {
    account: {
      annual_revenue: { readable: true, editable: false },
      internal_notes: { readable: false, editable: false },
    },
  },

  tabPermissions: {
    crm: 'visible',
    admin: 'hidden',
    reports: 'default_on',
  },
};
```

## Permission Set Properties

| Property | Type | Required | Description |
| :--- | :--- | :--- | :--- |
| `name` | `string` | ✅ | Machine name (`snake_case`) |
| `label` | `string` | optional | Display label |
| `isProfile` | `boolean` | optional | Is this a user profile (vs. permission set) |
| `objects` | `Record<string, ObjectPermission>` | ✅ | Object-level permissions |
| `fields` | `Record<string, Record<string, FieldPermission>>` | optional | Field-level security |
| `systemPermissions` | `string[]` | optional | System-level capabilities |
| `tabPermissions` | `Record<string, TabVisibility>` | optional | Tab/app visibility |
| `rowLevelSecurity` | `RLSPolicy[]` | optional | Row-level security policies |
| `contextVariables` | `Record<string, any>` | optional | RLS context variables |

## Object Permissions

Control CRUD operations on each object:

```typescript
objects: {
  account: {
    allowCreate: true,         // Can create records
    allowRead: true,           // Can view records
    allowEdit: true,           // Can update records
    allowDelete: false,        // Can delete records
    allowTransfer: false,      // Can change record ownership
    allowRestore: false,       // Can restore deleted records
    allowPurge: false,         // Can permanently delete (GDPR)
    viewAllRecords: false,     // Bypass sharing rules for read
    modifyAllRecords: false,   // Bypass sharing rules for write
  },
}
```

| Permission | Description |
| :--- | :--- |
| `allowCreate` | Create new records |
| `allowRead` | View records (subject to sharing rules) |
| `allowEdit` | Update records (subject to sharing rules) |
| `allowDelete` | Soft-delete records |
| `allowTransfer` | Transfer record ownership |
| `allowRestore` | Restore records from trash |
| `allowPurge` | Permanently delete records (GDPR compliance) |
| `viewAllRecords` | View all records regardless of sharing rules |
| `modifyAllRecords` | Edit all records regardless of sharing rules |

## Field Permissions

Control visibility and editability of individual fields:

```typescript
fields: {
  // Object name → Field name → Permission
  account: {
    annual_revenue: { readable: true, editable: false },  // Read-only
    internal_notes: { readable: false, editable: false },  // Hidden
    name: { readable: true, editable: true },              // Full access
  },
  contact: {
    ssn: { readable: false, editable: false },             // Restricted
    salary: { readable: true, editable: false },           // Read-only
  },
}
```

| Permission | Description |
| :--- | :--- |
| `readable` | User can see the field value |
| `editable` | User can modify the field value |

**Note:** `editable: true` requires `readable: true`. A field that is not readable is completely hidden from the user.

## Tab Permissions

Control which apps/tabs are visible to users:

```typescript
tabPermissions: {
  crm: 'visible',         // Always shown
  admin: 'hidden',         // Never shown
  reports: 'default_on',   // Shown by default, user can hide
  analytics: 'default_off', // Hidden by default, user can show
}
```

| Visibility | Description |
| :--- | :--- |
| `visible` | Always visible, user cannot hide |
| `hidden` | Always hidden, user cannot show |
| `default_on` | Visible by default, user can toggle off |
| `default_off` | Hidden by default, user can toggle on |

## System Permissions

Grant system-level capabilities:

```typescript
systemPermissions: [
  'manage_users',
  'customize_application',
  'view_all_data',
  'modify_all_data',
  'manage_sharing',
  'export_data',
  'api_access',
  'manage_integrations',
]
```

## Row-Level Security

Define policies that restrict which records a user can access:

```typescript
rowLevelSecurity: [
  {
    name: 'own_records_only',
    object: 'opportunity',
    condition: "owner = {$currentUser.id}",
  },
  {
    name: 'same_department',
    object: 'account',
    condition: "department = {$currentUser.department}",
  },
]
```

### Context Variables

Provide dynamic values for RLS policies:

```typescript
contextVariables: {
  region: '{$currentUser.region}',
  department: '{$currentUser.department}',
  role: '{$currentUser.role}',
}
```

## Profile vs. Permission Set

| Aspect | Profile (`isProfile: true`) | Permission Set (`isProfile: false`) |
| :--- | :--- | :--- |
| **Assignment** | One profile per user | Multiple permission sets per user |
| **Purpose** | Baseline access level | Additional capabilities |
| **Example** | `standard_user`, `admin` | `sales_analytics`, `export_data` |

A user's effective permissions = **Profile permissions** ∪ **All assigned Permission Set permissions**.

## Complete Example

```typescript
const salesManagerPermission = {
  name: 'sales_manager',
  label: 'Sales Manager',
  isProfile: false,

  objects: {
    account: {
      allowCreate: true,
      allowRead: true,
      allowEdit: true,
      allowDelete: true,
      allowTransfer: true,
      allowRestore: true,
      allowPurge: false,
      viewAllRecords: true,
      modifyAllRecords: false,
    },
    opportunity: {
      allowCreate: true,
      allowRead: true,
      allowEdit: true,
      allowDelete: true,
      allowTransfer: true,
      allowRestore: true,
      allowPurge: false,
      viewAllRecords: true,
      modifyAllRecords: true,
    },
    contact: {
      allowCreate: true,
      allowRead: true,
      allowEdit: true,
      allowDelete: false,
      viewAllRecords: true,
      modifyAllRecords: false,
    },
  },

  fields: {
    account: {
      annual_revenue: { readable: true, editable: true },
      internal_rating: { readable: true, editable: true },
    },
    contact: {
      salary: { readable: true, editable: false },
    },
  },

  tabPermissions: {
    crm: 'visible',
    reports: 'visible',
    admin: 'hidden',
  },

  systemPermissions: [
    'export_data',
    'api_access',
  ],

  rowLevelSecurity: [
    {
      name: 'team_accounts',
      object: 'account',
      condition: "team = {$currentUser.team}",
    },
  ],
};
```

## Related

- [Object Metadata](./object) — Objects that permissions control access to
- [Field Metadata](./field) — Fields that field permissions control
- [App Metadata](./app) — Apps that tab permissions control visibility for
