# lookup

> Source: https://docs.clonepartner.com/reference/steps/lookup/

<!-- sources: src/steps/lookup-step.ts, src/steps/step-connector-utils.ts, src/steps/types.ts, references/envoy/step-types.md, references/envoy/patterns.md -->

# lookup

Enriches each input item by trying an ordered chain of single-row lookups against SQL or MongoDB Connectors and merging the first match into the record.

## When to use it

Use `lookup` for cross-store joins and foreign-key resolution, such as resolving an email to a staged account row before writing a related record. For destination-ID dedup lookups against the mapping store, prefer [sync_record](/reference/steps/sync-record/) (or [get_destination_id](/reference/steps/get-destination-id/) in existing tasks).

## Configuration

| Key | Type | Required | Default | Description |
|---|---|---|---|---|
| `keys` | array | Yes | — | Ordered lookup attempts. The step stops at the first match. An empty list fails with `lookup requires at least one keys entry`. |
| `keys[].connector` | string | Yes | — | SQL (`sqlite`, `mssql`, `mysql`, `postgres`) or MongoDB Connector slot. Other types fail with `lookup requires a SQL or MongoDB connector`. |
| `keys[].table` | string | For SQL | — | Table to query. One of `table` or `collection` is required per key. |
| `keys[].collection` | string | For MongoDB | — | Collection to query. One of `table` or `collection` is required per key. |
| `keys[].match` | object | Yes | — | Filter passed to `find_one`. A string value starting with `$record.` is replaced by that field's value from the input item. |
| `keys[].select` | string[] | No | — | Columns to return. SQL: passed as `select`; MongoDB: converted to a projection. |
| `as` | string | No | — | Nest the matched row under this key on the output. Omitted, the row's fields are merged into the record. |
| `required` | boolean | No | — | When true and no key matches, the item is dropped (nothing emitted). |
| `emit` | string | No | — | `matched_only` or `all`. `matched_only` drops unmatched items, same as `required: true`. |

## Example

```yaml
name: resolve_ticket_accounts
connectors:
  source:
    type: mongodb
  staging:
    type: sqlite
steps:
  - name: read_tickets
    type: call_method
    config:
      connector: source
      method: find
      args:
        collection: tickets
  - name: resolve_account
    type: lookup
    config: |
      {
        "keys": [
          {
            "connector": "staging",
            "table": "accounts",
            "match": { "email": "$record.requester_email" },
            "select": ["destination_id", "name"]
          },
          {
            "connector": "staging",
            "table": "accounts",
            "match": { "domain": "$record.requester_domain" }
          }
        ],
        "as": "account",
        "required": true
      }
```

## Behavior notes

- **Fallback chain.** Keys are tried in order with the Connector's `find_one` method; the first row found wins and later keys are not queried. Use the chain to express "match by email, else by domain" style resolution.
- **`$record.` references.** Only string values in `match` that start with `$record.` are resolved against the input item; everything else is passed as a literal. The config as a whole can still be a JSONata string when values need expressions.
- **Merge semantics.** With `as`, the match is attached under one key and the rest of the record is untouched. Without `as`, the row's fields are merged into the record and overwrite same-named input fields.
- **No match.** With `required: true` or `emit: matched_only` the item is dropped from the stream. Otherwise the item passes through without enrichment.
- Non-object input (a bare string or number) is wrapped as `{ value: <input> }` before matching and merging.
- Lookups are issued per item on every pass; the step does not cache results.

## Related

- [sync_record](/reference/steps/sync-record/) — dedup lookups against the mapping store
- [transform](/reference/steps/transform/) — enrich with `$callMethod` when a chain is not needed
- [Migration playbook](/building/migration-playbook/) — where cross-store joins fit in a migration
