ShipStream Knowledge Base
Scripting

Common Expression Language

Common Expression Language, abbreviated CEL, is a small expression language used in ShipStream when you need advanced matching logic without writing a full script. CEL expressions always answer one question: does this record match?

ShipStream currently uses CEL in Product Profiles, Handling Classes, and Webhook Events Filters.

Where CEL Is Used

FeatureWhat the expression checksResult when trueResult when false
Product ProfilesProduct fieldsThe Product receives the Product ProfileThe Product does not receive the Product Profile
Handling ClassesProduct fieldsThe Product receives the Handling Class if no earlier class matchedShipStream checks the next Handling Class
Webhook Events FiltersWebhook payload fieldsThe webhook request is sentThe event is skipped and no HTTP request is made

All CEL fields are referenced by their field code. Field codes are shown in the expression builder or come from the webhook payload field names.

Basic Syntax

Use comparison operators to compare a field with a value:

status == "complete"

Use dot notation for nested webhook payload fields:

shipping_address.country_id == "US"

Use parentheses to group logic:

(source == "shopify" || source == "manual") && status == "complete"

CEL expressions must evaluate to true or false. An expression like qty_ordered + 1 is not valid by itself because it returns a number instead of a yes-or-no result.

Values

Value typeExampleNotes
Booleantrue, falseUse lowercase true and false.
String"shopify"Use double quotes around text.
Number10, 10.5Product measurement fields can be compared with whole numbers or decimals.
List["picked", "packed"]Useful with the in operator.
Empty fieldpresent(max_per_package)Product expressions can test whether a Product field has a saved value.

Operators

OperatorMeaningExample
==Equalsstatus == "complete"
!=Does not equalsource != "manual"
&&Logical ANDstatus == "complete" && source == "shopify"
||Logical ORstatus == "picked" || status == "packed"
!Logical NOT!is_global
>, <, >=, <=Number comparisonqty_ordered >= 10
inValue is in a liststatus in ["picked", "packed"]

Use && and || for AND and OR. The words and and or are not valid CEL operators.

String Functions

String functions are useful when exact matching is too strict.

FunctionMeaningExample
startsWith(prefix)String begins with a prefixorder_ref.startsWith("WEB-")
endsWith(suffix)String ends with a suffixorder_ref.endsWith("-INT")
contains(substring)String contains a substringshipping_method.contains("express")
toLower()Convert to lowercasecarrier_code.toLower() == "fedex"
toUpper()Convert to uppercasesource.toUpper() == "SHOPIFY"

Call string functions on the field value, such as field.startsWith("X"), not startsWith(field, "X").

Conversion and Size Functions

FunctionMeaningExample
int(x)Convert a value to an integerint(qty_ordered) >= 10
string(x)Convert a value to textstring(warehouse_id) == "3"
size(x)Length of a string or listsize(items) > 1

Use conversions when a field may arrive as text but you need a numeric comparison.

Empty Product Fields

Product Profile and Handling Class expressions can use present(field) to test whether a Product field has a saved value.

present(max_per_package)

Use !present(field) to match an empty field:

!present(max_per_package)

When comparing a field that might be empty, include present() if the empty case matters:

!present(max_per_package) || max_per_package <= 1.0

This example matches Products where Max Per Package is empty or no greater than 1.

Use present(), not has(), in ShipStream CEL. Some general CEL examples use has(), but it is not enabled in ShipStream.

Regular Expressions

Product Profile and Handling Class expressions support matches() for regular-expression matching:

matches(sku, "^29-")

You can also call it on a field value:

sku.matches("BOX-[0-9]+$")

Patterns are partial matches unless you anchor them with ^ and $. For example, matches(sku, "329") matches 29-329-5002.

Webhook Events Filters do not support matches(). Use startsWith(), endsWith(), or contains() for webhook filters.

Not Supported

ShipStream does not enable CEL macros because they can iterate over large lists and make expressions expensive to evaluate.

These macros are not available:

  • map
  • filter
  • all
  • exists
  • has

Use direct comparisons, in, string functions, or explicit field checks instead.

Behavior on Errors

Product Profile and Handling Class expressions are validated when you save. Expressions fail validation if they have invalid syntax, reference a field that is not available in that screen, or return something other than true or false.

Webhook Events Filters are also validated when you save, but the available payload fields depend on the event topic. If a saved webhook filter cannot be evaluated for a particular event, ShipStream sends the event as if no filter were set. This prevents a broken filter from silently dropping webhook events.

Examples

GoalExpression
Only Shopify eventssource == "shopify"
Only events from a channel family, case-insensitivesource.toLower().startsWith("shopify")
Only freight shipmentsshipment_type == "freight"
Shipments to Canadashipping_address.country_id == "CA"
One of several carrierscarrier_code in ["fedex", "ups", "usps"]
High-volume orders from one channelsource == "shopify" && int(qty_ordered) >= 10
Product field has a valuepresent(max_per_package)
Product field is empty!present(max_per_package)
Empty or no more than 1!present(max_per_package) || max_per_package <= 1.0
SKU starts with a numeric familysku.matches("^29-")