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

> ## Agent Instructions
> Autumn AI (autumn.ai) is a people and company intelligence API, a separate company from useautumn.com (billing software); no billing-product routes exist here.
> Base URL https://api.autumn.ai. Auth on every request: `X-API-Key: <key>` or `Authorization: Bearer <key>`. Plain HTTP + JSON. Hosted MCP: https://api.autumn.ai/mcp. Schemas: https://api.autumn.ai/openapi.json. Every docs page is Markdown at its URL plus `.md`.

# Signals

> Move Clay signals (funding, news, job posts, new hires) to an Autumn task on your own schedule.

A Clay signal watches a list for a kind of event and writes new events into a table. In
Autumn, the same list goes into a task with the event types and a date window, and your code
keeps only events it hasn't seen.

```mermaid theme={"theme":{"light":"github-light","dark":"github-dark"}}
flowchart LR
  S[Your schedule] --> T[Autumn task] --> D{New and passed rules?}
  D -- yes --> N[CRM or alert]
```

## Read the Clay signals

```bash wrap theme={"theme":{"light":"github-light","dark":"github-dark"}}
clay signals list | jq -r '.data[] | "\(.id)  \(.signal.type)  \(.runStatus)  \(.name)"'
clay signals get td_abc123 | jq '{name, input, signal}'
```

Note each signal's type, the list it watches (`input`), its schedule and its filter.

## The task

```json wrap theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "name": "Account signals",
  "prompt": "Watch these accounts for signals published between 2026-10-18 and 2026-10-25: a new funding round, a new executive hire (VP or above), or a job post for a sales or revenue operations role. Return one row per signal found, with the record_id of the account it belongs to. Skip accounts with no qualifying signal. Every row needs a source URL dated inside the window.",
  "input_schema": {"record_id": "str", "company_name": "str", "domain": "str"},
  "input_rows": [
    {"record_id": "acc_001", "company_name": "Linear", "domain": "linear.app"}
  ],
  "output": {
    "id": "account-signals", "kind": "research", "path": "outputs/account-signals.jsonl",
    "schema": {
      "record_id": {"type": "str"},
      "company_name": {"type": "str"},
      "signal_type": {"type": "str", "description": "funding, exec_hire, or sales_hiring"},
      "summary": {"type": "str"},
      "event_date": {"type": "date"},
      "source_url": {"type": "url"}
    }
  },
  "rules": {
    "in_window": "The source is dated between 2026-10-18 and 2026-10-25.",
    "known_account": "record_id is one of the input record_ids."
  },
  "clarify": false
}
```

On a four-account test over a one-month window this returned 14 events in about two and a
half minutes. Seven passed every rule; the rest had `in_window: unclear` because the job
post showed no date.

Here `record_id` is in the schema, unlike in [Move a table](https://www.autumn.ai/docs/clay-migration/move-a-table.md#3-write-the-mapping):
an account can have several events or none, so the task has to say which account each event
belongs to.

## Keep only new events

```python new_signals.py theme={"theme":{"light":"github-light","dark":"github-dark"}}
import json, os, sys, requests

AUTUMN = "https://api.autumn.ai"
HEADERS = {"X-API-Key": os.environ["AUTUMN_API_KEY"]}
RULES = ["in_window", "known_account"]  # the task's rules

def signal_key(row):
    parts = ["record_id", "signal_type", "event_date", "source_url"]
    return "|".join(str(row.get(p) or "") for p in parts)

seen = set(json.load(open("seen.json"))) if os.path.exists("seen.json") else set()

if sys.argv[1] == "--delivered":
    seen |= {signal_key(row) for row in json.load(open("new_signals.json"))}
    json.dump(sorted(seen), open("seen.json", "w"))
    print(f"{len(seen)} signals marked as seen")
    sys.exit()

def output_rows(task_id):
    params = {"limit": 500}
    while True:
        page = requests.get(f"{AUTUMN}/task/{task_id}/output",
                            headers=HEADERS, params=params).json()
        for row in page["rows"]:
            yield {k: v.get("value") for k, v in row.items()
                   if not k.startswith("_") and isinstance(v, dict)}
        if not page.get("_next_cursor"):
            return
        params["cursor"] = page["_next_cursor"]

new = {}
for row in output_rows(sys.argv[1]):
    if any(f"rule_{r}" not in row for r in RULES):
        continue
    if any(v != "match" for k, v in row.items() if k.startswith("rule_")):
        continue
    if signal_key(row) not in seen:
        new[signal_key(row)] = row

json.dump(list(new.values()), open("new_signals.json", "w"), indent=2)
print(f"{len(new)} new signals")
```

```bash wrap theme={"theme":{"light":"github-light","dark":"github-dark"}}
python3 new_signals.py "$TASK_ID"       # writes new_signals.json
# send new_signals.json to the CRM or alert, then:
python3 new_signals.py --delivered      # records them in seen.json
```

Run the task on the signal's old frequency (cron, a CI schedule, a worker), moving the
window forward each time. Overlapping windows are fine: the script drops repeats. An event
counts as a repeat when its account, type, date and source all match one already seen.
Mark events as seen only after they are delivered, so a failed send is retried on the next
run. Store `seen.json` wherever the scheduler keeps state.

For job changes on contacts, see [Enrichment](https://www.autumn.ai/docs/clay-migration/enrichment.md#job-changes).

> **[Enrichment](https://www.autumn.ai/docs/clay-migration/enrichment.md)**
>
> Contacts, emails, phones and job changes.

> **[Move a table](https://www.autumn.ai/docs/clay-migration/move-a-table.md)**
>
> The full steps for one table.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.