> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.runtype.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.runtype.com/_mcp/server.

# Using record steps (get/list/upsert)

Use record steps to retrieve, create, update, and search Records from a Flow.
Records hold JSON metadata that your Flows can reference.

For more information about Records, see [What are Records?](/user-guide/records/what-are-records).

## Choose between Get Record and List Records

Choose **Get Record** when you need one object, such as a lookup by ID or by a unique name. Choose **List Records** when the number of matches can vary, such as a lookup for every open ticket for a customer.

## Get Record step

Use **Get Record** when you need one Record. The step returns one object.

### Add a Get Record step

To add a Get Record step, follow these steps:

1. In the Flow editor, click the **+** control where you want to add the step. The step picker opens.
2. Select **Get Record**.
3. Under **Get record**, choose one of these lookup methods:
   * **By ID**: In **with ID**, enter a Record ID or a template such as `{{customer_id}}`.
   * **By query**: Select a **Record type** and add filter conditions. For more information, see [Filtering and searching records](/user-guide/records/filtering-and-searching-records).
4. Optional: To return only some fields, expand **Fields** and set **Include fields** or **Exclude fields**. With **By query**, you can click **Fetch example for include/exclude fields** to list the available fields.
5. In the output variable field, enter a name such as `customer`.
6. Click **Save version**, or **Create** for a new Flow.

The Record ID and filter values accept `{{variable}}` templates. Runtype resolves them before the lookup.

