Language basics
OpalScript uses Starlark syntax, which is similar to Python. This section gives you a quick overview of Starlark syntax and its key differences from Python.Syntax overview
# Variables
count = 10
name = "alice"
is_active = True
# Conditionals
if count > 5:
print("high")
elif count > 0:
print("low")
else:
print("zero")
# Loops
for item in [1, 2, 3]:
print(item)
for i in range(10):
print(i)
# Functions
def calculate_score(base, multiplier):
return base * multiplier
# String operations
text = "Hello World"
lower_text = text.lower() # "hello world"
contains_hello = "hello" in lower_text # True
# Lists
items = ["a", "b", "c"]
items.append("d")
first = items[0]
# Dictionaries
config = {"key": "value", "count": 42}
value = config["key"]
config["new_key"] = "new_value"
# Comprehensions
resource_ids = [r.resource_id for r in request.requested_resources]
Key differences from Python
OpalScript (Starlark) has some intentional limitations compared to Python:| Feature | OpalScript | Python |
|---|---|---|
| Import statements | Not supported (modules pre-loaded) | Supported |
| Classes | Not supported | Supported |
| Exceptions | Not supported (try/except) | Supported |
| While loops | Not supported (use for with range()) | Supported |
| Recursion | Not supported | Supported |
| f-strings | Not supported (use % or .format()) | Supported |
| Sets | Not supported (use a dict or list) | Supported |
| Global variables | Limited mutation | Full support |
| Standard library | Minimal | Extensive |
Quick reference
Utility functions
Which modules each script type can use is listed in Module availability by script type.| Module | Function | Description |
|---|---|---|
| access | check_access(principal, entity, [level]) | Check whether a principal has access to an entity |
| groups | add_user(group_id, user_id, [duration_minutes], [indefinite], [access_level_remote_id], [access_level_name]) | Add a user to a group (Request Review only) |
| notifications | notify_user(user_id, title, body) | Notify a specific user via email, Slack, or Microsoft Teams (if configured) |
| notifications | notify_admins(title, body) | Notify all Opal admins |
| notifications | notify_owner(owner_id, title, body) | Notify an owner via Slack channel (if configured) or all owner members |
| requests | by_user(user_id, [status], [cursor], [per_page]) | Get requests a user submitted or was the target of (paginated) |
| requests | by_resource(resource_id, [user_id], [status], [cursor], [per_page]) | Get historical requests for a resource (paginated) |
| requests | by_group(group_id, [user_id], [status], [cursor], [per_page]) | Get historical requests for a group (paginated) |
| risk | resource_sensitivity(resource_id) | Get the risk sensitivity level of a resource |
| risk | group_sensitivity(group_id) | Get the risk sensitivity level of a group |
| time | now() | Get the current Unix timestamp |
| time | from_unix(timestamp) | Convert a Unix timestamp to an RFC3339 string |
| time | is_before(timestamp1, timestamp2) | Check if the first timestamp is before the second |
| time | is_after(timestamp1, timestamp2) | Check if the first timestamp is after the second |
| time | seconds_since(timestamp1, timestamp2) | Get the number of seconds between two timestamps |
| time | minutes(n) / hours(n) / days(n) | Convert time intervals to seconds |
| entity | get_user(user_id) | Look up a user by ID |
| entity | get_group(group_id) | Look up a group by ID |
| entity | get_resource(resource_id) | Look up a resource by ID |
| tickets | providers.<PROVIDER> | Access an installed ticket provider (e.g. tickets.providers.JIRA) |
| tickets | list_projects(provider) | List projects on a provider; returns a dict keyed by project key |
| tickets | create_ticket(project, title, description) | Create a ticket on the provider; returns a ticket object |
| tickets | get_ticket(provider, remote_ticket_id) | Fetch an existing ticket by its remote identifier |
| tickets | comment_ticket(provider, remote_ticket_id, comment) | Add a comment to an existing ticket |
| tickets | close_ticket(provider, remote_ticket_id, [comment]) | Close a ticket, with an optional closing comment |
| http | request(url, [method], [headers], [params], [json], [body], [timeout]) | Send an outbound HTTP request and return a Response |
| http | get / post / put / patch / delete | Per-verb convenience wrappers around request |
| secrets | get(name) | Return an opaque Secret to use as an http URL or header value |
notifications and tickets are available in Request Review scripts only. http and secrets are available in Request Review and Paladin Context scripts. See Reach external services.
Request Review functions
| Module | Function | Description |
|---|---|---|
| context | get_request() | Get the request being reviewed |
| context | is_pending_reviewer() | Whether the stage is still waiting for a decision. For escalation timers; not available in test runs |
| context | get_escalation_delay_minutes() | The stage’s escalation delay in minutes, or None. For escalation timers; not available in test runs |
| actions | approve([comment], [duration_minutes]) | Approve the request, optionally overriding the access duration |
| actions | deny(comment) | Deny the request (comment required) |
| actions | comment(comment, [duration_minutes]) | Add a comment, optionally overriding the access duration |
| actions | pause(minutes) | Pause script execution for 1–1440 minutes, then resume from the same point; actions before the pause are skipped on replay |
| actions | poll(pollFunction, minutes, max_iterations) | Call pollFunction every minutes minutes (1–1440) up to max_iterations times (1–50, or up to 2,000 with extended polling Beta); returns True if it succeeded |
| groups | add_user(group_id, user_id, [duration_minutes], [indefinite], [access_level_remote_id], [access_level_name]) | Add a user to a group; returns None or an error message. See Manage group membership |
| groups | remove_user(group_id, user_id, reason) | Remove all of a user’s direct memberships in a group; returns None or an error message |
Delegation condition functions
See Delegation conditions.| Module | Function | Description |
|---|---|---|
| context | get_request() | Get the request being evaluated |
| actions | delegate(user_ids) | Declare the delegates eligible to cover the request (terminal) |
Request configuration condition functions
See Request configuration conditions.| Module | Function | Description |
|---|---|---|
| context | get_target_user_id() | Get the UUID of the target user for the request |
| context | get_resource_id() | Get the UUID of the requested resource, or None if evaluating a group |
| context | get_group_id() | Get the UUID of the requested group, or None if evaluating a resource |
| context | get_access_level_name() | Get the requested access level display name, or None |
| context | get_access_level_remote_id() | Get the requested access level remote ID, or None |
| actions | match() | Mark this request configuration condition as matched (terminal) |
Paladin context functions Beta
See Paladin Context.| Module | Function | Description |
|---|---|---|
| context | get_request() | Get the request Paladin is reviewing |
| actions | add_context(label=, data=, [source=]) | Add an item of context for Paladin (keyword arguments only; not terminal) |
Request attributes
| Attribute | Type | Always Present |
|---|---|---|
id | String | Yes |
created_at | int | Yes |
updated_at | int | Yes |
requester_id | String | Yes |
target_user_id | String | No |
target_group_id | String | No |
status | String | Yes |
reason | String | Yes |
requested_duration_minutes | int | No |
requested_resources | List | Yes (may be empty) |
requested_groups | List | Yes (may be empty) |
custom_fields | Dict | Yes (may be empty) |
support_ticket_id | String | No |
request_ticket_id | String | No |