Overview
Filters clean up a value right before it lands somewhere that cares about the exact format: a Slack message, a Stripe charge, an invoice number, or a partner's system. Tidy [email protected] into [email protected], stamp an order as INV-00001042, or push a due date 30 days out. It all happens inline, with no code and no extra step.
Every text field in a Taskade automation is a LiquidJS template. Anywhere you can type a value, you can also pipe values through filters that transform them inline: encode a string for JSON, pad an order number with zeros, shift a date forward by 30 days, or camelize a slug for a partner's system.
Taskade ships a curated set of filters: 9 custom Taskade filters for the transformations real automations need, plus 17 whitelisted standard LiquidJS filters. Anything else throws a clear error rather than running surprising code on your data.
TL;DR: Wrap any value in
{{ variable | filterName: arg }}. Pair with the HTTP Request, Webhook, or any Action to clean values right where you use them.
How filters fit into the loop
A filter runs after the variable resolves but before the rendered value is handed to the action. Everything happens inline in a single render pass.
Filters chain left-to-right with |. The output of one filter becomes the input of the next.
{{ "[email protected] " | downcase | strip }}
→ [email protected]
Custom Taskade filters
These nine filters ship with every automation. They cover the gaps standard LiquidJS leaves: base64, padding, date shifting, JSON-safe escaping, and a Shopify-style camelize.
| Filter | Category | What it does | Example |
|---|---|---|---|
camelize |
String | Shopify-style camelCase. Splits on _ and / (slashes become ::). Capitalizes first letter. |
{{ "user_profile" | camelize }} → UserProfile |
base64encode |
String | btoa(value), encode to base64 |
{{ "hello" | base64encode }} → aGVsbG8= |
base64decode |
String | atob(value), decode from base64 |
{{ "aGVsbG8=" | base64decode }} → hello |
padStart |
String | String.padStart(length, element) |
{{ "42" | padStart: 6, "0" }} → 000042 |
padEnd |
String | String.padEnd(length, element) |
{{ "abc" | padEnd: 6, "-" }} → abc--- |
encodeJsonValue |
String | JSON-safe escaping. JSON.stringify(value) then strip the outer quotes, for safely embedding values inside hand-written JSON. |
{{ "He said \"hi\"" | encodeJsonValue }} → He said \"hi\" |
power |
Math | Math.pow(value, exponent) |
{{ 2 | power: 10 }} → 1024 |
log |
Math | Math.log10(value) |
{{ 1000 | log }} → 3 |
dateOffset |
Date | Add an ISO 8601 duration to a date. | {{ "2026-01-01" | dateOffset: "P30D" }} → 2026-01-31 |
dateOffset duration strings
dateOffset accepts any ISO 8601 duration string. Use these as a starting point:
| Duration | Meaning |
|---|---|
P1D |
+ 1 day |
P7D |
+ 1 week |
P30D |
+ 30 days |
P1M |
+ 1 month |
P1Y |
+ 1 year |
PT1H |
+ 1 hour |
PT30M |
+ 30 minutes |
-P1D |
− 1 day (negate with leading -) |
Combine into compound durations: P1Y2M3DT4H5M = 1 year 2 months 3 days 4 hours 5 minutes.
Whitelisted standard filters
LiquidJS ships ~40 default filters. Taskade enables 17 of them: the safe, useful ones. Everything else throws `Filter '<name>' is disabled` so flows fail loudly instead of running unexpected logic.
| Category | Filter | Example |
|---|---|---|
| String | upcase |
{{ "hello" | upcase }} → HELLO |
| String | downcase |
{{ "HELLO" | downcase }} → hello |
| String | strip |
{{ " hi " | strip }} → hi |
| String | url_encode |
{{ "a b&c" | url_encode }} → a%20b%26c |
| String | url_decode |
{{ "a%20b" | url_decode }} → a b |
| Date | date |
{{ created | date: "%Y-%m-%d" }} → 2026-05-13 |
| Date | date_to_long_string |
{{ created | date_to_long_string }} → 13 May 2026 |
| Date | date_to_string |
{{ created | date_to_string }} → 13 May 2026 |
| Date | date_to_xmlschema |
{{ created | date_to_xmlschema }} → 2026-05-13T00:00:00Z |
| Math | plus |
{{ 5 | plus: 3 }} → 8 |
| Math | minus |
{{ 5 | minus: 3 }} → 2 |
| Math | times |
{{ 5 | times: 3 }} → 15 |
| Math | divided_by |
{{ 10 | divided_by: 4 }} → 2.5 |
| Math | modulo |
{{ 10 | modulo: 3 }} → 1 |
| Math | abs |
{{ -7 | abs }} → 7 |
| Math | ceil |
{{ 4.2 | ceil }} → 5 |
| Math | floor |
{{ 4.8 | floor }} → 4 |
Anything outside this list is intentionally disabled: replace, split, slice, truncate, escape, and similar. Reach for the Utility Actions (JSON Extract, Transform Array, Text Replace) when you need that kind of work.
Common recipes
Concrete patterns you can lift straight into a flow.
Base64 a Basic Auth header
Authorization: Basic {{ "user:password" | base64encode }}
Pad an invoice number
INV-{{ invoiceNumber | padStart: 8, "0" }}
→ INV-00001042
Add 14 days to a contract date
{{ trigger.body.signedAt | dateOffset: "P14D" | date: "%Y-%m-%d" }}
Camelize a slug for a JS API call
{ "{{ name | camelize }}": true }
Build a clean JSON body
{
"title": "{{ task.title | encodeJsonValue }}",
"url": "{{ task.url | url_encode }}"
}
Format a percentage
{{ completed | times: 100 | divided_by: total | ceil }}%
Where you can use filters
Filters work inside any rendered field. Wherever you can type @variable or {{ }}, you can also append a filter pipeline.
| Surface | Filters work here? |
|---|---|
| HTTP Request body / headers / URL | ✅ |
| Webhook action payload | ✅ |
| Send Email body | ✅ |
| Create Task title / description | ✅ |
| Custom Field updates | ✅ |
| Branch conditions (right side of comparison) | ✅ |
| Loop iteration variables | ✅ |
💡 Tip: Filters are case-sensitive.
{{ "foo" \| Upcase }}will fail;{{ "foo" \| upcase }}is the one that runs.
Gotchas
A few behaviors that catch flow authors off guard:
- Graceful fallback on type mismatch. If you pipe a number into a string-only filter, you get the original value back, not an error. Test in the Runs tab before relying on the output.
camelizefollows Shopify, not standard JS. It capitalizes the first letter (UserProfile), splits on_and/, and turns/into::. If you want lower-camelCase or different separators, use JSON Extract instead.encodeJsonValuestrips the outer quotes. That makes it safe to embed inside a hand-written JSON string. If you want a fully-quoted JSON value, the surrounding"chars are on you.- Disabled filters throw immediately. A flow that uses
replacewill fail on render, not at runtime. You'll see it in the Runs tab on the first execution. - A fresh engine per render. Filters can't share state between renders. Each automation step gets its own Liquid engine.
Plan availability
Filters are available on every plan that can author automations.
| Plan | LiquidJS filters |
|---|---|
| Free, Starter | ✅ Full access inside any automation |
| Pro, Business, Max, Enterprise | ✅ Full access inside any automation |
Related guides
- Automations Execution. How rendering and retries work end-to-end
- HTTP Request Action. The most common place to use
base64encodeandurl_encode - Webhook Trigger. Receive payloads to filter through these expressions
- Utility Actions. JSON Extract, Transform Array, and Text Replace for transformations filters don't cover
- Structured Output. Typed agent responses you can pipe into filters