When multiple Records match, **Get Record** returns the Record with the newest update time. When no Record matches, the step fails with an error that begins with `Record not found`. For what happens next, see [Handle missing results](#handle-missing-results).

### Read the result

The returned Record includes `id`, `name`, `type`, `metadata`, `createdAt`, and `updatedAt`. When you set **Include fields** or **Exclude fields**, the object contains only the selected fields.

If the output variable is `customer`, use these references:

```plaintext
{{customer.id}}
{{customer.name}}
{{customer.metadata.tier}}
{{customer.metadata.customer_name}}
```

## List Records step

Use **List Records** when the lookup can return zero, one, or multiple Records. The step returns an array of Records.

### Add a List Records step

To add a List Records step, follow these steps:

1. In the Flow editor, click the **+** control where you want to add the step. The step picker opens.
2. Select **List Records**.
3. Select a **Record type** and add filter conditions.
4. Optional: Set **Maximum records**. The default is 50, and the maximum is 1000.
5. Optional: Set **If no records match** to **Succeed with an empty list** or **Fail the step**. The default is **Succeed with an empty list**.
6. Optional: Expand **Fields** to include or exclude specific fields from each Record.
7. In the output variable field, enter a name such as `customers`.
8. Click **Save version**, or **Create** for a new Flow.

**List Records** orders results by update time, with the newest first.

### Read the results

**List Records** always returns an array, even when one Record matches. Each item has the same fields as a **Get Record** result. If the output variable is `customers`, index the array before you read a field.

Use either dot indexing or bracket indexing. The following references return fields of the first Record and the number of matches:

```plaintext
{{customers.0.metadata.tier}}
{{customers[0].metadata.tier}}
{{customers.0.id}}
{{customers.length}}
```

## Upsert Record step

Use **Upsert Record** to create a Record or update an existing Record. The step stores an object from a variable as the Record metadata.

### Add an Upsert Record step

To add an Upsert Record step, follow these steps:

1. In the Flow editor, click the **+** control where you want to add the step. The step picker opens.
2. Select **Upsert Record**.
3. Under **Create or update records**, select **Single**.
4. In **from variable**, enter the variable that holds the metadata object, such as `customer_profile`.
5. In **with type**, enter the Record type, such as `customer`.
6. In **named**, enter a stable Record name, such as `customer-{{customer_id}}`.
7. In the output variable field, enter a name such as `saved_record`.
8. Click **Save version**, or **Create** for a new Flow.

The step uses the Record type and name to find the Record. If a Record with the same type and name exists, the step updates it. Otherwise, it creates a Record. The name accepts `{{variable}}` templates. If you leave the name empty, the step generates a name from the Record type and the execution time, so every run creates a new Record.

To use the same Record in a later **Get Record** step, look it up with the same name.

### Choose a merge strategy

To change how the step writes to an existing Record, expand **Options** and set **Merge Strategy**:

* **Merge**: Replace the top-level fields that appear in the source object and keep the other fields. The default.
* **Replace**: Replace all existing metadata with the source object.

**Merge** works on top-level fields only. A nested object in the source replaces the stored nested object completely. For example, if the Record has `{"address": {"city": "Oslo", "street": "Main St"}}` and the source is `{"address": {"city": "Bergen"}}`, the result is `{"address": {"city": "Bergen"}}`. To change nested fields and keep their neighbors, use [Update Record](#update-record-step).

### Set the source variable

The source variable must resolve to a JSON object. The object becomes the Record metadata. If a **Run Task** step produces the object, set its **Response Format** to **JSON**.

The following object shows a valid source value:

```json
{
  "customer_id": "{{customer_id}}",
  "conversation_summary": "{{summarize_result}}",
  "sentiment": "{{analyze_sentiment.label}}",
  "timestamp": "{{_now.iso}}"
}
```

In this example, `customer_id`, `summarize_result`, and `analyze_sentiment` refer to values from previous steps. `{{_now.iso}}` inserts the execution timestamp.

An [Extract Fields step](/user-guide/flows/extraction) produces a ready-made source object. If its output variable is `claim`, set the source variable to the path `claim.values`, without braces. Fields that the input didn't contain are `null`, and with **Merge** a `null` replaces the stored value.

A plain-text source value is not a JSON object, so the step writes nothing. To store text, add a **Run Code** step that wraps the text in an object. Flows that you configure through the API or SDK can instead set `contentField` on the step, which stores the text as `{ "<contentField>": "<text>" }`. When you save the Flow, validation reports a text source as the warning `UPSERT_RECORD_SOURCE_NOT_JSON`.

### Upsert several Records at once

To create or update one Record for each item in an array, follow these steps:

1. Under **Create or update records**, select **Batch**.
2. In **For each item in**, enter the variable that holds the array, such as `products`.
3. Optional: Change **reference as** from `item` to another name, and change **with batch size** from the default of 100.
4. In **with type**, enter the Record type.
5. In **named**, enter a name that is unique for each item, such as `{{item.sku}}`.

Each item in the array becomes the metadata of one Record. If the variable does not hold an array, the step writes nothing. An empty array returns an empty result.

### Read the result

The output variable contains the saved Record's `id`, `name`, `type`, `createdAt`, and `updatedAt`. It does not contain the metadata. In batch mode, the output is an array with one such object for each item.

If the output variable is `saved_record`, use `{{saved_record.id}}` to reference the Record in a later step.

## Update Record step

Use **Update Record** to change metadata on one existing Record. Unlike **Upsert Record**, it does not create a Record when the target does not exist.

### Add an Update Record step

To add an Update Record step, follow these steps:

1. In the Flow editor, click the **+** control where you want to add the step. The step picker opens.
2. Select **Update Record**.
3. Under **Find record**, choose the target:
   * **From context**: Update the Record in the `_record` variable, such as the current Record in a batch run.
   * **By ID**: Enter a Record ID or a template such as `{{saved_record.id}}`.
   * **By filter**: Select a **Record type** and add filter conditions. The step updates the most recently updated match.
4. Under **Update metadata**, enter each field's path inside the metadata, such as `status` or `address.city`, its type, and its value. Click **Add Field** to add another field. To edit the updates as JSON instead, turn on **JSON mode**.
5. Optional: Set **Merge Strategy** to **Replace**, or turn on **Return full record**.
6. In the output variable field, enter a name such as `updated_record`.
7. Click **Save version**, or **Create** for a new Flow.

String values can contain `{{templates}}` that resolve before the write.

**Merge Strategy** controls how the updates combine with the existing metadata:

* **Merge**: Merge the updates into the existing metadata, including fields inside nested objects. The default. In the API and SDK, `deep-merge` behaves the same as `merge`.
* **Replace**: Replace all existing metadata with the updates.

### Read the result

By default, the output variable contains a summary: `id`, `name`, `type`, `updated`, `updatedAt`, `metadataKeysUpdated`, and `mergeStrategy`. When **Return full record** is on, the output variable contains the updated Record, including its metadata.

## Write limits and schema validation

**Upsert Record** and **Update Record** reject metadata that exceeds these limits:

* 256 KB of serialized JSON (`METADATA_TOO_LARGE`)
* 200 top-level keys (`METADATA_TOO_MANY_KEYS`)
* 64 characters for a top-level key (`METADATA_KEY_TOO_LONG`)
* 5 levels of nesting (`METADATA_TOO_DEEP`)

If the Record type has a collection schema, the schema's validation mode also applies. In `warn` mode, a nonconforming write proceeds and the step reports schema warnings. In `enforce` mode, the step rejects a nonconforming write. For more information, see [Defining a schema](/user-guide/records/defining-a-schema).

## Handle write failures

A write step that fails does not always stop the Flow. The result depends on the cause and on the step's error-handling setting. For the available settings, see [Error handling](/user-guide/flows/creating-and-editing-flows#error-handling).

The following table shows the outcome for each cause:

| Cause                                                                           | Error handling not set                                                        | **Continue on error**                                                       | **Stop on error** |
| ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------- | ----------------- |
| The source variable is missing or is not an object, or no target Record matches | The step reports a failure, writes no output variable, and the Flow continues | The step succeeds, sets the output variable to `null`, and writes no Record | The Flow stops    |
| The write is rejected, for example by a limit or an `enforce` schema            | The step succeeds, sets the output variable to `null`, and writes no Record   | The step succeeds, sets the output variable to `null`, and writes no Record | The Flow stops    |

An **Update Record** step that you add in the dashboard starts with **Stop on error**. An **Upsert Record** step starts with no setting, and its control shows **Continue on error** until you choose a mode. When a missed write must stop the Flow, set the step to **Stop on error**. In the API and SDK, set `defaultValue` to write a value other than `null` when the step continues.

## Vector Search step

Use **Vector Search** to find stored content by semantic similarity. Before you search, create embeddings with **Generate Embedding** and store them with **Store Vector**.

### Add a Vector Search step

To add a Vector Search step, follow these steps:

1. In the Flow editor, click the **+** control where you want to add the step. The step picker opens.
2. Select **Vector Search**.
3. In **Search for**, enter the search text or a template such as `{{question}}`.
4. In the **in** list, select the vector store: **Weaviate**, **Cloudflare Vectorize**, or, when it's available for your account, **PostgreSQL (pgvector)**. For Weaviate, also select the connection and collection. For Cloudflare Vectorize, select the connection and, optionally, enter a namespace.
5. Optional: Change the number of results after **returning**, and the minimum similarity after **results with similarity**. The defaults are 5 results and a similarity of 0.7.
6. In **using**, select the embedding model that you used when you stored the vectors.
7. In the output variable field, enter a name such as `search_results`.
8. Click **Save version**, or **Create** for a new Flow.

### Read search results

**Vector Search** returns an array of matches, with the closest match first. With **PostgreSQL (pgvector)** and **Cloudflare Vectorize**, each match includes `id`, `similarity`, and `metadata`. With Weaviate, each match includes `id`, `score`, and `properties`, which holds the stored fields.

If the output variable is `search_results`, read fields from an indexed result like this:

```plaintext
{{search_results[0].similarity}}
{{search_results[0].metadata.content}}
```

For guidance on choosing the number of results and the similarity threshold, see [Building retrieval that works](/user-guide/records/building-retrieval-that-works).

## Build a retrieval-augmented generation flow

A retrieval-augmented generation (RAG) flow retrieves relevant Record content and passes it to a model. Build the pattern with these steps:

1. **Get Record**, **List Records**, or **Vector Search**: Find relevant Records for the question.
2. **Run Code**: Optional. Extract and format the retrieved context.
3. **Run Task**: Generate an answer from the retrieved context.

Use a prompt like the following to answer a question from search results:

```plaintext
Answer this question using only the provided context.

Question: {{question}}

Context:
{{search_results[0].metadata.content}}
{{search_results[1].metadata.content}}

Answer:
```

This prompt reads the `question` variable and the first two items in the `search_results` array.

## Handle missing results

A read step that fails writes no output variable. The Flow stops only when the step's error handling is **Stop on error**. Otherwise, later steps run, and a reference such as `{{customer.id}}` stays in their input as literal text.

Choose the step behavior that matches how you handle missing Records:

* Use **Get Record** when no match must fail the step.
* Use **List Records** with **Succeed with an empty list** when no match is an expected result.
* Use **List Records** with **Fail the step** when an empty result must fail the step.

To branch on an empty **List Records** result, add a **Conditional Logic** step after it. If the output variable is `customers`, enter this condition in code mode:

```plaintext
customers.length > 0
```

Put the steps that use the Record data on the true path. Put the steps that handle missing Records, such as a default reply, on the false path. For more information, see [Using conditional steps](/user-guide/flows/using-conditional-steps).

## Best practices for record steps

Follow these practices when you configure record steps:

* **Use snake_case metadata keys**: Records that you create in the dashboard or through the API store keys in lowercase snake_case. Use keys such as `customer_name` in Flows too, so reads and filters match.
* **Keep metadata concise**: Store only the fields that your Flows use in queries or prompts, and stay within the [write limits](#write-limits-and-schema-validation).
* **Use stable names for upserts**: Set a stable name when later runs must update the same Record.
* **Set a List Records limit**: Set **Maximum records** to the number of Records that the Flow needs.

## Migrate from `retrieve-record`

The `retrieve-record` step is deprecated. Existing Flows that use it continue to run. When you edit such a Flow, replace the step with **Get Record** when you need one object, or with **List Records** when you need an array.

Existing `retrieve-record` steps behave like **Get Record** when they look up by ID. When they look up by type, name, or filter, they behave like **List Records** with a limit of 1000 that fails when no Record matches. For save-time checks, see [Flow validation warnings](/user-guide/flows/flow-validation-warnings).

## Next steps

Continue with these related topics:

* [Using records in flows](/user-guide/records/using-records-in-flows): combine record steps in common patterns.
* [Creating and managing records](/user-guide/records/creating-and-managing-records): create and organize Records.
* [Using prompt steps](/user-guide/flows/using-prompt-steps): generate structured output for **Upsert Record**.
* [Flow variables and templates](/user-guide/flows/flow-variables-and-templates): reference values between steps.
* [Using vector steps](/user-guide/flows/using-vector-steps): generate embeddings, store vectors, and run vector search in a Flow.
* [Using memory steps](/user-guide/flows/using-memory-steps): save and recall memory from a Flow.