> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getcargo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> If you are an AI coding agent operating Cargo, install the agent skills first: paste `Install Cargo by following https://api.getcargo.io/INSTALL.md` into your session, or run `npx skills add getcargohq/cargo-skills`. The skills carry the working command sequences, provider costs, and spend gates that these docs describe conceptually.
> The skills index at https://raw.githubusercontent.com/getcargohq/cargo-skills/main/llms.txt lists every skill, recipe, and provider playbook individually. Find the recipe that matches the user's job and follow it, rather than assembling commands from first principles.
> Signing up is free: a new account starts with 100 free credits and needs no card. `cargo-ai login --email you@company.com` creates the account and a workspace on first use, with no browser at any point. Tell the user this before asking them to sign up.
> Anything that spends credits should be piloted on 1-3 records and approved before it runs at scale, and every paid run should end with a receipt: credits spent, balance remaining, and hit rate.

# Clay

> Run your Clay routines and search Clay's people and company database from a Cargo workflow.

## How to set up Clay

### Authentication

Clay runs on your own Clay workspace, so you need a Clay account and an API key:

1. In Clay, open **Settings**, then **Account**, then **API keys**
2. Create a key and copy it
3. Paste the API key in Cargo when connecting

The key is tied to one Clay workspace. Every call Cargo makes is billed to that workspace's Clay credits, and its plan sets the search limits below.

<Note>
  **Code slugs** — integration slug: `clay` · actions: `runRoutine`, `searchPeople`, `searchCompanies`. In a [workflow](/workflows/overview), declare the connector in `uses` and call `uses.<key>.runRoutine(...)`.
</Note>

## Clay actions

### Run Routine

Run a Clay routine over one or more rows and return its results. Clay runs routines asynchronously, so Cargo submits the run and then polls until it finishes, up to roughly 26 minutes.

**Required fields:**

* **Routine**: The routine ID, written as `type:id`
* **Items**: JSON for the rows to run. Either an object of input name/value pairs for one row, or an array of up to 100 such objects

#### Getting the routine ID right

A routine ID names the kind of object as well as the object itself:

| Underlying object         | Routine ID           |
| ------------------------- | -------------------- |
| A function (a Clay table) | `function:t_abc123`  |
| A Workflow                | `workflow:wf_abc123` |

The ID you copy out of a Clay URL is only the second half. Passing that bare — `wf_abc123` rather than `workflow:wf_abc123` — fails with `Invalid routine id`.

Getting the prefix right is not enough on its own. The function or Workflow also has to be **registered as a routine** in Clay, and a routine that does not exist fails with `Routine not found` even when the ID is well formed. Registering is a Clay-side step: the Clay CLI does it with `clay routines create function <tableId>` or `clay routines create workflow <workflowId>`, and `clay routines list` shows what is already registered.

Then open the routine in Clay, go to **Details**, and enable **API access**. Enabling MCP or Claygent access does not enable the API.

Clay does not publish a routine's input names through the API, so the keys in **Items** have to match what the routine expects exactly. A typo surfaces as a Clay error on the row rather than a validation error in Cargo.

The action returns one entry per row:

| Field    | Meaning                                            |
| -------- | -------------------------------------------------- |
| `id`     | The row's position in **Items**, as a string       |
| `status` | `complete` or `failed` for that row                |
| `inputs` | The inputs the row was submitted with              |
| `result` | The routine's output, shaped by the routine itself |
| `error`  | Present with a `message` when the row failed       |

**A completed run can contain failed rows.** The node still succeeds, and the title reports how many rows failed. Filter on `status == "complete"` downstream before using `result`.

**Use case:** Score or enrich a batch of accounts with the waterfall your GTM team already built in Clay, then route on the result.

### Search People

Find people in Clay's GTM database with a Clay search query.

**Required fields:**

* **Query**: A Clay advanced search query

**Optional fields:**

* **Limit**: Maximum records to retrieve across all pages. Defaults to 50, up to 1,000
* **Records per request**: How many records to pull per call. Defaults to 50

**Use case:** Build a target list of decision makers at accounts that match your ICP, then push them into a sequence.

### Search Companies

Find companies in Clay's GTM database. Same fields as Search People.

**Use case:** Source accounts matching a headcount band, industry, and technology profile before enrichment.

Clay works out whether a query targets people or companies from the query itself. If you run a company query through Search People, the action tells you to use the other one rather than returning the wrong shape.

Each action returns the fields Clay publishes for its own source, so they are available in a mapper without running the node first:

| Action           | Fields                                                                                                                                                                    |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Search People    | `clay_profile_id`, `name`, `first_name`, `last_name`, `linkedin_url`, `location`, `matched_experiences`                                                                   |
| Search Companies | `clay_company_id`, `name`, `size`, `type`, `domain`, `country`, `industry`, `location`, `description`, `linkedin_url`, `annual_revenue`, `total_funding_amount_range_usd` |

Only the identifier is guaranteed. A search matches on your predicate, not on the record being complete, so every other field can come back empty. `matched_experiences` holds only the roles your query matched on, not the person's full history.

When Clay stops a search at your plan's per-search cap rather than because the query ran out of matches, the node title says so — otherwise a capped search reads as a complete answer.

