> ## Documentation Index
> Fetch the complete documentation index at: https://conductorone-hunner-patch-1.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# CEL expressions reference

> Complete reference for CEL expression objects, functions, and usage contexts in C1.

## Type definitions

CEL expressions use typed values. Understanding these types helps you write correct expressions and avoid runtime errors.

### Primitive types

| Type | Description | Example values |
| :- | :- | :- |
| `string` | Text value | `"Engineering"`, `"user@company.com"` |
| `bool` | Boolean true/false | `true`, `false` |
| `int` | Integer number | `0`, `42`, `-1` |
| `double` | Floating-point number | `3.14`, `0.5` |
| `bytes` | Binary data | Rarely used directly |

### Time types

| Type | Description | How to create |
| :- | :- | :- |
| `timestamp` | A point in time (UTC) | `now()`, `timestamp("2025-01-01T00:00:00Z")`, `time.parse(...)` |
| `duration` | A length of time | `duration("24h")`, `duration("30m")`, `duration("720h")` |

**Duration format:** Use `h` for hours, `m` for minutes, `s` for seconds. Examples: `"2h"` (2 hours), `"30m"` (30 minutes), `"720h"` (30 days).

**Timestamp arithmetic:**

```go theme={null}
now() + duration("24h")              // 24 hours from now
now() - duration("720h")             // 30 days ago
timestamp1 - timestamp2              // Returns a duration
```

### Collection types

| Type | Description | Example |
| :- | :- | :- |
| `list<T>` | Ordered list of items | `[user1, user2]`, `["a", "b", "c"]` |
| `map<K,V>` | Key-value mapping | `subject.profile` (map of string to any) |

**List operations:**

```go theme={null}
size(myList)                         // Number of items
myList[0]                            // First item (0-indexed)
"value" in myList                    // Check membership
myList + otherList                   // Concatenate lists
myList.filter(x, x.status == UserStatus.ENABLED)  // Filter
myList.map(x, x.email)               // Transform
myList.exists(x, x.department == "IT")  // Any match?
myList.all(x, x.status == UserStatus.ENABLED)     // All match?
```

### Enum types

Enums are predefined constants. Always use the full enum name (e.g., `UserStatus.ENABLED`, not just `ENABLED`).

#### UserStatus

| Value | Meaning |
| :- | :- |
| `UserStatus.ENABLED` | User is active |
| `UserStatus.DISABLED` | User is disabled |
| `UserStatus.DELETED` | User is deleted |

#### UserType

| Value | Meaning |
| :- | :- |
| `UserType.HUMAN` | Regular human user |
| `UserType.AGENT` | Automated agent |
| `UserType.SERVICE` | Service account |
| `UserType.SYSTEM` | System account |

#### TaskOrigin

| Value | Meaning |
| :- | :- |
| `TaskOrigin.WEBAPP` | Created in C1 web interface |
| `TaskOrigin.SLACK` | Created via Slack integration |
| `TaskOrigin.API` | Created via API |
| `TaskOrigin.JIRA` | Created via Jira integration |
| `TaskOrigin.COPILOT` | Created via Copilot |
| `TaskOrigin.PROFILE_MEMBERSHIP_AUTOMATION` | Created by automation |
| `TaskOrigin.TIME_REVOKE` | Created by time-based revocation |

#### AppUserStatus (for triggers)

Used in `ctx.trigger.oldAccount.status.status` and `ctx.trigger.newAccount.status.status`:

| Value | Meaning |
| :- | :- |
| `APP_USER_STATUS_ENABLED` | Account is active |
| `APP_USER_STATUS_DISABLED` | Account is disabled |
| `APP_USER_STATUS_DELETED` | Account is deleted |

#### AppUserType

| Value | Meaning |
| :- | :- |
| `AppUserType.SERVICE_ACCOUNT` | Service account (non-human identity used by programs) |
| `AppUserType.SYSTEM_ACCOUNT` | System account (infrastructure or system-level account) |
| `AppUserType.USER` | Human user account |

#### TimeFormat

| Constant | Format | Example output |
| :- | :- | :- |
| `TimeFormat.RFC3339` | ISO 8601 / RFC 3339 | `2025-10-22T14:30:00Z` |
| `TimeFormat.DATE` | Date only | `2025-10-22` |
| `TimeFormat.DATETIME` | Date and time | `2025-10-22 14:30:00` |
| `TimeFormat.TIME` | Time only | `14:30:00` |

### Object types

These are complex types returned by functions or available as variables.

<Note>
  **User vs AppUser:** These are different types. A `User` is a person in the C1 directory (your identity provider sync). An `AppUser` is that person's account within a specific application (their GitHub account, Okta account, etc.). One User can have many AppUsers across different apps.
</Note>

#### User

Represents a person in the C1 directory. This is your organization's user record, typically synced from an identity provider.

**Returned by:** `FindByEmail`, `GetByID`, `GetManagers`, `DirectReports`, `GetEntitlementMembers`

**Available as:** `subject`, elements of `appOwners`, `ctx.trigger.oldUser`, `ctx.trigger.newUser`

