Menu
Connectors
Shopify
Manage your Shopify store — products, variants, collections, orders, customers, draft orders, inventory, discounts, refunds, and webhooks — via the Admin REST API. Covers path/query/body parameters and response shapes for every action.
On this page
Connecting Shopify
Every action needs a Shopify connection configured with:
- Shop Domain — your store's
.myshopify.comdomain, withouthttps://(e.g.my-store.myshopify.com). - Admin API Access Token — from a custom app (or legacy private app) created in your Shopify admin under Settings → Apps and sales channels → Develop apps. It's sent as the
X-Shopify-Access-Tokenheader on every request automatically — there's no per-action auth configuration.
All requests go to the Shopify Admin REST API at https://<shop-domain>/admin/api/2026-07.
Reading the response
Every action's result lands at {{<stepReference>.response...}} (see Step Reference), in the same envelope the HTTP Connector uses:
{{<stepReference>.response.status}}— the HTTP status code.{{<stepReference>.response.statusText}}— the HTTP status text.{{<stepReference>.response.headers.<header-name>}}— a response header, e.g.{{<stepReference>.response.headers.link}}for pagination.{{<stepReference>.response.data.<field>}}— the parsed Shopify response body.
Shopify wraps a single resource in a key named after it and a list in the plural key. So a step named "Get Order" (reference getOrder) resolves the order's fields at {{getOrder.response.data.order.total_price}}, and a "List Orders" step resolves the array at {{listOrders.response.data.orders}} (e.g. {{listOrders.response.data.orders[0].id}}). Each action below states its own exact path.
Delete actions are the one exception to this envelope. On success, Delete Product, Delete Product Variant, and Delete Webhook return a plain { "success": true, ... } object directly — use {{<stepReference>.response.success}}, not {{<stepReference>.response.data...}}. If Shopify returns an error instead, the normal envelope is returned, so {{<stepReference>.response.status}} only resolves in that failure case.
Error handling
A non-2xx response from Shopify does not fail the step — it's returned as a normal result with the real status code and Shopify's error detail at {{<stepReference>.response.data.errors}}, so you can branch on {{<stepReference>.response.status}} in the workflow. A network failure (Shopify unreachable) is returned as a synthetic 503, and any other unexpected error as a synthetic 500 — both still in the same envelope shape rather than throwing.
JSON string fields
Several create/update actions take structured data (line items, addresses, variants, images, discounts) as a JSON string typed into a text field — e.g. Create Order's Line Items is [{"variant_id": 123456, "quantity": 2}]. If the string isn't valid JSON the step fails with an "Invalid <field> JSON" error before any request is sent.
Pagination
List actions accept a Limit (1–250, default 50). List Products also accepts a Page Info cursor — take it from the previous response's Link header ({{listProducts.response.headers.link}}) and feed it back in to page forward. Other list actions page by narrowing with date/status filters.
Get Product
Retrieve a single product by ID, with its variants, images, and metadata.
Configuring it
- Path — Product ID (
productId), interpolated into/products/{productId}.json. Required. - Query — Fields (
fields), a comma-separated list of fields to return (e.g.id,title,variants,images). Optional.
Reading the response
The product is at {{getProduct.response.data.product}} — e.g. {{getProduct.response.data.product.title}}, {{getProduct.response.data.product.variants}}, {{getProduct.response.data.product.status}}.
List Products
List products with optional filters for status, vendor, type, and collection.
Configuring it
All fields are query parameters:
- Limit (
limit) — page size, defaults to50if left blank. - Status (
status) —active,archived, ordraft. - Vendor (
vendor) — filter by vendor name. - Product Type (
product_type) — filter by product type. - Collection ID (
collection_id) — return products in this collection. - Title (
title) — filter by product title. - Page Info (
page_info) — pagination cursor from a previous response'sLinkheader.
Reading the response
Matching products are an array at {{listProducts.response.data.products}} (e.g. {{listProducts.response.data.products[0].id}}).
Create Product
Create a new product.
Configuring it
All fields are request body, sent under product:
- Title (
title) — required. - Description (HTML) (
body_html) — optional. - Vendor (
vendor) — optional. - Product Type (
product_type) — optional. - Status (
status) —active,draft, orarchived. Defaults toactive. - Tags (
tags) — comma-separated tags string. - Variants (
variants) — a JSON array string of variant objects, e.g.[{"price":"29.99","sku":"SKU001","inventory_quantity":100}]. - Images (
images) — a JSON array string of image objects, e.g.[{"src":"https://example.com/image.jpg","alt":"Product image"}].
Reading the response
The created product is at {{createProduct.response.data.product}} — e.g. {{createProduct.response.data.product.id}}, {{createProduct.response.data.product.handle}}.
Example
Set Title to Classic Running Shoe, Status to active, and Variants to [{"price":"79.99","sku":"SHOE-001","inventory_quantity":25}] to publish a product with one variant.
Update Product
Update an existing product's title, description, status, or metadata.
Configuring it
- Path — Product ID (
productId), interpolated into/products/{productId}.json. Required. - Body (sent under
product) — all optional; only fields you set are sent: Title (title), Description (HTML) (body_html), Vendor (vendor), Product Type (product_type), Status (status—active/draft/archived), Tags (tags).
Reading the response
The updated product is at {{updateProduct.response.data.product}} — e.g. {{updateProduct.response.data.product.updated_at}}.
Delete Product
Permanently delete a product.
Configuring it
- Path — Product ID (
productId), interpolated into/products/{productId}.json. Required.
Reading the response
On success this returns a bare object, not the envelope: {{deleteProduct.response.success}} is true and {{deleteProduct.response.productId}} echoes the ID. On failure, check {{deleteProduct.response.status}}.
List Product Variants
Get all variants for a product.
Configuring it
- Path — Product ID (
productId), interpolated into/products/{productId}/variants.json. Required. - Query — Limit (
limit), defaults to50.
Reading the response
Variants are an array at {{listProductVariants.response.data.variants}}.
Create Product Variant
Add a variant (size, color, SKU) to an existing product.
Configuring it
- Path — Product ID (
productId), interpolated into/products/{productId}/variants.json. Required. - Body (sent under
variant):- Price (
price) — required, a string like"29.99". - Title (
title) — e.g."Large / Blue". - SKU (
sku). - Option 1 (
option1), Option 2 (option2) — option values (e.g. size, color). - Inventory Quantity (
inventory_quantity) — a number. - Inventory Management (
inventory_management) —shopifyornot_managed. Defaults toshopify. - Inventory Policy (
inventory_policy) —denyorcontinue. Defaults todeny. - Requires Shipping (
requires_shipping) — defaults totrue. - Taxable (
taxable) — defaults totrue. - Barcode (
barcode).
- Price (
The handler also passes an option3 value through to Shopify if provided, but there is no Option 3 field in the action's configuration form, so it can't currently be set. weight / weight_unit are likewise not exposed.
Reading the response
The created variant is at {{createProductVariant.response.data.variant}} — e.g. {{createProductVariant.response.data.variant.id}}.
Update Product Variant
Update a variant's price, SKU, or inventory.
Configuring it
- Path — Variant ID (
variantId), interpolated into/variants/{variantId}.json. Required. - Body (sent under
variant) — all optional: Price (price), Compare-At Price (compare_at_price), SKU (sku), Title (title), Option 1 (option1), Option 2 (option2), Inventory Quantity (inventory_quantity), Inventory Management (inventory_management), Inventory Policy (inventory_policy), Barcode (barcode).
Reading the response
The updated variant is at {{updateProductVariant.response.data.variant}}.
Delete Product Variant
Remove a variant from a product.
Configuring it
- Path — Product ID (
productId) and Variant ID (variantId), interpolated into/products/{productId}/variants/{variantId}.json. Both required.
Reading the response
Bare object on success: {{deleteProductVariant.response.success}} is true, {{deleteProductVariant.response.variantId}} echoes the ID.
Get Collection
Retrieve a single collection by ID.
Configuring it
- Path — Collection ID (
collectionId). Required. - Collection Type (
collectionType) —customorsmart, defaultcustom. This is not sent to Shopify as a parameter; it selects the endpoint:custom→/custom_collections/{collectionId}.json,smart→/smart_collections/{collectionId}.json.
Reading the response
A custom collection is at {{getCollection.response.data.custom_collection}}; a smart collection at {{getCollection.response.data.smart_collection}}.
List Collections
List custom or smart collections.
Configuring it
- Collection Type (
collectionType) —custom(default) orsmart; selects the endpoint (/custom_collections.jsonor/smart_collections.json), not sent as a parameter. - Query — Limit (
limit, default50), Title Filter (title).
Reading the response
{{listCollections.response.data.custom_collections}} or {{listCollections.response.data.smart_collections}} depending on the type.
Create Collection
Create a new custom collection.
Configuring it
All fields are request body, sent under custom_collection:
- Title (
title) — required. - Description (HTML) (
body_html) — optional. - Sort Order (
sort_order) — one ofmanual,best-selling,alpha-asc,alpha-desc,price-desc,price-asc,created,created-desc. Defaults tomanual. - Image URL (
image_src) — sent to Shopify nested asimage.src. - Published (
published) — defaults totrue.
Reading the response
The created collection is at {{createCollection.response.data.custom_collection}} — e.g. {{createCollection.response.data.custom_collection.id}}, {{createCollection.response.data.custom_collection.handle}}.
Get Order
Retrieve a single order with its line items, customer, fulfillment status, and payment details.
Configuring it
- Path — Order ID (
orderId), interpolated into/orders/{orderId}.json. Required. - Query — Fields (
fields), comma-separated fields to return. Optional.
Reading the response
The order is at {{getOrder.response.data.order}} — e.g. {{getOrder.response.data.order.total_price}}, {{getOrder.response.data.order.financial_status}}, {{getOrder.response.data.order.line_items}}.
List Orders
List orders with filters for status, payment, and fulfillment.
Configuring it
All fields are query parameters:
- Limit (
limit) — defaults to50. - Status (
status) —open,closed,cancelled, orany. Defaults toany. - Financial Status (
financial_status) —authorized,pending,paid,partially_paid,refunded,voided,partially_refunded, orunpaid. - Fulfillment Status (
fulfillment_status) —shipped,partial,unshipped, orunfulfilled. - Customer ID (
customer_id). - Created After (
created_at_min) / Created Before (created_at_max) — ISO 8601 date-times.
The handler also forwards a fields value as a query parameter if present, but there is no Fields field in this action's configuration form, so it can't currently be set.
Reading the response
Orders are an array at {{listOrders.response.data.orders}}.
Create Order
Programmatically create a new order.
Configuring it
All fields are request body, sent under order:
- Line Items (
line_items) — required, a JSON array string, e.g.[{"variant_id": 123456, "quantity": 2}]. - Customer Email (
email). - Customer (
customer) — a JSON object string, e.g.{"id": 123}or{"email": "...", "first_name": "..."}. - Shipping Address (
shipping_address) / Billing Address (billing_address) — JSON object strings. - Financial Status (
financial_status) —pending,authorized, orpaid. Defaults topending. - Note (
note), Tags (tags). - Send Receipt Email (
send_receipt) — defaults tofalse.
Reading the response
The created order is at {{createOrder.response.data.order}} — e.g. {{createOrder.response.data.order.id}}, {{createOrder.response.data.order.order_number}}, {{createOrder.response.data.order.total_price}}.
Example
Set Line Items to [{"variant_id": 123456, "quantity": 1}], Customer Email to customer@example.com, and Financial Status to paid to record a paid single-item order.
Update Order
Update an order's note, tags, or email.
Configuring it
- Path — Order ID (
orderId), interpolated into/orders/{orderId}.json. Required. - Body (sent under
order) — Note (note), Tags (tags), Email (email).
Reading the response
{{updateOrder.response.data.order.id}}, {{updateOrder.response.data.order.updated_at}}.
Cancel Order
Cancel an order, with an optional refund and restock.
Configuring it
- Path — Order ID (
orderId), interpolated into/orders/{orderId}/cancel.json. Required. - Body (sent flat, not wrapped):
- Cancellation Reason (
reason) —customer,fraud,inventory,declined, orother. Defaults toother. - Send Cancellation Email (
email) — defaults totrue. - Restock Items (
restock) — defaults totrue. - Refund Amount (
amount) — leave empty for a full refund. - Currency (
currency).
- Cancellation Reason (
Reading the response
{{cancelOrder.response.data.order.cancelled_at}}, {{cancelOrder.response.data.order.cancel_reason}}.
Create Fulfillment
Mark an order (or its line items) fulfilled and attach tracking.
Configuring it
- Path — Order ID (
orderId), interpolated into/orders/{orderId}/fulfillments.json. Required. - Body (sent under
fulfillment):- Location ID (
location_id) — required, the Shopify location the fulfillment ships from. - Tracking Number (
tracking_number). - Tracking Company (
tracking_company) — carrier name (e.g.UPS). - Tracking URLs (
tracking_urls) — a JSON array string of URL strings; if it isn't valid JSON the single string is wrapped in an array. - Notify Customer (
notify_customer) — defaults totrue.
- Location ID (
Reading the response
{{createFulfillment.response.data.fulfillment.id}}, {{createFulfillment.response.data.fulfillment.status}}.
Get Customer
Retrieve a single customer by ID.
Configuring it
- Path — Customer ID (
customerId), interpolated into/customers/{customerId}.json. Required. - Query — Fields (
fields), comma-separated. Optional.
Reading the response
The customer is at {{getCustomer.response.data.customer}} — e.g. {{getCustomer.response.data.customer.email}}, {{getCustomer.response.data.customer.orders_count}}, {{getCustomer.response.data.customer.total_spent}}.
List Customers
List customers with optional filtering.
Configuring it
All fields are query parameters: Limit (limit, default 50), Email Filter (email), Tags Filter (tags), Created After (created_at_min).
Reading the response
Customers are an array at {{listCustomers.response.data.customers}}.
Create Customer
Create a new customer account.
Configuring it
All fields are request body, sent under customer:
- Email (
email) — required. - First Name (
first_name), Last Name (last_name), Phone (phone), Tags (tags), Note (note). - Verified Email (
verified_email) — defaults totrue. - Accepts Marketing (
accepts_marketing) — defaults tofalse. - Addresses (
addresses) — a JSON array string of address objects. - Send Welcome Email (
send_email_welcome) — defaults tofalse.
Reading the response
{{createCustomer.response.data.customer.id}}, {{createCustomer.response.data.customer.created_at}}.
Update Customer
Update a customer's profile.
Configuring it
- Path — Customer ID (
customerId), interpolated into/customers/{customerId}.json. Required. - Body (sent under
customer) — all optional: First Name (first_name), Last Name (last_name), Email (email), Phone (phone), Tags (tags), Note (note), Accepts Marketing (accepts_marketing).
Reading the response
{{updateCustomer.response.data.customer.id}}, {{updateCustomer.response.data.customer.updated_at}}.
Search Customers
Find customers by email, name, phone, or tags.
Configuring it
All fields are query parameters:
- Search Query (
query) — required, e.g.email:customer@example.comorfirst_name:John. - Limit (
limit) — defaults to50. - Order By (
order) — sort expression, e.g.last_order_date DESC.
The handler also forwards a fields query parameter if present, but there is no Fields field in this action's configuration form, so it can't currently be set.
Reading the response
Matching customers are an array at {{searchCustomers.response.data.customers}}.
Get Draft Order
Retrieve a single draft order by ID.
Configuring it
- Path — Draft Order ID (
draftOrderId), interpolated into/draft_orders/{draftOrderId}.json. Required.
Reading the response
{{getDraftOrder.response.data.draft_order}}.
List Draft Orders
List draft orders with an optional status filter.
Configuring it
Query parameters: Status (status — open, invoice_sent, or completed), Limit (limit, default 50).
Reading the response
{{listDraftOrders.response.data.draft_orders}}.
Create Draft Order
Create a draft order (quote).
Configuring it
All fields are request body, sent under draft_order:
- Line Items (
line_items) — required, a JSON array string, e.g.[{"variant_id": 123, "quantity": 1, "price": "29.99"}]. - Customer ID (
customer_id) — sent nested ascustomer.id. - Email (
email), Note (note), Tags (tags). - Discount (
discount) — a JSON object string, sent asapplied_discount, e.g.{"value_type": "percentage", "value": "10", "description": "10% off"}. - Shipping Address (
shipping_address) — a JSON object string.
The handler additionally supports a send_invoice flag (which triggers a follow-up call to /draft_orders/{id}/send_invoice.json), but there is no Send Invoice field in this action's configuration form, so it can't currently be set. billing_address and use_customer_default_address are likewise not exposed.
Reading the response
{{createDraftOrder.response.data.draft_order.id}}, {{createDraftOrder.response.data.draft_order.invoice_url}}, {{createDraftOrder.response.data.draft_order.status}}.
Update Draft Order
Update a draft order's line items, note, tags, or email before it's completed.
Configuring it
- Path — Draft Order ID (
draftOrderId), interpolated into/draft_orders/{draftOrderId}.json. Required. - Body (sent under
draft_order) — Line Items (line_items, a JSON array string that replaces the existing items), Note (note), Tags (tags), Email (email).
Reading the response
{{updateDraftOrder.response.data.draft_order}}.
Complete Draft Order
Convert a draft order into a real order.
Configuring it
- Path — Draft Order ID (
draftOrderId), interpolated into/draft_orders/{draftOrderId}/complete.json. Required. - Query — Payment Pending (
payment_pending) — settrueif payment has not been collected yet. Defaults tofalse.
Reading the response
{{completeDraftOrder.response.data.draft_order.order_id}} is the ID of the created order; {{completeDraftOrder.response.data.draft_order.status}}.
Get Inventory Levels
Retrieve inventory levels for items across locations.
Configuring it
All fields are query parameters:
- Inventory Item IDs (
inventory_item_ids) — comma-separated inventory item IDs. - Location IDs (
location_ids) — comma-separated location IDs. - Limit (
limit) — defaults to50.
Reading the response
Levels are an array at {{getInventoryLevels.response.data.inventory_levels}} — each entry has inventory_item_id, location_id, and available.
Adjust Inventory Level
Add to or subtract from the available quantity of an item at a location.
Configuring it
All fields are request body (sent flat):
- Location ID (
location_id) — required, sent as a number. - Inventory Item ID (
inventory_item_id) — required, sent as a number. - Quantity Adjustment (
available_adjustment) — required, a number: positive to add stock, negative to reduce.
Reading the response
{{adjustInventoryLevel.response.data.inventory_level.available}} is the new available quantity.
Example
Location ID 123456789, Inventory Item ID 987654321, Quantity Adjustment -1 to decrement stock by one after a manual sale.
List Price Rules
List all discount price rules in the store.
Configuring it
Query parameters: Limit (limit, default 50), Title Filter (title).
Reading the response
{{listPriceRules.response.data.price_rules}}.
Create Price Rule
Create a discount price rule (the discount campaign configuration). Use Create Discount Code afterward to generate the code customers enter.
Configuring it
All fields are request body, sent under price_rule:
- Title (
title) — required, internal name. - Target Type (
target_type) — required,line_itemorshipping_line. - Value Type (
value_type) — required,fixed_amountorpercentage. - Value (
value) — required, the discount value; must be negative, e.g.-10.00. - Allocation Method (
allocation_method) —acrossoreach. Defaults toacross. - Customer Selection (
customer_selection) —allorprerequisite. Defaults toall. - Starts At (
starts_at) — ISO 8601; defaults to now. - Ends At (
ends_at) — ISO 8601. - Usage Limit (
usage_limit) — max total uses; leave empty for unlimited. - Once Per Customer (
once_per_customer) — defaults tofalse. - Minimum Order Amount (
prerequisite_subtotal_range) — sent nested asprerequisite_subtotal_range.greater_than_or_equal_to.
Reading the response
{{createPriceRule.response.data.price_rule.id}} — pass this to Create Discount Code.
Create Discount Code
Generate a customer-facing discount code for an existing price rule.
Configuring it
- Path — Price Rule ID (
price_rule_id), interpolated into/price_rules/{price_rule_id}/discount_codes.json. Required. - Body (sent under
discount_code) — Discount Code (code), required, e.g.SUMMER10.
Reading the response
{{createDiscountCode.response.data.discount_code.id}}, {{createDiscountCode.response.data.discount_code.code}}.
Calculate Refund
Preview refund amounts for an order before issuing the refund.
Configuring it
- Path — Order ID (
orderId), interpolated into/orders/{orderId}/refunds/calculate.json. Required. - Body (sent under
refund):- Shipping Refund (
shipping) — a JSON object string, e.g.{"full_refund": true}or{"amount": "5.00"}. - Line Items to Refund (
line_items) — a JSON array string, e.g.[{"line_item_id": 123, "quantity": 1, "restock_type": "return"}]. - Currency (
currency).
- Shipping Refund (
Reading the response
{{calculateRefund.response.data.refund.transactions}} holds the suggested transaction amounts to pass to Create Refund.
Create Refund
Issue a refund for an order.
Configuring it
- Path — Order ID (
orderId), interpolated into/orders/{orderId}/refunds.json. Required. - Body (sent under
refund):- Note (
note) — internal note. - Notify Customer (
notify) — defaults totrue. - Restock Items (
restock) — defaults totrue. - Line Items to Refund (
line_items) — a JSON array string. - Transactions (
transactions) — a JSON array string, e.g.[{"parent_id": 456, "amount": "29.99", "kind": "refund", "gateway": "bogus"}]. - Shipping Refund (
shipping) — a JSON object string.
- Note (
Reading the response
{{createRefund.response.data.refund.id}}, {{createRefund.response.data.refund.transactions}}.
Create Webhook
Register a webhook so Shopify sends events to your endpoint.
Configuring it
All fields are request body, sent under webhook:
- Topic (
topic) — required. One of the supported Shopify topics, includingorders/create,orders/updated,orders/cancelled,orders/fulfilled,orders/paid,products/create,products/update,products/delete,customers/create,customers/update,inventory_levels/update,fulfillments/create,refunds/create,checkouts/create,carts/update, andshop/update(among others). - Endpoint URL (
address) — required, an HTTPS URL to receive the payload. - Format (
format) —jsonorxml. Defaults tojson.
Reading the response
{{createWebhook.response.data.webhook.id}} — keep this to delete the webhook later.
Example
To drive a workflow off new orders, point a Shopify webhook with topic orders/create at that workflow's webhook trigger URL.
Delete Webhook
Unregister a webhook by ID.
Configuring it
- Path — Webhook ID (
webhookId), interpolated into/webhooks/{webhookId}.json. Required.
Reading the response
Bare object on success: {{deleteWebhook.response.success}} is true, {{deleteWebhook.response.webhookId}} echoes the ID.
Also applies here
Step Name
What it's for
Every step in a workflow gets a name — either one you set or a default based on the connector and action (e.g. "Get Order Details", "Send Welcome Email"). It's shown throughout the UI and in your execution history, and it's also the source for the step's Reference — a camelCase identifier auto-generated from the name (e.g. "Get Order Details" → getOrderDetails) — which is what you actually use in {{...}} expressions to read this step's output from later steps. See Step Reference.
Rules
- Must be at least 2 characters, and 50 characters or fewer.
- Must be unique within the workflow — reusing a name that's already taken will be rejected, with a suggested alternative (e.g.
"Get Order Details 2"). - Can't be empty.
Tips
- Prefer a descriptive, human-readable name over a generic one — "Get Order Details" is easier to work with later than "HTTP Request 2", especially once a workflow has a dozen steps.
- Renaming a step updates every reference to it elsewhere in the workflow automatically.
Step Reference
Syntax
Any input field can reference earlier data using {{expression}}. The expression is evaluated as JSONata — so simple dot-paths and more advanced queries (filters, functions) both work.
Referencing a step's output
Use the step's Reference — a camelCase identifier auto-generated from its Name (e.g. "Get Order Details" → getOrderDetails), shown read-only wherever the step's fields are configured — followed by the field path. Elsewhere in these docs this general pattern is written as {{<stepReference>.field.path}}:
{{getOrderDetails.response.data.id}}
{{getOrderDetails.response.status}}
The raw display name won't work here even though it's what you see in the UI — {{Get Order Details.response.data.id}} isn't valid, since a bare name containing spaces isn't a single JSONata identifier. Always use the camelCase Reference.
You can also reference steps by position instead of by reference:
{{steps[0].response.data.id}}
Referencing trigger data
{{trigger.headers.authorization}}
{{trigger.body.customerId}}
{{trigger.query.page}}
{{trigger.params.orderId}}
{{trigger.method}}
{{trigger.path}}
Referencing workflow variables
{{variables.myVariable}}
See Variables for the full list of variable types and more examples, including connection-type variables.
Referencing runtime variables
A separate, mutable namespace written by the Variable connector while a run is in progress — not the same as the workflow-level variables above:
{{runtimeVariables.myVariable}}
Notes
- If an expression can't be resolved (a typo in a step name, a field that doesn't exist), it resolves to an empty string rather than failing the workflow — check your execution history if a value comes through blank.
- Object values are automatically JSON-stringified when interpolated into a string field.
Execution Settings
Fire-and-forget
When enabled, the workflow doesn't wait for this step to complete before moving to the next one. Use it for steps whose result nothing downstream depends on — logging, analytics, notifications — so they don't add latency to the steps that matter.
Continue on error
When enabled, a failure in this step doesn't stop the workflow — execution continues to the next step. The failure is still recorded in the execution history; this just controls whether it's fatal.
Combine the two for steps that are genuinely optional to the outcome: fire-and-forget so they don't add latency, continue-on-error so a failure in them (e.g. an analytics endpoint being briefly down) doesn't take down an otherwise-successful workflow run.
Caching
What it does
When enabled, a step's result is cached for a configurable TTL (time-to-live, in seconds). If the step runs again with the same effective cache key before the TTL expires, the cached result is returned instead of re-running the step.
Configuring it
- Enabled — turn caching on or off for this step.
- TTL — how long (in seconds) a cached result stays valid.
- Cache key template — an expression (supporting the same
{{...}}step reference syntax used elsewhere) that determines what counts as "the same call". By default this is based on the step's resolved input; a custom template lets you cache more narrowly or broadly than that.
When to use it
Good candidates are steps that call something slow or rate-limited but return the same answer for the same input within a short window — a lookup against a rarely-changing external system, for example. Skip it for steps whose result must always be fresh (anything involving live inventory, pricing, or payment state).
Locking
What it does
When enabled, only one execution of this step (for a given lock key) can run at a time. If a second execution tries to run the same step while a lock is held, it waits until the lock is released or the hold period elapses.
Configuring it
- Enabled — turn locking on or off for this step.
- Lock key — an expression (supporting the same
{{...}}step reference syntax used elsewhere) that determines what counts as "the same resource". By default the lock is scoped to the step itself; a custom key lets you lock per-customer, per-order, or any other identifier that needs serialized access. - Period — how long (in seconds) the lock is held before it's automatically released, in case an execution doesn't complete normally.
When to use it
Use it whenever concurrent executions could race on the same resource — e.g. two workflow runs both trying to update the same order's status at once. A lock key scoped to the order id ensures only one of them proceeds at a time.