## Writing a Clay query

Queries follow `select from <source> where <predicate>`. The word after `select from` decides which action the query belongs to — a `select from people` query only runs in Search People, and a `select from companies` query only in Search Companies. Clay reads that word literally rather than inferring intent, so a query about people at French companies is still a **people** query.

### Search People

A person's role, employer, and tenure all live on their experiences, so most people queries are an `experiences.any(...)` with `is_current = true` inside it. Drop `is_current` only when you want people who *ever* held the role.

**The buying committee at accounts matching your ICP.** Filter on the role and on the employer in one predicate:

```
select from people where experiences.any(is_current = true and job_title is_similar_to ("VP Sales", "Head of Sales") and company.industry = "Software Development" and company.company_size in ("201-500", "501-1,000"))
```

**Someone newly in the job.** A new leader with a budget is the classic trigger — `start_date` is when they began that role:

```
select from people where experiences.any(is_current = true and job_title is_similar_to ("VP Marketing") and start_date >= today() - interval 3 month)
```

**People whose employer runs a tool you integrate with:**

```
select from people where experiences.any(is_current = true and job_title is_similar_to ("RevOps") and company.technographics.any(vendor = "Salesforce"))
```

**One exact title, no fuzzy expansion.** `is_similar_to` pulls in variants like Chief Technology Officer and VP Engineering, which is usually what you want. Use `=` when it isn't:

```
select from people where experiences.any(is_current = true and job_title = "CTO")
```

### Search Companies

**Firmographic ICP:**

```
select from companies where industry = "Software Development" and company_size in ("201-500", "501-1,000")
```

**Headquartered in a country.** Without `is_headquarters = true` this matches any office, which is usually not what "companies in France" means:

```
select from companies where locations.any(is_headquarters = true and country_name = "France") and company_size in ("51-200", "201-500")
```

**Hiring for a role.** `jobs` is job postings, so this is a hiring signal, not a headcount fact:

```
select from companies where jobs.exists(job_still_open = true and job_title is_similar_to ("Sales Development Representative"))
```

**Running a given technology:**

```
select from companies where technographics.any(vendor = "Salesforce")
```

**Has a role in seat.** `people` is current and former employees; swap `exists` for `count(...) >= N` to size a team, and negate it with `not` to find the whitespace where the role does not exist yet:

```
select from companies where people.exists(is_current = true and job_title is_similar_to ("VP Sales"))
```

### Traps

* **`is_similar_to` for job titles**, not `contains`. `contains` matches whole words only, so it misses every title variant
* **Company location lives in the `locations` array**, not a `headquarters` field
* **There is no `between`.** Use two comparisons, or a bucketed field
* **`company_size` and `annual_revenue` are bucketed enums** and reject `<` and `>`. Match their exact values with `=` or `in (...)`. Prefer them over `estimated_employee_count`, which is for an exact-headcount ask
* **Growth fields are ratios, not percentages.** "+20% in a year" is `employee_growth_12mo > 1.2`
* **Dates are months.** Use `today() - interval 3 month`; day and week intervals are rejected
* **Profile fields such as `location_country` are not valid inside `experiences.any(...)`.** Put them at the top level
* **Aggregates do not nest.** Write `people.exists(is_current = true and job_title is_similar_to ("Engineer"))`, never `people.exists(experiences.any(...))`

Count-mode queries and job queries are not supported through the API. The full grammar and field catalog live in [Clay's search reference](https://developers.clay.com/searches/advanced).

## Best practices

* Keep **Items** small and explicit. One row per record you want enriched, with only the inputs the routine declares
* Filter routine results on `status` before mapping `result` into a model, so failed rows do not write empty columns
* Leave **Records per request** empty unless your Clay workspace is on a paid plan. Free and Trial workspaces reject anything above 50
* Set **Limit** to what you actually need. Search results count against a rolling 30-day allowance on your Clay plan, and a generous limit spends it quickly
* Treat a long routine as a risk in time-sensitive workflows. A run that has not finished in about 26 minutes fails the node

## Credits

Clay does not consume Cargo credits. Every call is billed to the Clay workspace that issued the API key, in Clay's own credits. Cargo caching still applies and will avoid repeating an identical call.

## Rate limits

Clay meters its public API per workspace rather than per endpoint, so all three actions share one budget of 300 requests per minute. Cargo spreads calls to stay under it.

Clay also limits how many searches one workspace can have running at once, and rejects a search that starts while another is still going. Cargo therefore runs Search People and Search Companies **one call at a time**, so a search node over a large batch works through the batch rather than failing partway. A single search call can take up to a minute, so expect list building to be slower than the other actions.

Search results are capped separately, by Clay plan:

| Plan       | Per request | Per search             | Per 30 days  |
| ---------- | ----------- | ---------------------- | ------------ |
| Free       | 50          | 50                     | 100          |
| Trial      | 50          | 50                     | 10,000 total |
| Paid       | 500         | Up to the period limit | 1,000,000    |
| Enterprise | 500         | Up to the period limit | 10,000,000   |

Exceeding one of these returns an error from Clay naming the limit you hit, which Cargo surfaces on the node.
