> ## Documentation Index
> Fetch the complete documentation index at: https://www.c1.ai/docs/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={"theme":{"light":"css-variables","dark":"css-variables"}}
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={"theme":{"light":"css-variables","dark":"css-variables"}}
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.

<Info>
  **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.
</Info>

#### 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)

#### 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.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)
* `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 |

**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

<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={"theme":{"light":"css-variables","dark":"css-variables"}}
  // 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>

<Info>
  **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.
</Info>

### 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={"theme":{"light":"css-variables","dark":"css-variables"}}
// 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={"theme":{"light":"css-variables","dark":"css-variables"}}
// 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={"theme":{"light":"css-variables","dark":"css-variables"}}
// 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={"theme":{"light":"css-variables","dark":"css-variables"}}
// 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={"theme":{"light":"css-variables","dark":"css-variables"}}
// 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 |

<Info>
  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`.
</Info>

#### 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={"theme":{"light":"css-variables","dark":"css-variables"}}
// 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={"theme":{"light":"css-variables","dark":"css-variables"}}
// Group membership - include all Engineering employees
subject.department == "Engineering" && subject.status == UserStatus.ENABLED
```

*In Automations:*

```go theme={"theme":{"light":"css-variables","dark":"css-variables"}}
// Automation trigger - when user status changes
subject.status == UserStatus.DISABLED
```

*In Campaigns:*

```go theme={"theme":{"light":"css-variables","dark":"css-variables"}}
// User selection - include contractors only
!subject.email.endsWith("@company.com")
```

*In Account provisioning:*

```go theme={"theme":{"light":"css-variables","dark":"css-variables"}}
// 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={"theme":{"light":"css-variables","dark":"css-variables"}}
// Policy expression - assign to specific user
c1.directory.users.v1.GetByID("user123")
```

*In Groups:*

```go theme={"theme":{"light":"css-variables","dark":"css-variables"}}
// Group membership - include users by department
user.department == "Engineering"
```

*In Automations:*

```go theme={"theme":{"light":"css-variables","dark":"css-variables"}}
// Automation step - modify specific user
user.status == UserStatus.DISABLED
```

*In Campaigns:*

```go theme={"theme":{"light":"css-variables","dark":"css-variables"}}
// 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={"theme":{"light":"css-variables","dark":"css-variables"}}
// 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={"theme":{"light":"css-variables","dark":"css-variables"}}
// 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={"theme":{"light":"css-variables","dark":"css-variables"}}
// 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={"theme":{"light":"css-variables","dark":"css-variables"}}
// Policy conditional - check if IP is private
ip.isPrivate

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

*In Automations:*

```go theme={"theme":{"light":"css-variables","dark":"css-variables"}}
// 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={"theme":{"light":"css-variables","dark":"css-variables"}}
// Policy conditional - check if IP is in range
cidr('10.1.2.0/24').contains(ip('10.1.2.5'))
```

*In Automations:*

```go theme={"theme":{"light":"css-variables","dark":"css-variables"}}
// 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={"theme":{"light":"css-variables","dark":"css-variables"}}
// 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={"theme":{"light":"css-variables","dark":"css-variables"}}
// 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={"theme":{"light":"css-variables","dark":"css-variables"}}
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={"theme":{"light":"css-variables","dark":"css-variables"}}
// 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.
