Skip to main content
The pipe operator (|) is a QuivaWorks-specific extension that enables powerful string concatenation and handling of missing values. It’s one of the most frequently used features for building dynamic content.
The pipe operator is not part of standard JSONPath. It’s a custom QuivaWorks feature designed to make string building easier and more intuitive.

How It Works

The pipe operator splits your expression into segments and concatenates them:
segment1|segment2|segment3
  • Static text segments are included as-is
  • Variable mappings (starting with $.) are evaluated and inserted
  • All segments are converted to strings and joined together
Important: The pipe operator forces string conversion of all values. This means objects, arrays, and primitives are all converted to strings during concatenation.
{
  "message": "Hello |$.user.name|, welcome back!"
}

Pipe Operator with Different Data Types

The trailing pipe (|) has special behavior depending on the data type:
Mapping
{
  "withPipe": "$.user.name|",
  "withoutPipe": "$.user.name",
  "numberWithPipe": "$.user.age|",
  "numberWithoutPipe": "$.user.age",
  "booleanWithPipe": "$.user.active|",
  "booleanWithoutPipe": "$.user.active"
}
With Data
{
  "user": {
    "name": "Alice",
    "age": 30,
    "active": true
  }
}
Result (same for both)
{
  "withPipe": "Alice",
  "withoutPipe": "Alice",
  "numberWithPipe": "30",
  "numberWithoutPipe": 30,
  "booleanWithPipe": "true",
  "booleanWithoutPipe": true
}
For primitive values (strings, numbers, booleans), the pipe converts them all into strings.
Key Behaviors:
Data TypeWithout Pipe $.pathWith Pipe $.path|
String/Number/BooleanValue as-isValue as string
ArrayFull arrayComma-separated string
ObjectFull object"[object Object]"
MissingProperty removedEmpty string ""
The pipe operator always forces string conversion, so use it carefully with objects and arrays.

Working with Arrays

The pipe operator provides convenient ways to work with array data.
Mapping
{
  "commaSeparated": "$.order.items|",
  "withLabel": "Items: |$.order.items|"
}
With Data
{
  "order": {
    "items": ["Widget", "Gadget", "Doohickey"]
  }
}
Result
{
  "commaSeparated": "Widget,Gadget,Doohickey",
  "withLabel": "Items: Widget,Gadget,Doohickey"
}
Arrays are automatically joined with commas.
When to use pipe with arrays:
  • ✅ Simple arrays of strings or numbers: ["a", "b", "c"]"a,b,c"
  • ✅ When you want a comma-separated list
  • ❌ Arrays of objects - will result in "[object Object],[object Object]"
  • ❌ When you need to preserve array structure for further processing

Working with Objects

When working with nested objects, be careful with the pipe operator.
Mapping
{
  "street": "$.user.address.street|",
  "city": "$.user.address.city|",
  "fullAddress": "$.user.address.street|, |$.user.address.city|, |$.user.address.state|"
}
With Data
{
  "user": {
    "address": {
      "street": "123 Main St",
      "city": "Boston",
      "state": "MA"
    }
  }
}
Result
{
  "street": "123 Main St",
  "city": "Boston",
  "fullAddress": "123 Main St, Boston, MA"
}
✅ Access nested properties individually for concatenation.
Common Mistake:
// ❌ Wrong - will return "[object Object]"
{"address": "$.user.address|"}

// ✅ Correct - preserves object
{"address": "$.user.address"}

// ✅ Correct - access specific properties
{"address": "$.user.address.street|, |$.user.address.city|"}
Combine static text with dynamic values from your flow.
Mapping
{
  "greeting": "Hello |$.fetch_user.name|!",
  "message": "Your order |$.trigger.order_id| is ready",
  "status": "Processing order for |$.customer.email|"
}
With Data
{
  "trigger": {"order_id": "ORD-123"},
  "fetch_user": {"name": "Alice"},
  "customer": {"email": "[email protected]"}
}
Result
{
  "greeting": "Hello Alice!",
  "message": "Your order ORD-123 is ready",
  "status": "Processing order for [email protected]"
}
The pipe operator automatically converts all values to strings during concatenation, so you can mix numbers, booleans, and strings freely.

