> This page is part of the [Customer.io documentation](https://docs.customer.io). For the complete index, see [llms.txt](https://docs.customer.io/llms.txt).
> Last updated: November 4, 2026
> To take actions in Customer.io from an agent, use the [Customer.io CLI](https://docs.customer.io/ai/cli/get-started.md) or the [MCP server](https://docs.customer.io/ai/mcp/get-started.md).

# Query objects in your workflow

A **Query Objects** action finds [custom objects](https://docs.customer.io/messaging/objects-data/objects/start/) in your workspace, like courses or stores, and saves them for later steps in the journey. Then you can list those objects in your messages with liquid.

## How it works

When a person reaches a **Query Objects** action, Customer.io finds the objects of one [object type](https://docs.customer.io/messaging/objects-data/objects/types/) that match your conditions. Then it saves them to a [journey attribute](https://docs.customer.io/journeys/send/workflows/attribute-updates/set-journey-attributes/) or a [profile attribute](https://docs.customer.io/journeys/people/manage/attributes/) for later steps. You can add it to any automation except a webhook-triggered one.

For example, if you store courses as objects, a Query Objects action can find five advanced courses that a person isn’t enrolled in yet, and a later email can list them.

If your data is in a [collection](https://docs.customer.io/messaging/objects-data/collections/), use a Query Collection action instead.

## Add a Query Objects action

In the workflow builder, scroll to *Data* and drag **Query Objects** onto your canvas.

![A cursor drags Query Objects from the Data section of the Add panel onto the canvas, then clicks the new action, which opens a panel with an Add query button.](https://docs.customer.io/images/query-objects-add.gif)

Click the Query Objects block, and you can add conditions to filter for who should enter vs skip the query action. An action can have up to 20 conditions.

## Build a query to find objects

Click the action, give it a name, and click **Add query** to get started.

 ![The Query Objects editor set up for the example: Courses not related to the profile, where level is advanced and topics include the person's interests, sorted by rating, limited to 5, and stored in recommended_courses.](https://docs.customer.io/images/query-objects-editor.png)

Let’s find the five highest-rated advanced courses that match a person’s interests and that the person isn’t enrolled in yet.

### Choose objects

Define your criteria under *Choose objects*:

1.  Select an **Object type**, like *Courses*.
2.  Choose a **Relationship** to decide which objects of that type the action considers:
    *   **Any object** considers every object of the type, whether or not the person in the journey has a [relationship](https://docs.customer.io/messaging/objects-data/objects/relationships/) with it.
    *   **Related to the profile** considers only the objects the person in the journy has a relationship with.
    *   **Not related to the profile** considers only the objects the person in the journey doesn’t have a relationship with.
3.  Click **Add query condition** to filter for the objects you want. An object has to match every condition.

A condition can compare an object attribute with a fixed value or with an attribute of the person, the trigger event, or the journey.

In this example, a person has a relationship with each course they’re enrolled in. Choose *Courses* and **Not related to the profile** to find the courses they haven’t enrolled in yet, and add two conditions:

*   `level` **is equal to** **the value** `advanced`
*   `topics` **includes any of** **the current person’s attribute** `interests`

The `interests` attribute is a list on the person’s profile, like `["python", "statistics"]`.

### Limit the results

Under *Limit the results*, choose which of the matching objects to keep:

*   To pick a random set each time a person reaches the action, turn on **Randomize results**.
*   To sort them instead, choose an object attribute in **Sort results by**. Results sort highest first. Click the button beside the field to flip the order.

Then you decide the number of results. In **Limit results**, choose **Limit to 1 result** or choose **Limit to a specific number** and set the count from 1 to 50.

Tip

Store prices and dates on your objects as plain numbers, like `1200` or a Unix timestamp, so they sort correctly.

For this example, choose `rating` in **Sort results by** and limit the results to 5.

Sorting and random results work on object types with up to 1 million objects.

### Decide how to store the results

Under *Store the query results*, choose an option in **Store as**:

*   [**Journey attribute**](https://docs.customer.io/messaging/send/workflows/attribute-updates/set-journey-attributes/) keeps the results for later steps until the person exits the journey. The data is only available while the person actively moves through your automation.
*   **Profile attribute** saves the results on the person’s profile, overwriting any earlier value, so segments and other automations can use them.

Then pick an attribute or type a new name, and choose up to 100 **Object attributes** to keep with each result’s `object_id`. If you leave the field empty, each result includes every attribute. A profile attribute holds up to 1,000 characters by default, so keep only the object attributes your message needs, or use a journey attribute.

For this example, store `name`, `level`, and `rating` in the journey attribute `recommended_courses`. The action saves a [JSON](https://docs.customer.io/messaging/people/getting-started-with-json/) array like this one:

```
[
  {
    "object_id": "c-101",
    "name": "Bayesian statistics",
    "level": "advanced",
    "rating": "4.8"
  },
  {
    "object_id": "c-205",
    "name": "Deep learning with Python",
    "level": "advanced",
    "rating": "4.6"
  }
]
```

### Preview the results

The **Query result preview** shows what the action finds for the profile you select in the **Sample data** panel.

 ![The Query result preview for Jordan Lee: the Table tab lists five advanced courses by rating, above the note Previewing the first 5 results.](https://docs.customer.io/images/query-objects-preview.png)

The **Table** tab lists the objects, and **Value to store** shows the JSON the action would save. If you see **Previewing the first 5 results**, the action found as many objects as your limit allows, and more might match. **Results not available yet** means that your sort or some of your conditions start working once the automation is running and you’ve saved the action.

## Reference object data with liquid

In any later step that supports [liquid](https://docs.customer.io/messaging/liquid/using-liquid/), you can use the results as `journey.<name>` or `customer.<name>`.

To list each object in a message, you loop over the array:

```
{% for course in journey.recommended_courses %}
  - {{ course.name }} ({{ course.level }})
{% endfor %}
```

If you chose **Limit to 1 result**, read the object’s attributes directly, like `{{ journey.next_course.name }}`.

Going back to our example, people without `interests` match no courses, so make sure the message is relevant for them to. Below, the else statement recommends going to your course catalog.

```
{% if journey.recommended_courses != blank %}
  Here are some courses we picked for you.
{% else %}
  Browse our full course catalog.
{% endif %}
```

## If the action finds nothing or fails

If the action finds nothing or fails, the person always moves to the next step.

If no objects match, the action clears the profile or journey attribute you chose to set.

If the action fails, you see a **Failed Object Query** entry in the person’s [activity log](https://docs.customer.io/messaging/people/filtering-logs/).
