Circumstances are the primary element of Engine.

Following are some typical Circumstances you might configure Engine to handle:

Any time a user is at a restaurant on a weekend night, in an area they don’t often frequent, and they are near a bar that will be showing live music in an hour
Any time a user is at a coffee shop they’ve never been to, but the user regularly frequents other coffee shops.
Any time a user is at a big electronics store that is closed, and is near another one that is open.

Defining Circumstances

The preferred method for rolling out Circumstances in Engine is through the Garage UI. However, Engine also allows for programmatically defined Circumstances, as described below.

Programmatically, Circumstances are defined as S-expressions. We’ve chosen this format because it’s:

  • Efficient
  • Flexible
  • Familiar to most programmers
  • Platform neutral
  • Generally easy to read


  • Currently, the syntax consists of the these predicates: (and ...), (or ...), (not ...), (at ...), (near ...), (<comparison op> ...), (any-of ...), is-open, any-factual-place, has-open-hours and true. More will be added later. <comparison op> is one of =/</>/<=/>=.
  • and, or, and not can be nested in one another.
  • at/near can’t contain at/near expressions.
  • =/any-of, is-open, has-open-hours and any-factual-place can only occur within at/near expressions. Exceptions to this rule are =/anyof local-day-of-week/local-minute-of-day, which can occur anywhere.
  • Sub-expressions of at/near expressions are implicitly anded together.
  • A comparison predicate must be in the form (= factual-id "foo"), where the second element of the list is one of factual-id, category-id, chain-id, local-day-of-week, local-minute-of-day or threshold-met.
  • Only local-day-of-week and local-minute-of-day are currently allowed in comparison predicates other than =/any-of.
  • The any-of predicate must be in the form (any-of factual-id "foo" "bar"), like = but variadic.
  • An =/any-of expression whose second element is threshold-met must have values "high", "low" or "near".

The DSL runner returns only a boolean result initially. Another method returns the relevant at and near places.

Places for an at/near expression are returned if the at/near is the only filter or is only in an or. If the at/near is in an and, the places are only returned if the outermost and succeeds. An at/near inside a not never returns the places.

Currently is-open for a place that doesn’t have an hours field is false.


The following examples reference Factual Ids, category Ids, and chain Ids.

At any place (in Factual’s Places Data)

(at any-factual-place)

At any restaurant

(at (= category-id 347))
  • 347 is the category Id for Social > Food and Dining > Restaurants

At any restaurant or a bar

(or (at (= category-id 312)) (at (= category-id 347)))
  • 312 is the category Id for Social > Bars
  • 347 is the category Id for Social > Food and Dining > Restaurants

At any restaurant or a bar, using the any-of shorthand

(at (any-of category-id 312 347))

At a Starbucks near a Peet’s Coffee & Tea

(and (at (= chain-id "ab4c54c0-d68a-012e-5619-003048cad9da"))
     (near (= chain-id "e60a5d2d-6e9e-4d3b-bfe4-5cebfc0c3d97")))
  • “ab4c54c0-d68a-012e-5619-003048cad9da” is the chain Id for Starbucks.
  • “e60a5d2d-6e9e-4d3b-bfe4-5cebfc0c3d97” is the chain Id for Peet’s Coffee & Tea.

At a closed Best Buy, near any open electronics store

(and (at (= chain-id "ab4affd0-d68a-012e-5619-003048cad9da") (not is-open))
     (near (= category-id 133) is-open))
  • “ab4affd0-d68a-012e-5619-003048cad9da” is the chain Id for Best Buy
  • 133 is the category Id for Retail > Computers and Electronics

Near the Los Angeles Staples Center

(near (= factual-id "d9d3c9e1-983d-438b-8970-ebaddaa71386"))
  • “d9d3c9e1-983d-438b-8970-ebaddaa71386” is the Factual Id for the Staples Center.

Increased At Confidence Threshold

The default threshold for Engine determining a user is at a place is "low". It is intended to provide a reasonable level of confidence that the device is at the place defined in your DSL expression. If you find that the false positive rate for users being at specific places is too high for your use case, you can override the threshold:

(at (= chain-id "ab4c54c0-d68a-012e-5619-003048cad9da")
    (= threshold-met "high"))