# Autopilot

> After each run, a recurring Content Research Agent tunes its own keywords so it keeps finding new videos. Autopilot is on by default, never removes the keywords you set, and never adds charges.

Source: https://dev.virlo.ai/docs/agents/autopilot
Markdown: https://dev.virlo.ai/docs/agents/autopilot.md
Section: Content Research Agents

## About the Virlo API (applies to every page)

- Base URL: `https://api.virlo.ai/v1`. Every request needs the header `Authorization: Bearer YOUR_API_KEY` (keys start with `virlo_tkn_`).
- Responses are JSON inside a `data` field, except the `/v1/webhooks` endpoints, which return the object or array directly. Field names are snake_case.
- Prices are in US dollars from a prepaid balance. 1 credit = $0.01. The `X-Cost` response header on each successful response is the exact charge. Errors are free.
- Slow jobs return an ID. Check its status every 15 seconds (or whatever `retry_after_seconds` says) until `finalized` is `true`.
- All docs pages: https://dev.virlo.ai/llms.txt. Every page in one file: https://dev.virlo.ai/llms-full.txt. MCP server for AI assistants: https://dev.virlo.ai/api/mcp/mcp.

---

After each run, a recurring [Content Research Agent](https://dev.virlo.ai/docs/agents) tunes its own keywords so it keeps finding new videos. Autopilot is on for every new agent, and it never removes the keywords you set.

**At a glance**

- **What it does:** Rewords the agent's searches, adds keywords, follows breaking stories in its niche, and collects more widely after a thin run. One-time agents do none of this.
- **You send:** Nothing. It is on by default. To turn it off, send `autopilot: false`.
- **You get back:** `autopilot` and `pinned_keywords` on every agent, plus an [activity log](https://dev.virlo.ai/docs/agents/autopilot#get-agent-activity) of what changed and why.
- **Cost:** Free. Autopilot never adds charges. The price per run stays the same, and it never starts an extra billed run.

---

## How autopilot works

Autopilot is on by default for every new recurring agent (`is_recurring: true`), whether you make it through the API, over [MCP](https://dev.virlo.ai/docs/mcp), or in the Virlo app. There is nothing to unlock.

After each run, autopilot can:

- Reword the agent's searches.
- Add keywords, up to 15 in all. If you set 15 or more yourself, it keeps them all and adds none.
- Add short-lived **timely keywords** for a breaking story in the niche ([events](https://dev.virlo.ai/docs/agents/autopilot#get-agent-events)). They expire on their own.
- Collect more widely when a run comes back thin.

Every keyword change has to pass a quality check first. The [activity log](https://dev.virlo.ai/docs/agents/autopilot#get-agent-activity) records each change and the reason for it.

Autopilot never:

- Removes a keyword or exclude term you set on an agent made through the API or MCP (see [Your keywords stay](https://dev.virlo.ai/docs/agents/autopilot#pinned-keywords)).
- Adds charges. The price per run stays the same, and it never starts an extra billed run.
- Changes the schedule (`cadence`) or pauses the agent.

To turn it off, send `autopilot: false` when you [create](https://dev.virlo.ai/docs/agents#create-agent) or [update](https://dev.virlo.ai/docs/agents#update-agent) the agent, or use [Turn autopilot on or off](https://dev.virlo.ai/docs/agents/autopilot#set-autonomy). The agent then keeps its setup exactly as you set it, and it still runs on schedule.

Agent responses show the setting as `autopilot`, `true` when it is on. `autonomy_level` and `autopilot_unlocked` are older fields that still come back. Read `autopilot` instead.

### Your keywords stay

The keywords you send are pinned. The agent returns them as `pinned_keywords`, and autopilot only adds around them, so `keywords` holds yours plus any autopilot added.

Send new `keywords` with [Update agent](https://dev.virlo.ai/docs/agents#update-agent) and your list replaces the pinned one. Your edits win. Exclude terms work the same way: autopilot never removes one you set in `exclude_keywords`.

Agents made in the Virlo app have no pinned keywords (`pinned_keywords: null`), even after you edit them through the API, so autopilot manages their whole list.

---

## Turn autopilot on or off

**Endpoint:** `PUT https://api.virlo.ai/v1/agents/:id/autonomy`

Turns autopilot on or off for one agent. It does the same as `autopilot` on [Update agent](https://dev.virlo.ai/docs/agents#update-agent). Free. Send at least one field.

- `autopilot` (boolean, optional): `true` turns autopilot on, the default. `false` keeps the agent's setup exactly as you set it.
- `cognition_enabled` (boolean, optional): `false` stops the agent reviewing its own runs, so autopilot changes nothing. The agent keeps collecting. `true` turns reviews back on.
- `autonomy_level` (string, optional): The older way to set the same switch, still accepted. `"autopilot"` is on and `"suggest"` is off. Send this or `autopilot`, not both.

An empty body returns `400` (`No autonomy fields provided`). So does sending `autopilot` and `autonomy_level` with different answers (`autopilot and autonomy_level disagree; send one of them`).

**Autopilot off request:**

```bash
curl -X PUT https://api.virlo.ai/v1/agents/{agent_id}/autonomy \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "autopilot": false }'
```

**Autopilot on request:**

```bash
curl -X PUT https://api.virlo.ai/v1/agents/{agent_id}/autonomy \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "autopilot": true }'
```

**Self-review off request:**

```bash
curl -X PUT https://api.virlo.ai/v1/agents/{agent_id}/autonomy \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "cognition_enabled": false }'
```

---

## Get activity

**Endpoint:** `GET https://api.virlo.ai/v1/agents/:id/activity`

The agent's change log: what it noticed, what autopilot changed, and why. Newest first. Free. Empty for one-time agents, and possibly right after a recurring agent's first run.

Each entry has `content`, `created_at`, and a `category`: usually `observation`, `decision`, or `milestone`, sometimes `user_pref` (a note about your preferences).

- `limit` (integer, optional): 1 to 100. Default `20`.

**cURL request:**

```bash
curl -G https://api.virlo.ai/v1/agents/{agent_id}/activity \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d limit=20
```

**Response 200:**

```json
{
  "data": {
    "agent_id": "a1f3c8e2-0b7d-4a91-9c2e-2f5d6b8e1a44",
    "count": 1,
    "activity": [
      {
        "content": "This run collected 283 videos, and 71% were on target. No-cook recipes drove most of the reach.",
        "category": "observation",
        "created_at": "2026-10-04T02:31:04.325401+00:00"
      }
    ]
  }
}
```

---

## Get events

**Endpoint:** `GET https://api.virlo.ai/v1/agents/:id/events`

Breaking stories a recurring agent spotted in its niche, such as a launch or a controversy. Free. Active events come first, most important first. The [`content_research_agent.event.detected`](https://dev.virlo.ai/docs/webhooks#supported-events) webhook fires as soon as one is confirmed.

- `limit` (integer, optional): 1 to 100. Default `50`.

**Full field reference**

- `source`: `corpus_burst` (spotted in your agent's videos) or `news_scan` (from the news, then confirmed on the platforms).
- `salience`: importance from 0 to 10.
- `status`: `candidate`, `confirmed` (active), `dismissed`, or `expired`.
- `evidence`: `url` and `views` are `null` when the evidence is your agent's own videos.

**cURL request:**

```bash
curl -G https://api.virlo.ai/v1/agents/{agent_id}/events \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d limit=50
```

**Response 200:**

```json
{
  "data": {
    "agent_id": "a1f3c8e2-0b7d-4a91-9c2e-2f5d6b8e1a44",
    "count": 1,
    "events": [
      {
        "id": "c12fc742-7c5d-44fd-9b9c-765710147980",
        "title": "New protein cereal launch",
        "summary": "A major cereal brand launched a 20g-protein cereal, and taste tests are spreading.",
        "source": "news_scan",
        "salience": 8,
        "status": "confirmed",
        "keywords": ["protein cereal launch"],
        "evidence": [
          {
            "url": "https://example.com/protein-cereal-launch",
            "views": null,
            "description": "Cereal brand announces a 20g-protein cereal"
          }
        ],
        "detected_at": "2026-10-01T23:52:14.965821+00:00",
        "confirmed_at": "2026-10-01T23:52:15.102+00:00",
        "expires_at": "2026-10-08T23:52:21.405+00:00"
      }
    ]
  }
}
```

---

## List proposals (deprecated)

**Endpoint:** `GET https://api.virlo.ai/v1/agents/:id/proposals`

> **Note:** Deprecated. Autopilot applies changes itself, and [Get activity](https://dev.virlo.ai/docs/agents/autopilot#get-agent-activity) shows what changed and why. The proposal endpoints still work for now and send a `Deprecation: true` header. There is no removal date yet.

The changes this agent has proposed, newest first. Free. Changes autopilot made show as `auto_applied`. With autopilot off, changes wait here as `pending`.

- `status` (string, optional): `pending`, `applied`, `auto_applied`, `dismissed`, or `reverted`. An unknown value returns an empty list, not an error.
- `limit` (integer, optional): 1 to 100. Default `50`.

| Proposal `type` | What it proposes |
| - | - |
| `keyword_refresh` | Swap stale keywords |
| `filter_change` | Collect more broadly |
| `timely_keywords` | Add short-lived keywords for a breaking story |
| `cadence_change` | Run daily (costs more) |
| `pause` | Pause the agent |
| `early_run` | An extra run now, billed like any other |
| `event_detected` | An alert, not a change. Dismiss it once read. |

**cURL request:**

```bash
curl -G https://api.virlo.ai/v1/agents/{agent_id}/proposals \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -d status=pending
```

**Response 200:**

```json
{
  "data": {
    "agent_id": "a1f3c8e2-0b7d-4a91-9c2e-2f5d6b8e1a44",
    "count": 1,
    "proposals": [
      {
        "id": "b94357b3-0c3b-4289-87c7-648beb1fe31f",
        "content_research_agent_id": "a1f3c8e2-0b7d-4a91-9c2e-2f5d6b8e1a44",
        "type": "timely_keywords",
        "source": "event",
        "rationale": "Detected \"New protein cereal launch\" in your niche. Adding 1 timely keyword.",
        "diff": {
          "before": { "timely_keywords": [] },
          "after": {
            "event_id": "c12fc742-7c5d-44fd-9b9c-765710147980",
            "timely_additions": [
              { "keyword": "protein cereal launch", "expires_at": "2026-10-08T23:52:21.405+00:00" }
            ]
          },
          "event": { "title": "New protein cereal launch", "salience": 8 }
        },
        "status": "pending",
        "created_at": "2026-10-01T23:52:25.448948+00:00",
        "decided_at": null,
        "updated_at": "2026-10-01T23:52:25.448948+00:00"
      }
    ]
  }
}
```

---

## Apply, dismiss, or revert a proposal (deprecated)

Deprecated, like [List proposals](https://dev.virlo.ai/docs/agents/autopilot#list-agent-proposals). Three free calls, under `/v1/agents/:id/proposals/:proposal_id/`. Applying an `early_run` starts an extra run, billed like any other.

- `POST .../apply` approves a pending proposal and makes the change. On an agent made through the API, the keywords and exclude terms you approve become your [pinned](https://dev.virlo.ai/docs/agents/autopilot#pinned-keywords) lists.
- `POST .../dismiss` rejects a pending proposal.
- `POST .../revert` undoes an applied or auto-applied change.

Only `pending` proposals can be applied or dismissed, and only applied or auto-applied ones reverted. Otherwise you get `400`, such as `Proposal ... is already applied`. `event_detected` has nothing to apply, so dismiss it. Applying `early_run` returns `400` if the agent ran in the last 24 hours or your balance can't cover the run.

To stop autopilot changing an agent, turn it off with `autopilot: false` ([Turn autopilot on or off](https://dev.virlo.ai/docs/agents/autopilot#set-autonomy)) instead of reverting each change.

**cURL request:**

```bash
curl -X POST https://api.virlo.ai/v1/agents/{agent_id}/proposals/{proposal_id}/apply \
  -H "Authorization: Bearer YOUR_API_KEY"
```

---

More in Content Research Agents:

- [Overview](https://dev.virlo.ai/docs/agents.md)
- [Intent cookbook](https://dev.virlo.ai/docs/intent-cookbook.md)
- [Data Intelligence](https://dev.virlo.ai/docs/intelligence.md)
