What is Jinja and why do I need it?
A pipeline automates tasks by moving data between steps. When a trigger happens in one place an action happens in another. But, the data moving between steps rarely fits perfectly. You'll often need to combine two fields, format a date, branch on a condition, or build a list.
Jinja is a templating language that fills in placeholders with real data when the pipeline runs. Write it into any field that accepts text, wrapped in curly braces, and Pipelines evaluates it when the run happens.
Use Jinja any time you want to:
Combine values from earlier steps, ex. combining a first name and last name.
Change a value before sending it on, ex. formatting a numerical value as a date.
Make a decision, ex. writing VIP if the customer meets a set criteria, or leaving it blank if they don’t.
Handle a list, ex. having one line per record returned by a search step.
The moment a pipeline needs to use data from a previous step or react to what's coming in, you need something in place of static text. Jinja is that something. You don't have to learn all of Jinja to get value from it—most pipelines use a handful of common patterns.
Glossary
Term | What it means |
|---|---|
Step | One action in a pipeline |
Field | A piece of data on a record |
Trigger | The event that starts a pipeline Ex: A record was created in a table |
Search step | A step that returns a list of records matching a filter |
Designer | The visual editor where you build a pipeline |
Code mode | When you select the </> icon on a field to type a raw value yourself |
Activity log | A history of every pipeline run, with every step's output—open via the pipeline's More menu (3-dot icon), then select View activity |
What Jinja looks like
Here's a Jinja expression:
{{a.first_name}} {{a.last_name}}In a pipeline step, this prints the first and last names from the previous step. The Reference steps section of this article explains what a means. The Common tasks table shows how to combine values, and Debug your pipeline covers what to do when things go wrong.
Jinja isn't the Quickbase formula language. The two share goals but use different syntax. IfThenElse(...) is a formula; {% if ... %}...{% endif %} is Jinja.
Common tasks
I want to… | Try this |
|---|---|
Combine two fields |
|
Concatenate without spaces |
|
Uppercase a value |
|
Round a number |
|
Today's date |
|
Date 7 days from now |
|
If / else |
|
Loop through a list |
|
Fall back if empty |
|
Clear a field |
|
Detect a changed field |
|
For more, see Common Jinja expressions.
Where to write Jinja
Most fields in a step accept Jinja. Select the code icon </> next to a field to switch it into code mode, then type your expression. Once you type {{, the field starts validating and formatting as you go.
To start, drag a field tile from the Variables panel into a code-mode field. The Designer writes the dot notation for you.
Note: A few fields don't accept Jinja and use a different language instead. The most common case is Advanced Query / filter fields on Quickbase search and trigger steps. Those use Quickbase query syntax, not Jinja. See Jinja reference for the syntax. If your Jinja stops working in a filter field, that's probably why.
Reference steps
Every step in a pipeline gets a letter: the first is a, the second is b, the third is c, and so on.
When you write b.customer_name, it means: the customer_name field from the second step.
For a Record Created trigger, step a is the record that fired the trigger, and a.field_name gets any field on it.
Three ways to reach a field
{{a.first_name}} # standard
{{a['Date Created']}} # field name has spaces
{{a.get('phone', 'No phone')}} # with a default if missingUse brackets when the field name has spaces or matches a reserved word like items.
For address fields, use brackets when the parent label has spaces:
{{a['Mailing Address'].city}}Single records vs. lists
Triggers and lookups return one record. a.field works directly.
Searches and Get All steps return a list, even if only one record matches. Don't dot-access them—use the first item, or loop:
{{b[0].customer_name}} # first record
{% for r in b %} # all records
{{r.customer_name}}
{% endfor %}If you see an error like TypeError: 'list' object has no attribute… in the activity log, you tried to dot-access a list when you should have looped or indexed.
In the Designer, list-returning steps show a green indicator on their output and single-record steps show blue.
Track what changed with $prev
When a record is updated, compare current and previous values with $prev:
{% if a.status != a.$prev.status %}
Status changed from {{a.$prev.status}} to {{a.status}}.
{% endif %}Warning:
$prevonly works on Record Updated triggers—not on Create or Delete. It goes between the step letter and the field:a.$prev.field_name, not$prev.a.field_name. It also doesn't work inside Quickbase Advanced Query filter strings.
A common use is an audit-log pipeline that writes a row per field change.
Empty a field intentionally with {{CLEAR}}
By default, Pipelines refuses to write an empty value to a field. It silently skips it instead, to protect you from accidentally wiping data.
If you actually want to clear a field, use {{CLEAR}}:
{% if a.priority == 'No priority' %}{{CLEAR}}
{% else %}{{a.priority}}
{% endif %}{{CLEAR}} only works in a step that writes data back—typically Create or Update.
Conditionals and loops
Use conditionals
A conditional asks a yes/no question and follows one path if true, another if false. Is the amount over $1,000? Label it "Big." Is it not? Check the next condition.
Example: Label every order as "Big," "Medium," or "Small" depending on the amount.
{% if a.amount > 1000 %}Big
{% elif a.amount > 100 %}Medium
{% else %}Small
{% endif %}If the amount is bigger than 1,000, write "Big." Otherwise, if it's bigger than 100, write "Medium." Otherwise, write "Small."
The pattern is always:
{% if … %}—the question to check{% elif … %}—(optional) another question if the first one was no{% else %}—(optional) what to do if nothing matched{% endif %}—closes the block
You can also use if on its own to write something only when a condition is true:
{% if a.is_vip %}
This customer is a VIP.
{% endif %}Use loops
A loop is how you handle a list of things, like 1 line per customer, 1 entry per order, or 1 row per search result.
Example: A search step returned a list of customers, and you want to print every customer's name on its own line.
{% for customer in b %}
{{customer.name}}
{% endfor %}For each customer in the list b, write that customer's name.
The pattern is always:
{% for X in Y %}—go through every item in the listY. The nameXis a placeholder;customerin this example, butr,item, or any valid variable name works.The lines in the middle—what to do for each item, using the placeholder name (
customer) to reach its fields{% endfor %}—closes the block
While inside the loop, Jinja gives you a few useful helpers:
loop.index—position of the current item, counting from 1 (the third item hasloop.indexof 3)loop.last—true on the last item onlyloop.length—the total number of items
Use loop.last to add a comma after every item except the last:
{% for c in b %}{{c.name}}{% if not loop.last %}, {% endif %}{% endfor %}That writes Alice, Bob, Carol instead of Alice, Bob, Carol,. For the full set of loop helpers, see the Jinja reference.
Why the curly-brace patterns are different
Both patterns use {% %}—curly braces with a percent sign. That's because they're instructions, not values.
{{ ... }}—show me this value (double curly braces){% ... %}—do something, like an if or a loop (curly braces with a percent)
The runtime object
Every pipeline run has a runtime object with information about itself:
Pipeline "{{runtime.pipeline_name}}" failed.
View activity: {{runtime.activity_url}}Available properties: pipeline_name, pipeline_id, pipeline_url, run_id, activity_url, triggered_at, user_id.
On Quickbase triggers, you also get a context object with details about the table and user:
Updated in {{a.context.table_name}} ({{a.context.app_name}})
at {{a.context.occurred_at}} by {{a.context.user}}.Debug your pipeline
Open the pipeline, then select the More menu (3-dot icon) > View activity. The activity log shows every step's rendered Jinja, color-coded by data type.
Symptom | Likely cause | Fix |
|---|---|---|
Output renders as literal | You combined a null value with text | Add |
Output missing from the log | Empty value silently skipped | Use |
| Jinja syntax error | Check for missing |
| You reached for a field on a null value | Guard with |
Use AI to draft expressions
Once you're comfortable with the basics, the Pipelines AI can write Jinja for you. Open code mode on any field and select the AI suggestion icon. Describe what you want in plain language (for example, set this to 7 days after the trigger date or build a list of customer emails separated by semicolons) and the AI proposes a Jinja expression you can review and edit before applying.
The expressions it generates make more sense, and are easier to review and adjust, once you're familiar with the patterns in this article.
Where to go next
Full filter and test reference—Jinja reference
Ready-to-use patterns by task or field type—Common Jinja expressions