| Field | Type | Description |
| :- | :- | :- |
| `id` | string | Unique user identifier |
| `email` | string | Primary email address |
| `emails` | list\<string> | All email addresses |
| `displayName` | string | Display name |
| `username` | string | Username |
| `usernames` | list\<string> | All usernames |
| `department` | string | Department |
| `jobTitle` | string | Job title |
| `employmentType` | string | Employment type (e.g., "Full Time") |
| `employmentStatus` | string | Employment status (e.g., "Active") |
| `status` | UserStatus | User status enum |
| `directoryStatus` | UserStatus | Directory sync status |
| `type` | UserType | User type enum |
| `manager` | string | Manager's email |
| `manager_id` | string | Manager's user ID |
| `profile` | map | Custom profile attributes |
| `attributes` | map | Custom user attributes |

#### Group

Represents a C1 group. Returned by `FindByName`.

| Field | Type | Description |
| :- | :- | :- |
| `id` | string | Group identifier |
| `app_id` | string | App ID the group belongs to |
| `display_name` | string | Group display name |

#### AppUser

Represents a user's account within a specific connected application (e.g., their GitHub account, Salesforce account, AWS IAM user). Different from `User` which is the directory-level identity.

**Returned by:** `ListAppUsersForUser`

**Available as:** `ctx.trigger.oldAccount`, `ctx.trigger.newAccount` (in account-change triggers)

| Field | Type | Description |
| :- | :- | :- |
| `id` | string | App user identifier |
| `displayName` | string | Display name |
| `username` | string | Username in the app |
| `usernames` | list\<string> | All usernames |
| `email` | string | Email in the app |
| `emails` | list\<string> | All emails |
| `employeeIds` | list\<string> | Employee IDs |
| `app_user_type` | AppUserType | Account type (service account, system account, or user) |
| `status` | AppUserStatus | Nested status object |
| `status.status` | enum | APP\_USER\_STATUS\_ENABLED/DISABLED/DELETED |
| `status.details` | string | Status details |
| `profile` | map | Custom profile attributes |
| `attributes` | map | Attribute mappings |

#### Task