Joining Values

Use the pipe operator with a separator to join multiple values.
Mapping
{
  "fullName": "$.user.firstName| |$.user.lastName",
  "fullAddress": "$.user.title| |$.user.firstName| |$.user.lastName"
}
Result
{
  "fullName": "Alice Smith",
  "fullAddress": "Dr. Alice Smith"
}
Each | | adds a space between values.

Handling Missing Values

The pipe operator provides elegant solutions for handling missing or undefined data.
Mapping
{
  "phone": "$.user.phone|",
  "address": "$.user.address|",
  "notes": "$.order.notes|"
}
With Data (missing properties)
{
  "user": {
    "name": "Alice"
  },
  "order": {
    "id": "ORD-123"
  }
}
Result
{
  "phone": "",
  "address": "",
  "notes": ""
}
Trailing pipe ensures properties exist with empty strings instead of being removed.
Without a trailing pipe:
{"value": "$.missing.property"}  // Property removed from output
With a trailing pipe:
{"value": "$.missing.property|"}  // Returns: ""
The trailing pipe ensures the property exists in the output with an empty string value instead of being removed.

Building URLs and Endpoints

Construct dynamic URLs using the pipe operator.
Mapping
{
  "userEndpoint": "https://api.example.com/users/|$.trigger.user_id|",
  "orderEndpoint": "https://api.example.com/orders/|$.trigger.order_id|/details",
  "searchUrl": "https://example.com/search?q=|$.query.term|&limit=|$.query.limit|"
}
With Data
{
  "trigger": {
    "user_id": "USR-789",
    "order_id": "ORD-456"
  },
  "query": {
    "term": "laptop",
    "limit": 10
  }
}
Result
{
  "userEndpoint": "https://api.example.com/users/USR-789",
  "orderEndpoint": "https://api.example.com/orders/ORD-456/details",
  "searchUrl": "https://example.com/search?q=laptop&limit=10"
}

Email and Message Templates

Create personalized email content and notifications.
Mapping
{
  "orderConfirmation": "Order |$.order.id| Confirmed - Thank you |$.customer.firstName|!",
  "shipmentNotice": "Your order |$.order.id| has shipped!",
  "invoice": "Invoice #|$.invoice.number| for |$.customer.company|"
}

Complex Concatenation

Combine multiple data sources and formats.
{
  "summary": "Customer |$.customer.name| (|$.customer.tier| tier) placed order |$.order.id| on |$.order.date| for $|$.order.total|. Shipping to |$.shipping.address.city|, |$.shipping.address.state|.",
  
  "details": "Order: |$.order.id|\nCustomer: |$.customer.firstName| |$.customer.lastName|\nEmail: |$.customer.email|\nPhone: |$.customer.phone|\nTotal: $|$.order.total|\nStatus: |$.order.status|\nEstimated Delivery: |$.shipping.estimatedDate|"
}
Result:
{
  "summary": "Customer Alice Smith (premium tier) placed order ORD-12345 on 2025-10-08 for $299.99. Shipping to San Francisco, CA.",
  
  "details": "Order: ORD-12345
Customer: Alice Smith
Email: [email protected]
Phone: +1-555-0123
Total: $299.99
Status: confirmed
Estimated Delivery: 2025-10-15"
}

JSONPath with Pipe Operator

Combine JSONPath queries with string concatenation.
Mapping
{
  "firstItem": "First item: |$.items[0].name| ($|$.items[0].price|)",
  "lastItem": "Last item: |$.items[-1].name| ($|$.items[-1].price|)"
}

Common Patterns

