Expressions

Expressions are CEL. They are written in integration definitions and in hooks. An expression is at most 2,000 characters.

Syntax

WhereWritten asExample
An integration or hook written as JSON${{ <CEL> }}"${{ body.event.type == \"app_mention\" }}"
An integration or hook written in HCL$${{ <CEL> }}"$${{ body.data.id }}"
A hook's whenbare CEL, or ${{ <CEL> }}event.inputs.query != ''

A field that holds one expression must be the whole string, ${{ ... }}, with nothing around it but spaces. Such fields are refused otherwise, with must be an expression, written as ${{ ... }}.

A field that holds text (a request's url, query, headers and body strings, a tool input's default, asymmetric.key, and a hook step's with strings and request fields) may hold any number of expressions inside it:

https://api.example.com/issues/${{ task.marks["linear.issue"] }}/comments

When the text is one expression and nothing else, it keeps the expression's type, so a body field written ${{ {"channel": target.channel} }} sends a JSON object. Otherwise each expression is written into the text.

Evaluation

The expression isWhen it fails to evaluate
a condition (when, mine, respond.when, expect.success, a hook's when)it counts as false
in a request's url, query, headers or body, or a hook requestthe request is not sent and fails
anywhere elseit reads as nothing: no mark is kept, a capture is null, a title is empty

Reading a field that is not there fails. has() tests whether a field is there:

has(body.data.assignee) ? body.data.assignee.email : ""

No expression may read a secret. An expression containing secret. or secret[ is refused.

Integration positions

A position is the path of a field, with * for any key or index. Returns is what the expression must return, when anything is required. These tables leave out the integration. prefix and write [*] as .*: the check and positions APIs write webhook.events.*.when as integration.webhook.events[*].when, and a check names a real index, as in integration.webhook.events[0].when.

PositionReturnsSees
webhook.verify.hmac.signed, webhook.verify.hmac.freshness.at, webhook.verify.asymmetric.key, webhook.verify.asymmetric.signed, webhook.verify.asymmetric.freshness.atbody, headers, raw
webhook.respond.whenboolbody, headers, raw
webhook.respond.bodybody, headers, raw
webhook.mineboolbody, headers, raw
webhook.route.by, webhook.deliverybody, headers, raw
webhook.events.*.forEachlistbody, headers, raw
webhook.events.*.whenboolbody, headers, raw, item
webhook.events.*.lookup.request.url, .query.*, .headers.*, .bodybody, headers, raw, item
webhook.events.*.lookup.request.expect.successboolthe above, response, status
webhook.events.*.lookup.request.capture.*, webhook.events.*.lookup.capture.*the above, response, status
webhook.events.*.raise.say, .opens.title, .opens.description, .opens.assignBy.email, .branch, .marks.*.valuebody, headers, raw, item, captures
webhook.events.*.raise.attaches.eachlistbody, headers, raw, item, captures
webhook.events.*.raise.attaches.name, .type, .size, and .fetch request fieldsthe above, file
poll.request.url, .query.*, .headers.*, .bodycursor, connection
poll.request.expect.successboolcursor, connection, response, status
poll.request.capture.*cursor, connection, response, status
poll.cursorbody, cursor
poll.events.*as for webhook.events.*body, cursor, and as for webhook.events.*
actions.*.request.url, .query.*, .headers.*, .bodytask, ask, target, connection, inputs
actions.*.request.expect.successboolthe above, response, status
actions.*.request.capture.*the above, response, status
tools.*.inputs.*.defaulttask
tools.*.outputtask, ask, target, connection, response, status, capture, inputs
connection.oauth2.identityresponse
on.*.to.*task, ask
on.*.mark.*.valuetask, ask, target, connection, response, capture

item is seen only when the event has forEach. Captures are the names in the event's lookup.capture, each seen by its own name. inputs is seen by an action only when a tool runs it. A capture named like a built-in name shadows it, and is warned about.

What an action has at run time depends on what runs it:

Run byHas
a tooltask, inputs, connection
an on bindingtask, ask, target, connection
a scheduleconnection

Integration values

NameTypeDescription
bodydynThe delivery or poll response, parsed as JSON.
headersdynThe delivery's headers, by lowercase name.
rawstringThe delivery's body as text.
cursorstringThe poll's cursor, "" before the first.
connectionstringThe team's OAuth access token.
responsedynThe response body, parsed as JSON when it is JSON, else text; null when empty.
statusdynThe response's HTTP status.
capturemapThe request's capture values, by name.
itemdynThe current entry of forEach.
filedynThe current entry of attaches.each.
inputsmapThe tool's inputs, by name.
targetmap of stringThe values of the binding's to, and channel from routes.

task:

FieldTypeDescription
task.linestringThe key of the task's line.
task.stationstringThe name of the task's station.
task.stationKeystringThe key of the task's station.
task.marksmap of stringThe task's marks, by kind, as in task.marks["slack.thread"].

ask:

FieldTypeDescription
ask.bodystringWhat the task asks, when it waits on a person; otherwise "". When it waits on an approval, ends with a link to decide it.
ask.linkdynThe address of the conversation in the inbox, or null.
ask.stationstringThe name of the task's station.

Hook positions

PositionReturnsSees
hook.whenboolevent, task, run, vars, hook
hook.steps.*.with.*the above, step
hook.steps.*.request.urlthe above, step
hook.steps.*.request.headers.*the above, step
hook.steps.*.request.bodythe above, step

A hook's match is compiled into conditions of the same kind as when.

Hook values

NameTypeDescription
eventobjectThe event's fields. Every event has event.at, an ISO 8601 time. The rest are listed per event in Events.
task.idstringThe task's id.
task.titlestringThe task's title.
task.urlstringThe task's address in Spotlight.
task.linestringThe key of the task's line.
task.stationstringThe name of the task's station.
task.stationKeystringThe key of the task's station.
task.marksmap of stringThe task's marks, by kind.
task.repositorieslist of stringThe line's repositories, as owner/name.
task.pullRequestdynThe task's pull request address, or null.
run.idstringThe run's id. Only on events that happen in a run.
varsmap of stringThe hook's vars.
hook.keystringThe hook's key.
step.<name>.statusstringdone, failed or waiting, for a step before this one.
step.<name>.outputdynThat step's response.

Type-checking

Every expression is type-checked against what its position sees when the definition is written.

ProblemIntegrationHook
does not parseerror, write refusederror, write refused
does not type-check, or returns the wrong typewarning, definition kepterror, write refused

An integration warning reads does not check: <message>, followed by . Did you mean <suggestion>? when there is one. Warnings are returned in the resource's warnings, and the Terraform provider shows them as warnings on the attribute.

body and response are dyn unless a shape declares them. See Shapes.

Check API

POST /api/expressions/check

Type-checks one expression where it would be written, as terraform plan does. Needs the plan permission; a workspace token without it is refused with 403. It takes the same credentials and returns the same problems as the resource API.

FieldTypeDescription
positionstringWhere the expression is written, as in integration.webhook.events[0].when or hook.steps[0].with.text. Begins with integration or hook.
expressionstringThe CEL, without ${{ }}.
definitionobjectThe whole integration or hook, as it would be created.
{
  "position": "hook.when",
  "expression": "event.inputz.query != ''",
  "definition": { "key": "grafana-reads", "on": "tool.used", "steps": [{ "name": "notify", "call": "grafana.search" }] }
}

The response:

FieldTypeDescription
typestring or nullThe CEL type the expression returns, or null when it does not check.
errorsarrayEach has message, span (start, end, or null) and suggestion (or null).
warningsarrayEach has position and message.

422 when the request is not shaped as a check, when the position names neither an integration nor a hook attribute, when the definition holds errors (each listed under definition), or when the position holds no expression in the definition.

Positions API

GET /api/expressions/positions

Returns every position, integration and hook, as a list. It needs any credential.

FieldTypeDescription
positionstringThe position, beginning with integration or hook, with [*] for any key or index, as in integration.webhook.events[*].when.
returnsstring or nullbool, list, or null.
seesarrayThe names the expression sees, each with name, type and nested fields.
alsoSeesarrayNames seen only in some definitions: item, captures, inputs.

On this page