Represents an access request or task in C1. See [Task object](#task-object) for all fields.

**Available as:** `task` (in policy expressions)

#### TaskAnalysis

Analysis data attached to a task, including access conflict information. See [Task analysis object](#task-analysis-object) for all fields.

**Available as:** `task.analysis` (in policy expressions)

#### Entitlement

The entitlement (permission/role) being requested. See [Entitlement object](#entitlement-object) for all fields.

**Available as:** `entitlement` (in policy expressions and [entitlement configuration rules](/product/admin/entitlement-config-rules); in configuration rules for cloud infrastructure apps, `entitlement.role` and `entitlement.scope` are also populated)

#### IP

Represents an IP address with properties for classification. See [IP address object](#ip-address-object) for all fields.

**Created by:** `ip("1.2.3.4")`

#### CIDR

Represents a network range for IP matching. See [IP CIDR object](#ip-cidr-object) for all methods.

**Created by:** `cidr("10.0.0.0/8")`

#### Context (ctx)

Workflow execution context containing trigger data and step outputs. See [Context object](#context-object-ctx) for all fields.

**Available as:** `ctx` (in automation triggers and workflow steps)

### Built-in variables

These variables are automatically available in specific contexts.

| Variable | Type | Available in | Description |
| :- | :- | :- | :- |
| `subject` | User | Policies, Groups, Automations, Campaigns, Account provisioning | The current user being evaluated |
| `account` | AppUser | Policies only | The application user (account) associated with the access request |
| `task` | Task | Policies only | The current access request |
| `entitlement` | Entitlement | Policies only | The entitlement being requested |
| `appOwners` | list\<User> | Policy step approvers only | Owners of the application |
| `ctx` | Context | Automations only | Workflow context and trigger data |
| `ip` | IP | Policies, Automations | Requestor's IP address (when available) |

***

## Functions

These library functions let you interact with the C1 system to look up whether a user has access to a certain application or entitlement, or to find the user or list of users who should review a task.

### User library functions

| Function | Accepts | Returns | Availability |
| :- | :- | :- | :- |
| `c1.user.v1.HasApp` | user, app ID | Boolean | Policies, Groups, Automations, Account provisioning |
| `c1.user.v1.HasEntitlement` | user, app ID, entitlement ID | Boolean | Policies, Groups, Automations, Account provisioning |
| `c1.user.v1.GrantedFromEnrollment` | user, app ID, entitlement ID | Boolean | Policy conditions only |
| `c1.user.v1.AutomaticallyGrantedFromEnrollment` | user, app ID, entitlement ID | Boolean | Policy conditions only |
| `c1.user.v1.GetAppUserStatus` | user, app ID | String | Policies, Groups, Automations, Access Reviews, Account provisioning, User attribute mappings |
| `c1.user.v1.ListAppUsersForUser` | user, app ID | List of AppUser | Automations, Account provisioning |

**What can go wrong:**

* Invalid app ID or entitlement ID returns `false` for boolean functions (no error thrown)
* `GetAppUserStatus` returns `"USER_STATUS_UNSPECIFIED"` if the user has no account in the app, the source app hasn't completed a successful sync, or the source attribute is empty — handle that branch explicitly rather than relying on a default
* `ListAppUsersForUser` returns empty list `[]` if user has no accounts in the app

### Directory library functions

| Function | Accepts | Returns | Availability |
| :- | :- | :- | :- |
| `c1.directory.users.v1.FindByEmail` | email | user | Policies, Groups, Automations, Account provisioning |
| `c1.directory.users.v1.FindByName` | display name | user | Policies, Groups, Automations, Account provisioning |
| `c1.directory.users.v1.GetByID` | user ID | user | Policies, Groups, Automations, Account provisioning |
| `c1.directory.users.v1.GetManagers` | user | list of users | Policies, Groups, Automations, Account provisioning |
| `c1.directory.users.v1.DirectReports` | user or list of users | list of users | Policies, Groups, Automations, Account provisioning |
| `c1.directory.groups.v1.FindByName` | group name | group | Policies, Groups, Automations, Account provisioning |
| `c1.directory.apps.v1.GetEntitlementMembers` | app ID, entitlement ID | list of users | Policies, Groups, Automations, Account provisioning |
| `c1.directory.apps.v1.GetEntitlementOwners` | app ID + entitlement ID, or entitlement | list of users | Policies |

**What can go wrong:**

* `FindByEmail` fails if email doesn't exist in directory - verify emails before deploying
* `FindByName` returns the first matching user if multiple users share the same display name - use with caution when names may not be unique
* `GetByID` fails if user ID doesn't exist
* `GetManagers` returns empty list `[]` if user has no manager - **this silently skips approval steps**
* `DirectReports` returns empty list `[]` if user has no reports
* `GetEntitlementMembers` returns empty list `[]` if entitlement has no members
* `GetEntitlementOwners` returns empty list `[]` if the entitlement has no owners — add a fallback to prevent silently skipping the approval step

<Warning>
  **Critical:** When `GetManagers` returns an empty list in a policy approver expression, the approval step is silently skipped rather than failing. Always add a fallback approver:

  ```go theme={null}
  // DANGER without fallback: step skipped if no manager
  c1.directory.users.v1.GetManagers(subject)

  // SAFE with fallback: uses app owners if no manager
  size(c1.directory.users.v1.GetManagers(subject)) > 0
    ? c1.directory.users.v1.GetManagers(subject)
    : appOwners
  ```
</Warning>

<Tip>
  Go to an application or entitlement's details page to look up its ID, or use [Cone](/product/cli/install/).
</Tip>

<Note>
  **Function availability varies by context.**

  Note that automation triggers do NOT have access to directory or user library functions - they can only access the `ctx.trigger` object and basic enums.
</Note>

### Time functions

C1 provides comprehensive time functions for working with dates and times in CEL expressions. These functions are available in **all CEL contexts** (Policies, Groups, Automations, Account provisioning).

#### Core time functions

| Function | Description | Example |
| :- | :- | :- |
| `now()` | Get current timestamp | `now() > timestamp("2025-01-01T00:00:00Z")` |
| `time.parse(value, layout)` | Parse time from string (UTC) | `time.parse("2025-10-22", TimeFormat.DATE)` |
| `time.parse(value, layout, timezone)` | Parse time with timezone | `time.parse("2025-10-22 14:30:00", TimeFormat.DATETIME, "America/New_York")` |
| `time.format(timestamp, layout)` | Format timestamp to string (UTC) | `time.format(now(), TimeFormat.RFC3339)` |
| `time.format(timestamp, layout, timezone)` | Format with timezone | `time.format(now(), "Monday", "America/New_York")` |
| `time.unix(seconds)` | Convert Unix timestamp (seconds) | `time.unix(1734787200)` |
| `time.unix_ms(milliseconds)` | Convert Unix milliseconds | `time.unix_ms(1734787200123)` |
| `time.start_of(timestamp, unit)` | Get start of period | `time.start_of(now(), "day")` |
| `time.start_of(timestamp, unit, timezone)` | Get start of period in timezone | `time.start_of(now(), "week", "America/New_York")` |
| `time.end_of(timestamp, unit)` | Get end of period | `time.end_of(now(), "month")` |

**Time units for start\_of/end\_of:**

* `"day"` - Start/end of day (midnight)
* `"week"` - Start/end of week (Monday/Sunday)
* `"month"` - Start/end of month
* `"quarter"` - Start/end of quarter (Jan/Apr/Jul/Oct)
* `"year"` - Start/end of year

#### TimeFormat constants

See [TimeFormat enum](#timeformat) for all constants. You can also use custom Go time layouts like `"2006-01-02"` or `"Monday, January 2, 2006"`.

#### Common time patterns

**Check if date is recent:**

```go theme={null}
// Check if user's hire date is within last 30 days (if stored in profile)
has(subject.profile.hire_date) &&
time.parse(subject.profile.hire_date, TimeFormat.DATE) > now() - duration("720h")

// Account modified this month (in automations)
has(ctx.trigger.newAccount.updated_at) &&
timestamp(ctx.trigger.newAccount.updated_at) >= time.start_of(now(), "month")
```

**Business hours checking:**

```go theme={null}
// Check if it's business hours (9 AM - 5 PM ET)
time.format(now(), "15", "America/New_York") >= "09" &&
time.format(now(), "15", "America/New_York") < "17"

// Check if it's after hours
time.format(now(), "15", "America/New_York") >= "17" ||
time.format(now(), "15", "America/New_York") < "09"
```

**Day of week checking:**

```go theme={null}
// Check if it's a weekend
time.format(now(), "Monday", "America/New_York") == "Saturday" ||
time.format(now(), "Monday", "America/New_York") == "Sunday"

// Check if it's a weekday
!(time.format(now(), "Monday") == "Saturday" || time.format(now(), "Monday") == "Sunday")
```

**Parsing profile date fields:**

```go theme={null}
// Parse contract end date and check if within 30 days
has(subject.profile.contract_end_date) &&
time.parse(subject.profile.contract_end_date, TimeFormat.DATE) - now() < duration("720h")

// Check if hire date is in the past
has(subject.profile.hire_date) &&
time.parse(subject.profile.hire_date, TimeFormat.DATE) < now()
```

**Working with timezones:**

```go theme={null}
// Start of day in company timezone
time.start_of(now(), "day", "America/New_York")

// Format date in user's timezone
time.format(now(), TimeFormat.DATETIME, "Europe/London")
```

<Warning>
  **Important:**

  The `now()` function returns the same value throughout a single expression evaluation. All timestamps are stored in UTC internally; timezone parameters only affect parsing and formatting.
</Warning>

### External insights functions

These functions are available in policy conditions when [external insights](/product/admin/external-insights) are configured. They let you reference security findings and risk scores from connected tools directly in access decisions.

#### Issue insights

Issue insights are discrete security findings (for example, critical CVEs or misconfigurations) synced from Wiz Insights.

| Function | Accepts | Returns | Availability |
| :- | :- | :- | :- |
| `c1.app_user.v1.HasSecurityInsight(account)` | account | `bool` | Policy conditions |
| `c1.app_user.v1.GetSecurityInsights(account, source)` | account, source name | `list` | Policy conditions |
| `c1.app_user.v1.HasSecurityInsightWithSeverity(account, source, severity)` | account, source name, severity | `bool` | Policy conditions |

Severity values: `"CRITICAL"`, `"HIGH"`, `"MEDIUM"`, `"LOW"`, `"INFORMATIONAL"`. The `HasSecurityInsightWithSeverity` comparison is case-insensitive; direct field comparisons are not.

#### Risk scores

Risk scores are normalized values (0–100, higher = more risk) synced from CrowdStrike.

| Function | Accepts | Returns | Availability |
| :- | :- | :- | :- |
| `c1.app_user.v1.HasRiskScore(account)` | account | `bool` | Policy conditions |
| `c1.app_user.v1.GetRiskScore(account, source)` | account, source name | `c1.risk_score.v1` | Policy conditions |

The source name must exactly match the connector app's display name in your tenant (for example, `"Wiz Insights"` or `"CrowdStrike"`). The match is case-sensitive.

For full examples and best practices, see [Use external insights in CEL policy conditions](/product/admin/external-insights#use-external-insights-in-cel-policy-conditions).

## Objects

### Subject object

The "subject" variable refers to the C1 user.

#### Most common fields

These fields are used in the majority of condition expressions:

| Field | Data type | Description | Example usage | Common use cases |
| :- | :- | :- | :- | :- |
| `subject.email` | string | User's email address | `subject.email.endsWith("@company.com")` | Distinguish between employees and contractors |
| `subject.department` | string | User's department | `subject.department == "Engineering"` | Department-based access control |
| `subject.status` | enum | User's active status | `subject.status == UserStatus.ENABLED` | Check if user is active |
| `subject.type` | enum | User type (human, service, system) | `subject.type == UserType.HUMAN` | Filter by user type |

#### Identity and contact fields

| Field | Data type | Description | Example usage | Common use cases |
| :- | :- | :- | :- | :- |
| `subject.id` | string | Unique user identifier | `subject.id == "user123"` | Specific user targeting |
| `subject.email.startsWith` | string | Email prefix check | `subject.email.startsWith("admin")` | Admin user identification |
| `subject.email.endsWith` | string | Email domain check | `subject.email.endsWith("@company.com")` | Employee vs contractor |
| `subject.manager` | string | User's manager email | `subject.manager == "boss@company.com"` | Manager-based routing |
| `subject.manager_id` | string | User's manager ID | `subject.manager_id != ""` | Check if user has manager |

<Note>
  Both `subject.manager` (email) and `subject.manager_id` (ID) are available. Use `manager` for email-based comparisons and `manager_id` to check existence or for ID-based lookups with `GetByID`.
</Note>

#### Organizational fields

| Field | Data type | Description | Example usage | Common use cases |
| :- | :- | :- | :- | :- |
| `subject.jobTitle` | string | User's job title | `subject.jobTitle == "Senior Developer"` | Role-based access |
| `subject.employmentType` | string | Employment type | `subject.employmentType == "Full Time"` | Employment status checks |
| `subject.employmentStatus` | string | Employment status | `subject.employmentStatus == "Active"` | Active employee verification |

#### Status and directory fields

| Field | Data type | Description | Example usage | Common use cases |
| :- | :- | :- | :- | :- |
| `subject.directoryStatus` | enum | Directory sync status | `subject.directoryStatus == UserStatus.ENABLED` | Directory health checks |
| `subject.profile` | map\[string]interface{} | Profile attributes | `subject.profile.department == "IT"` | Custom profile data access |
| `subject.attributes.<CUSTOM_USER_ATTRIBUTE>` | varies | Custom user attributes | `subject.attributes.contractor == "true"` | Custom business logic |

<Tip>
  **Use custom user attributes.**

  You can write condition expressions that leverage the custom user attributes you've set up in C1. Any [custom user attribute](/product/admin/attributes) can be passed in to the `subject.attributes.<CUSTOM USER ATTRIBUTE>` property and used in your condition expressions.
</Tip>

### User object

The user object is used when referencing other users in the system (not the current subject).

| Field | Data type | Description | Example usage | Common use cases |
| :- | :- | :- | :- | :- |
| `user.id` | string | Unique user identifier | `user.id == "user123"` | Specific user targeting |
| `user.email` | string | User's email address | `user.email.endsWith("@company.com")` | Email-based filtering |
| `user.displayName` | string | User's display name | `user.displayName == "John Doe"` | Name-based identification |
| `user.username` | string | User's username | `user.username == "johndoe"` | Username-based filtering |
| `user.department` | string | User's department | `user.department == "Engineering"` | Department-based grouping |
| `user.jobTitle` | string | User's job title | `user.jobTitle == "Manager"` | Role-based filtering |
| `user.status` | UserStatus enum | User's active status | `user.status == UserStatus.ENABLED` | Active user verification |
| `user.directoryStatus` | UserStatus enum | Directory sync status | `user.directoryStatus == UserStatus.ENABLED` | Directory health checks |
| `user.employmentType` | string | Employment type | `user.employmentType == "Full Time"` | Employment status filtering |
| `user.employmentStatus` | string | Employment status | `user.employmentStatus == "Active"` | Active employee verification |
| `user.profile` | JSON | Profile attributes | `user.profile.department == "IT"` | Custom profile data access |

### Task object

The task object is used in policy expressions to reference the current access request or task.

| Field | Data type | Description | Example usage | Common use cases |
| :- | :- | :- | :- | :- |
| `task.id` | string | Unique task identifier | `task.id == "task123"` | Specific task targeting |
| `task.numericId` | string | Numeric task identifier | `task.numericId == "12345"` | Numeric task reference |
| `task.displayName` | string | Human-readable task name | `task.displayName == "Access Request"` | Task identification |
| `task.origin` | TaskOrigin enum | Where the task was created | `task.origin == TaskOrigin.SLACK` | Route based on creation source |
| `task.isGrantPermanent` | Boolean | Whether access is permanent | `task.isGrantPermanent == true` | Permanent vs temporary access |
| `task.grantDuration` | duration | How long access is granted | `task.grantDuration > duration("2h")` | Time-based access control |
| `task.subjectUserId` | string | ID of the user who is the subject of the task | `task.subjectUserId == task.requestorUserId` | Self-service access checks |
| `task.requestorUserId` | string | ID of the user who created the task | `task.requestorUserId == "user123"` | Requestor-based routing |
| `task.created_at` | timestamp | When the task was created | `task.created_at > now() - duration("24h")` | Time-based routing, after-hours handling |
| `task.analysis` | analysis | Task analysis data | `task.analysis.hasConflictViolations` | Access conflict detection |

See [TaskOrigin enum](#taskorigin) for all possible values.

### Task analysis object

The task analysis object provides information about potential access conflicts and other analysis data.

| Field | Data type | Description | Example usage | Common use cases |
| :- | :- | :- | :- | :- |
| `task.analysis.id` | string | Analysis identifier | `task.analysis.id == "analysis123"` | Specific analysis reference |
| `task.analysis.hasConflictViolations` | Boolean | Whether conflicts exist | `task.analysis.hasConflictViolations` | Check for access conflicts |
| `task.analysis.conflictViolations` | array of strings | List of conflict IDs | `"conflict123" in task.analysis.conflictViolations` | Specific conflict detection |

### Entitlement object

The `entitlement` object can only be used in CEL expressions in policies. It does not work when writing CEL expressions to form C1 groups.

| Field | Data type | Description | Example usage | Common use cases |
| :- | :- | :- | :- | :- |
| `entitlement.id` | string | Entitlement identifier | `entitlement.id == "entitlement123"` | Specific entitlement reference |
| `entitlement.appId` | string | Application identifier | `entitlement.appId == "app123"` | Application-specific logic |
| `entitlement.risk_level_value_id` | string | Risk level attribute value ID | `entitlement.risk_level_value_id == "high"` | Risk-based approval workflows |

### IP address object

The IP address object is used for network-based access control and filtering.

| Field | Data type | Description | Example usage | Common use cases |
| :- | :- | :- | :- | :- |
| `ip.is4` | Boolean | Whether IP is IPv4 | `ip.is4` | IPv4-specific logic |
| `ip.is6` | Boolean | Whether IP is IPv6 | `ip.is6` | IPv6-specific logic |
| `ip.isPrivate` | Boolean | Whether IP is private | `ip.isPrivate` | Internal network access |
| `ip.isLoopback` | Boolean | Whether IP is loopback | `ip.isLoopback` | Localhost detection |
| `ip.isGlobalUnicast` | Boolean | Whether IP is global unicast | `ip.isGlobalUnicast` | Public IP detection |
| `ip.isMulticast` | Boolean | Whether IP is multicast | `ip.isMulticast` | Multicast traffic detection |
| `ip.isInterfaceLocalMulticast` | Boolean | Whether IP is interface local multicast | `ip.isInterfaceLocalMulticast` | Local multicast detection |
| `ip.isLinkLocalMulticast` | Boolean | Whether IP is link local multicast | `ip.isLinkLocalMulticast` | Link local multicast detection |
| `ip.isUnspecified` | Boolean | Whether IP is unspecified | `ip.isUnspecified` | Invalid IP detection |

### IP CIDR object

The IP CIDR object is used for network range-based access control and filtering.

| Function | Description | Example usage | Common use cases |
| :- | :- | :- | :- |
| `cidr('10.1.2.0/24').contains(ip('1.2.3.4'))` | Check if IP is within CIDR range | `cidr('10.1.2.0/24').contains(ip('10.1.2.5'))` | Network-based access control |
| `ip('10.1.2.5').within(cidr('10.1.2.0/24'))` | Check if IP is within CIDR range | `ip('10.1.2.5').within(cidr('10.1.2.0/24'))` | Alternative syntax for range checking |
| `cidr('10.1.2.0/24', '5.4.3.2/32')` | Create CIDR range with multiple networks | `cidr('10.1.2.0/24', '5.4.3.2/32')` | Multiple network range definition |

## Object usage reference

Understanding where each object can be used helps you write effective CEL expressions for different C1 features.

### Quick object reference table

| Object | Policies | Groups | Automations | Campaigns | Account provisioning |
| :- | :- | :- | :- | :- | :- |
| subject | <Icon icon="square-check" iconType="solid" color="#c937ae" /> | <Icon icon="square-check" iconType="solid" color="#c937ae" /> | <Icon icon="square-check" iconType="solid" color="#c937ae" />\* | <Icon icon="square-check" iconType="solid" color="#c937ae" /> | <Icon icon="square-check" iconType="solid" color="#c937ae" /> |
| user | <Icon icon="square-check" iconType="solid" color="#c937ae" /> | <Icon icon="square-check" iconType="solid" color="#c937ae" /> | <Icon icon="square-check" iconType="solid" color="#c937ae" />\* | <Icon icon="square-check" iconType="solid" color="#c937ae" /> | <Icon icon="circle-minus" iconType="solid" color="red" />️ |
| task | <Icon icon="square-check" iconType="solid" color="#c937ae" /> | <Icon icon="circle-minus" iconType="solid" color="red" />️ | <Icon icon="circle-minus" iconType="solid" color="red" />️ | <Icon icon="circle-minus" iconType="solid" color="red" />️ | <Icon icon="circle-minus" iconType="solid" color="red" />️ |
| task.analysis | <Icon icon="square-check" iconType="solid" color="#c937ae" /> | <Icon icon="circle-minus" iconType="solid" color="red" />️ | <Icon icon="circle-minus" iconType="solid" color="red" />️ | <Icon icon="circle-minus" iconType="solid" color="red" />️ | <Icon icon="circle-minus" iconType="solid" color="red" />️ |
| entitlement | <Icon icon="square-check" iconType="solid" color="#c937ae" /> | <Icon icon="circle-minus" iconType="solid" color="red" />️ | <Icon icon="circle-minus" iconType="solid" color="red" />️ | <Icon icon="circle-minus" iconType="solid" color="red" />️ | <Icon icon="circle-minus" iconType="solid" color="red" />️ |
| ctx | <Icon icon="circle-minus" iconType="solid" color="red" />️ | <Icon icon="circle-minus" iconType="solid" color="red" />️ | <Icon icon="square-check" iconType="solid" color="#c937ae" /> | <Icon icon="circle-minus" iconType="solid" color="red" />️ | <Icon icon="circle-minus" iconType="solid" color="red" />️ |
| ip | <Icon icon="square-check" iconType="solid" color="#c937ae" /> | <Icon icon="circle-minus" iconType="solid" color="red" />️ | <Icon icon="square-check" iconType="solid" color="#c937ae" /> | <Icon icon="circle-minus" iconType="solid" color="red" />️ | <Icon icon="circle-minus" iconType="solid" color="red" />️ |
| ip CIDR | <Icon icon="square-check" iconType="solid" color="#c937ae" /> | <Icon icon="circle-minus" iconType="solid" color="red" />️ | <Icon icon="square-check" iconType="solid" color="#c937ae" /> | <Icon icon="circle-minus" iconType="solid" color="red" />️ | <Icon icon="circle-minus" iconType="solid" color="red" />️ |

\* In Automations, user data is accessed through the `ctx` object (e.g., `ctx.trigger.newUser`, `ctx.trigger.oldUser`)

### Subject object

**Available in:** <Icon icon="square-check" iconType="solid" color="#c937ae" /> Policies <Icon icon="square-check" iconType="solid" color="#c937ae" /> Groups <Icon icon="square-check" iconType="solid" color="#c937ae" /> Automations <Icon icon="square-check" iconType="solid" color="#c937ae" /> Campaigns <Icon icon="square-check" iconType="solid" color="#c937ae" /> Account provisioning\
**Description:** References the current user in the context\
**Usage:** The most commonly used object across all CEL expression contexts

**Context-specific examples:**

*In Policies:*

```go theme={null}
// Policy conditional - check if user is in Engineering department
subject.department == "Engineering"

// Policy expression - route to user's manager
c1.directory.users.v1.FindByEmail(subject.manager)
```

*In Groups:*

```go theme={null}
// Group membership - include all Engineering employees
subject.department == "Engineering" && subject.status == UserStatus.ENABLED
```

*In Automations:*

```go theme={null}
// Automation trigger - when user status changes
subject.status == UserStatus.DISABLED
```

*In Campaigns:*

```go theme={null}
// User selection - include contractors only
!subject.email.endsWith("@company.com")
```

*In Account provisioning:*

```go theme={null}
// Data mapping - use user's first name
subject.attributes.firstName
```

### User object

**Available in:** <Icon icon="square-check" iconType="solid" color="#c937ae" /> Policies <Icon icon="square-check" iconType="solid" color="#c937ae" /> Groups <Icon icon="square-check" iconType="solid" color="#c937ae" /> Automations <Icon icon="square-check" iconType="solid" color="#c937ae" /> Campaigns <Icon icon="circle-minus" iconType="solid" color="red" />️ Account provisioning\
**Description:** References other users in the system (not the current subject)\
**Usage:** Used when you need to reference or compare against other users

**Context-specific examples:**

*In Policies:*

```go theme={null}
// Policy expression - assign to specific user
c1.directory.users.v1.GetByID("user123")
```

*In Groups:*

```go theme={null}
// Group membership - include users by department
user.department == "Engineering"
```

*In Automations:*

```go theme={null}
// Automation step - modify specific user
user.status == UserStatus.DISABLED
```

*In Campaigns:*

```go theme={null}
// User selection - include users by status
user.status == UserStatus.ENABLED
```

### Task object

**Available in:** <Icon icon="square-check" iconType="solid" color="#c937ae" /> Policies <Icon icon="circle-minus" iconType="solid" color="red" />️ Groups <Icon icon="circle-minus" iconType="solid" color="red" />️ Automations <Icon icon="circle-minus" iconType="solid" color="red" />️ Campaigns <Icon icon="circle-minus" iconType="solid" color="red" />️ Account provisioning\
**Description:** References the current access request or task\
**Usage:** Only available in policy expressions where there's an active task context

**Context-specific examples:**

*In Policies:*

```go theme={null}
// Policy conditional - check task origin
task.origin == TaskOrigin.SLACK

// Policy conditional - check for access conflicts
task.analysis.hasConflictViolations

// Policy conditional - time-based access
task.isGrantPermanent == false && task.grantDuration > duration("2h")

// Policy conditional - auto-approve self-service revocations
task.subjectUserId == task.requestorUserId

// Policy conditional - route after-hours requests to security team
task.created_at.getHours("America/New_York") < 9 ||
task.created_at.getHours("America/New_York") >= 17

// Policy conditional - escalate tasks older than 24 hours
now() - task.created_at > duration("24h")

// Policy conditional - weekend requests require extra approval
task.created_at.getDayOfWeek("America/New_York") == 0 ||
task.created_at.getDayOfWeek("America/New_York") == 6
```

### Task analysis object

**Available in:** <Icon icon="square-check" iconType="solid" color="#c937ae" /> Policies <Icon icon="circle-minus" iconType="solid" color="red" />️ Groups <Icon icon="circle-minus" iconType="solid" color="red" />️ Automations <Icon icon="circle-minus" iconType="solid" color="red" />️ Campaigns <Icon icon="circle-minus" iconType="solid" color="red" />️ Account provisioning\
**Description:** Provides information about potential access conflicts and other analysis data\
**Usage:** Only available in policy expressions for conflict detection and analysis

**Context-specific examples:**

*In Policies:*

```go theme={null}
// Policy conditional - check for any conflicts
task.analysis.hasConflictViolations

// Policy conditional - check for specific conflict
"conflict123" in task.analysis.conflictViolations
```

### Entitlement object

**Available in:** <Icon icon="square-check" iconType="solid" color="#c937ae" /> Policies <Icon icon="circle-minus" iconType="solid" color="red" />️ Groups <Icon icon="circle-minus" iconType="solid" color="red" />️ Automations <Icon icon="circle-minus" iconType="solid" color="red" />️ Campaigns <Icon icon="circle-minus" iconType="solid" color="red" />️ Account provisioning\
**Description:** References the entitlement being requested\
**Usage:** Only available in policy expressions where there's an active entitlement request

**Context-specific examples:**

*In Policies:*

```go theme={null}
// Policy conditional - check specific entitlement
entitlement.id == "entitlement123"

// Policy conditional - require extra approval for high-risk entitlements
entitlement.risk_level_value_id == "high"

// Policy expression - assign to users with same entitlement
c1.directory.apps.v1.GetEntitlementMembers(entitlement.appId, entitlement.id)
```

### IP address object

**Available in:** <Icon icon="square-check" iconType="solid" color="#c937ae" /> Policies <Icon icon="circle-minus" iconType="solid" color="red" />️ Groups <Icon icon="square-check" iconType="solid" color="#c937ae" /> Automations <Icon icon="circle-minus" iconType="solid" color="red" />️ Campaigns <Icon icon="circle-minus" iconType="solid" color="red" />️ Account provisioning\
**Description:** Used for network-based access control and filtering\
**Usage:** Available in policies and automations for network-based logic

**Context-specific examples:**

*In Policies:*

```go theme={null}
// Policy conditional - check if IP is private
ip.isPrivate

// Policy conditional - check if IP is IPv4
ip.is4
```

*In Automations:*

```go theme={null}
// Automation trigger - when IP is from private network
ip.isPrivate
```

### IP CIDR object

**Available in:** <Icon icon="square-check" iconType="solid" color="#c937ae" /> Policies <Icon icon="circle-minus" iconType="solid" color="red" />️ Groups <Icon icon="square-check" iconType="solid" color="#c937ae" /> Automations <Icon icon="circle-minus" iconType="solid" color="red" />️ Campaigns <Icon icon="circle-minus" iconType="solid" color="red" />️ Account provisioning\
**Description:** Used for network range-based access control and filtering\
**Usage:** Available in policies and automations for network range checking

**Context-specific examples:**

*In Policies:*

```go theme={null}
// Policy conditional - check if IP is in range
cidr('10.1.2.0/24').contains(ip('10.1.2.5'))
```

*In Automations:*

```go theme={null}
// Automation trigger - when IP is in specific range
ip('10.1.2.5').within(cidr('10.1.2.0/24'))
```

### Context object (ctx)

**Available in:** <Icon icon="circle-minus" iconType="solid" color="red" />️ Policies <Icon icon="circle-minus" iconType="solid" color="red" />️ Groups <Icon icon="square-check" iconType="solid" color="#c937ae" /> Automation Triggers <Icon icon="square-check" iconType="solid" color="#c937ae" /> Workflow Steps <Icon icon="circle-minus" iconType="solid" color="red" />️ Campaigns <Icon icon="circle-minus" iconType="solid" color="red" />️ Account provisioning\
**Description:** Provides access to workflow execution context and trigger data\
**Usage:** Used in automations to access trigger data and previous step outputs

**Available fields:**

*In Automation Triggers:*

* `ctx.trigger.oldUser` / `ctx.trigger.old_user` - User state before change
* `ctx.trigger.newUser` / `ctx.trigger.new_user` - User state after change
* `ctx.trigger.oldAccount` / `ctx.trigger.old_account` - Account state before change
* `ctx.trigger.newAccount` / `ctx.trigger.new_account` - Account state after change
* `ctx.trigger.entitlement` - Entitlement that triggered the workflow

*In Workflow Steps:*

* `ctx.trigger` - Trigger output data (structure varies by trigger type)
* `ctx.[step_name]` - Output from each completed workflow step

**Context-specific examples:**

*In automation triggers:*

```go theme={null}
// Trigger when user changes departments
ctx.trigger.newUser.department != ctx.trigger.oldUser.department && 
ctx.trigger.newUser.department == "Engineering"

// Trigger when account status changes to disabled
ctx.trigger.newAccount.status.status == APP_USER_STATUS_DISABLED &&
ctx.trigger.oldAccount.status.status == APP_USER_STATUS_ENABLED
```

*In automation steps:*

```go theme={null}
// Access trigger data
ctx.trigger.user.email

// Template syntax for embedding in strings
"User {{ ctx.trigger.user.display_name }} changed departments"
```

<Warning>
  **Important:**

  Automation triggers have limited function access. They do NOT have access to directory functions (`c1.directory.*`) or user entitlement checking functions (`c1.user.v1.HasApp`, etc.). Only basic user/account field access and status enums are available.
</Warning>

## Important notes

### Use camelCase

CEL expressions should be written in camelCase. C1 is moving away from snake\_case for consistency and readability. Existing expressions in snake\_case will still work, but new ones should follow the camelCase convention.

### Null safety with has()

Use the `has()` macro to check for existence of optional fields, especially in profile maps:

```go theme={null}
has(subject.profile.custom_field) ? subject.profile.custom_field == "value" : false
```

### Performance considerations

Directory functions (`c1.directory.*`) perform I/O operations. Place them after simpler conditions when possible:

```go theme={null}
// Good: Check fast conditions first
subject.department == "Engineering" && c1.directory.users.v1.FindByEmail(subject.email)

// Less optimal: I/O operation runs for all evaluations
c1.directory.users.v1.FindByEmail(subject.email) && subject.department == "Engineering"
```

### Function memoization

Directory function calls are automatically memoized within a single expression evaluation, so calling the same function multiple times is safe and efficient.

### Template syntax

Only Workflow Steps support `{{ expression }}` template syntax for embedding CEL expressions in strings.