{
  "fullName": "$.user.firstName| |$.user.lastName",
  "displayName": "$.user.title| |$.user.firstName| |$.user.lastName|, |$.user.suffix|"
}
{
  "fullAddress": "$.address.street|, |$.address.city|, |$.address.state| |$.address.zip|",
  "singleLine": "$.address.street| |$.address.city| |$.address.state| |$.address.zip|"
}
{
  "isoDate": "$.date.year|-|$.date.month|-|$.date.day|",
  "usDate": "$.date.month|/|$.date.day|/|$.date.year|",
  "display": "$.date.month| |$.date.day|, |$.date.year|"
}
{
  "price": "$|$.product.price|",
  "withCurrency": "$.order.total| |$.order.currency|",
  "detailed": "Total: $|$.order.subtotal| + $|$.order.tax| = $|$.order.total|"
}
{
  "summary": "Order contains: |$.items[0].name|, |$.items[1].name|, and |$.items[2].name|",
  "count": "Total items: |$.items.length|",
  "simpleList": "Tags: |$.tags|"
}
Note: Use $.tags| to join simple arrays. For arrays of objects, access individual items.
{
  "tagsAsArray": "$.product.tags",
  "tagsAsString": "$.product.tags|",
  "customFormat": "Tags: |$.product.tags[0]|, |$.product.tags[1]|, |$.product.tags[2]|"
}
Choose based on whether you need array structure or a readable string.

Best Practices

1

Use Trailing Pipes for Optional Fields

Add | at the end when you want to ensure empty strings instead of undefined:
{"optional": "$.user.middleName|"}
2

Be Consistent with Spacing

Choose a spacing style and stick with it:
{
  "withSpaces": "$.a| |$.b| |$.c",
  "withCommas": "$.a|, |$.b|, |$.c|"
}
3

Handle Missing Data Gracefully

Structure your templates to work even when optional data is missing:
{
  "message": "Status: |$.status||. |$.details||"
}
4

Test with Missing Values

Always test your pipe expressions with partial data to ensure they degrade gracefully.
5

Keep It Readable

For complex concatenations, consider breaking into multiple fields:
{
  "firstName": "$.user.firstName|",
  "lastName": "$.user.lastName|",
  "fullName": "$.user.firstName| |$.user.lastName|"
}

Performance Tips

Efficient

Simple concatenations with few variables:
"Hello |$.name|!"

Less Efficient

Complex expressions with many variables:
"|$.a||$.b||$.c||$.d||$.e||$.f|"
The pipe operator is evaluated during mapping, so there’s minimal performance impact. However, excessively long concatenations with dozens of variables may be better split into multiple fields.

Debugging Tips

If parts of your string are missing:
  1. Check that the source path exists
  2. Verify the node has executed
  3. Use the Flow tester to inspect data
  4. Add trailing pipes to see empty strings: $.value|
If concatenation doesn’t work as expected:
  1. Verify $. prefix on all variable paths
  2. Check for typos in node IDs or property names
  3. Test each variable separately first
  4. Ensure all segments are separated by |
If you need a literal pipe character in your output:Unfortunately, there’s no escape for literal | in pipe expressions. Consider:
  • Using a different character
  • Building the string in multiple steps
  • Using a static field with the pipe character
If you’re seeing "[object Object]" in your output:Cause: You’re using a trailing pipe with an object:
{"address": "$.user.address|"}  // ❌ Returns "[object Object]"
Solutions:
  1. Remove the pipe to preserve the object:
{"address": "$.user.address"}  // ✅ Returns full object
  1. Access individual properties:
{"address": "$.user.address.street|, |$.user.address.city|"}
If your array is appearing as a comma-separated string:Cause: You’re using a trailing pipe with an array:
{"items": "$.order.items|"}  // Returns "item1,item2,item3"
Solutions:
  • Remove pipe to preserve array: {"items": "$.order.items"}
  • Keep pipe if you want comma-separated string
  • Use JSONPath to select specific items: $.order.items[0]

What’s Next?

Questions? Check the Reference or visit our Help Center.