JavaScript templates

Learn about how you can use templating to compose blocks into powerful Runbooks.
JavaScript templates ("JS templates") allow you to pass data from parameters or configs into runbook blocks or from the output of one block into subsequent blocks. You can also use JavaScript to transform data as needed.
If you don't know JavaScript, don't worry—follow the syntax below and you'll be able to do everything you need to build powerful runbooks.

What are JS templates?

JS templates in Airplane runbooks work a lot like templates in other tools. A common place you might have seen these is in email marketing tools, where you can write something like Hello {{first_name}} {{last_name}}! and the latter part will get filled in with the person's first name and last name when the email is sent.
Airplane's JS templates work the same way. However, in addition to simple string substitution templates like the one above, JS templates in Airplane also allow you to execute JavaScript code.

Anatomy of a JS template

A JS template is composed of expressions, wrapped in {{}}'s, interwoven with raw text:
When a JS template is evaluated, the contents of each expression (including the {{}}'s) are replaced with their result. The example above would produce a string such as:
Added Wilbur Wright to team Rocketship
In other words, the expression results are casted to strings to match the interwoven raw text.
If a template consists of a singular expression, with no other text, then the output of the expression will not be casted to a string. This allows you to generate non-string values, such as integers:

Using JavaScript in JS templates

Within each JS expression, you can access runbook metadata such as runbook parameters:
You can learn more about what globals are available below.
Most JS expressions tend to be references, like However, you can use the full power of JS to perform data transformations inline. For example, you can introduce fallback values:
Hello, {{block1.output?.name ?? "friend"}}!
If block1 did not produce a name, this will default to the string "friend" instead. Therefore the whole expression might say Hello, [your name]! or it might just say Hello, friend!
You can also use JS to transform data, such as splitting a comma-delimited list of IDs into a list of strings:
{{ params.ids.split(",").map(s => s.trim()) }}
For example, an input of "1, 3, 6, 10, 15" would be transformed into ['1', '3', '6', '10', '15'].
Another use-case is transforming data into Markdown. For example, this will convert a list of rows from a DB query into a Markdown list that can be rendered in the Slack integration:
2 {
return "* " +;

How to use JS templates in Airplane

JS templates can be used in any input that has the template icon:
Certain inputs, such as dates or integers, have a template toggle which allows you to toggle between the native input (f.e. a datepicker or numeric input) and an input that supports templates. Click the text that says "templating: off" to toggle:
Start conditions don't require {{ notation for templating. You can enter JavaScript directly into a start condition since all start conditions must be JavaScript expressions that evaluate to either true or false.


You can define top-level parameters in the "Define" section of a runbook. These can be strings, integers, numbers, or more complex input types like dates or files.
All runbook parameters are stored in a global object called params. You can use this object along with the parameter's slug to refer to any parameter within a template. In this example, {{params.user_email}} will be used to refer to the "User email" parameter from the screenshot above:

Config variables

You can define config attachments in the "Build" section of a runbook. These allow you to reference config variables you've added in any block of the runbook.
Similar to parameters, any attached configs are stored in a global object called configs. You can reference the config by its name in any templated field. In the example below, {{configs.sender_email}} refers to the sender_email config variable and is used in an Email block:

Block output

You can also take the output from any block and pass it into future blocks. Each block has its own slug that can be edited by clicking the pencil icon to the right of the block name. To refer to a block's output, use the expression block_slug.output. For example, if your block has the slug sql_query, then you could access its output with sql_query.output. The slug of a block is displayed to the right of the name at the top of the block.
If you're using a Task block created with Python, JavaScript, Shell, or other kind of custom code builder, use Output to render them in a way that can be accessed from runbook JS templates.
Below is an example of a block that generates an output table (representing a JavaScript array) called response_data. This table has one row with three columns. The next block, a Slack message, can reference one of the values in the output with an expression like: {{block_slug.output.response_data[0].user}}
In this expression, block_slug.output refers to the entire output object. response_data is the specific object outputted within here (there can be multiple output objects). [0] refers to the first row of the output array (there's only one row in this example, but there can be multiple). And then user refers to the value of the user field in that row.
If you want to examine the output of a block as raw JSON, click the "JSON" tab above the output:

SQL block output

SQL blocks or SQL-based task blocks will display output in a table that can be referenced in subsequent blocks via output. This works the same way as above: use block_slug.output to refer to the output, and then reference the child objects, rows, and values to get to the value you want.
By convention, the output of all SQL select blocks will be an object with keys such as Q1, Q2, etc. The first select statement's results will be included as a list of rows in Q1, the second query in Q2, and so on.
Therefore, you'd refer to the output of a SQL block as something like {{block_slug.output.Q1[0].field_name}}, as shown below:

Block inputs

You can refer to the input used by any block in a future block.
In the example below, we send an email to an individual followed by an API call with the same individual's email. Rather than copy and pasting their email address, we can refer to the recipients parameter of the email task with an expression like {{email.input.recipients[0].email}}
In this expression, email.input refers to all of the parameters on the task.

Globals reference

In addition to {{params}}, here is a full list of other global variables that you can use in runbooks.
{{params}}An object, keyed by parameter slug, containing the runbook's parameter values.
{{configs}}An object, keyed by config name, containing the runbook's config attachments.
{{<block_slug>.output}}The output of the block with slug, block_slug. The format of this value matches what is shown in the JSON output tab.
{{<block_slug>.input}}The input used by the block with slug, block_slug. The format of this value depends on the type of the task.
{{}}The Airplane-generated ID of the session.
{{session.url}}A URL that opens the session.
{{}}The name of the session.
{{}}The email of the person running the runbook.
{{}}The name of the person running the runbook.
{{}}The Airplane-generated user ID of the person running the runbook.
{{}}The Airplane-generated ID of the current block.
{{block.slug}}The slug of the current block.
{{}}The Airplane-generated ID of the associated runbook.
{{runbook.url}}A URL that opens the associated runbook.
{{}}The name of the associated runbook.
{{}}The id of the environment context of the session. For information on environments, see Environments.
{{env.slug}}The slug of the environment context of the session.
{{}}The name of the environment context of the session.
{{env.is_default}}true if the environment context of the session is the team's default environment.