Content Research Agents

A Content Research Agent (CRA) is the unified, forward-facing way to run research on Virlo. One resource, /v1/agents, replaces the split Orbit (one-shot keyword search) and Comet (recurring niche monitor) endpoints — a single is_recurring flag decides the mode. Creating an agent costs $0.50 (recurring agents bill per scheduled run); all reads are free.


Migrating from Orbit / Comet

Orbit and Comet remain available during a migration window and will be removed on August 3, 2026. Migrating is mostly a rename — the data shapes are the same.

Endpoint mapping

LegacyUnified
POST /v1/orbitPOST /v1/agents with is_recurring: false
POST /v1/cometPOST /v1/agents with is_recurring: true
GET /v1/orbit/:id · GET /v1/comet/:idGET /v1/agents/:id
GET /v1/{orbit,comet}/:id/videosGET /v1/agents/:id/videos
GET /v1/{orbit,comet}/:id/slideshowsGET /v1/agents/:id/slideshows
GET /v1/{orbit,comet}/:id/adsGET /v1/agents/:id/ads
GET /v1/{orbit,comet}/:id/creators/outliersGET /v1/agents/:id/creators/outliers
GET /v1/{orbit,comet}/:id/analysis[/latest]GET /v1/agents/:id/analysis[/latest]
GET /v1/{orbit,comet}/:id/trends[/latest]GET /v1/agents/:id/trends[/latest]
GET /v1/{orbit,comet}/:id/soundsGET /v1/agents/:id/sounds
GET /v1/comet/:id/hashtagsGET /v1/agents/:id/hashtags
GET /v1/comet/:id/benchmarksGET /v1/agents/:id/benchmarks
GET /v1/comet/:id/affinityGET /v1/agents/:id/affinity
GET /v1/comet/:id/creators/:cid/similarGET /v1/agents/:id/creators/:cid/similar
PUT /v1/comet/:idPUT /v1/agents/:id
DELETE /v1/comet/:idDELETE /v1/agents/:id

Every read is a same-path rename (/v1/orbit/:id/… or /v1/comet/:id/…/v1/agents/:id/…) and the IDs are identical, so you can migrate one call site at a time.

What changed

  • min_views / time_range / time_period are removed from creation (collection is system-managed). Move any view/date filtering to the videos endpoint query params.
  • intent is required on agents — it drives keyword quality, intent-match filtering, and the agent's self-optimization.
  • Webhook: subscribe to content_research_agent.run.completed (carries is_recurring) instead of orbit.run.completed / comet.run.completed. The legacy events keep firing until removal.
  • New: an autonomy surface (activity log + change proposals) — see below.

POST/v1/agents

Create agent

Creates a one-shot (is_recurring: false) or recurring (is_recurring: true) agent and dispatches its first run immediately.

  • Name
    is_recurring
    Type
    boolean
    Required
    *
    Description

    false = one-shot (Orbit-equivalent). true = recurring monitor (Comet-equivalent).

  • Name
    intent
    Type
    string
    Required
    *
    Description

    What you want this agent to research, in one sentence. Drives keyword quality, intent_match filtering, and the agent's self-optimization. Example: "Track viral protein-recipe content for a fitness brand".

  • Name
    keywords
    Type
    string[]
    Required
    *
    Description

    1–50 keywords. 3–7 specific multi-word phrases covering synonyms of the same concept work best. Hashtag tokens are normalized (#melodichouse == melodic house).

  • Name
    platforms
    Type
    string[]
    Description

    youtube, tiktok, instagram. Defaults to all.

  • Name
    cadence
    Type
    string
    Description

    Required when is_recurring: true. "daily", "weekly", "monthly", or a cron expression (must run at most once per day — sub-daily crons are rejected). Rejected for one-shot agents.

  • Name
    name
    Type
    string
    Description

    Human-friendly name. Defaults to a generated label.

  • Name
    exclude_keywords
    Type
    string[]
    Description

    Keywords to filter out of results.

  • Name
    exclude_keywords_strict
    Type
    boolean
    Description

    Also match exclude_keywords against transcripts. Default false.

  • Name
    meta_ads_enabled
    Type
    boolean
    Description

    Also collect Meta ads. Default false.

  • Name
    data_intelligence_enabled
    Type
    boolean
    Description

    Enable per-video AI intelligence (40+ fields). Adds $1.00 to the base cost. Cannot be applied retroactively. Default false.

Request

POST
/v1/agents
curl -X POST https://api.virlo.ai/v1/agents \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "is_recurring": true,
    "intent": "Track viral protein-recipe content for a fitness brand",
    "keywords": ["high protein recipe", "protein meal prep", "protein snack ideas"],
    "platforms": ["youtube", "tiktok", "instagram"],
    "cadence": "weekly",
    "name": "Protein Recipes",
    "meta_ads_enabled": true
  }'

Response

{
  "data": {
    "id": "a1f3c8e2-0b7d-4a91-9c2e-2f5d6b8e1a44",
    "name": "Protein Recipes",
    "is_recurring": true,
    "active": true,
    "is_deleted": false,
    "team_id": "9d4c2b10-8e6f-4a23-b1c7-0a5e3f9d2b18",
    "source": "api",
    "keywords": ["high protein recipe", "protein meal prep", "protein snack ideas"],
    "platforms": ["youtube", "tiktok", "instagram"],
    "exclude_keywords": [],
    "exclude_keywords_strict": false,
    "meta_ads_enabled": true,
    "data_intelligence_enabled": false,
    "intent": "Track viral protein-recipe content for a fitness brand",
    "autonomy_level": "suggest",
    "autopilot_unlocked": false,
    "cognition_enabled": true,
    "cadence": "weekly",
    "next_run_at": "2026-07-10T14:00:00Z",
    "last_run_at": null,
    "is_processing": true,
    "created_at": "2026-07-03T14:00:00Z",
    "updated_at": "2026-07-03T14:00:00Z",
    "latest_run": {
      "id": "5c8b1f20-3a4d-4e7f-9b12-6d2a1c7e8f90",
      "status": "processing",
      "finalized": false
    },
    "job_id": "job_7f2a9c1e4b6d"
  },
  "message": "Agent created"
}

GET/v1/agents

List agents

Lists your agents.

  • Name
    is_recurring
    Type
    boolean
    Description

    Filter to recurring (true) or one-shot (false) agents.

  • Name
    include_inactive
    Type
    boolean
    Description

    Include deactivated agents. Default false.

  • Name
    page
    Type
    integer
    Description

    1-indexed page. Default 1.

  • Name
    limit
    Type
    integer
    Description

    Results per page (max 100). Default 50.

Request

GET
/v1/agents
curl -G https://api.virlo.ai/v1/agents \
  -H "Authorization: Bearer {token}" \
  -d is_recurring=true \
  -d limit=50

Response

{
  "data": {
    "limit": 50,
    "page": 1,
    "count": 2,
    "agents": [
      {
        "id": "a1f3c8e2-0b7d-4a91-9c2e-2f5d6b8e1a44",
        "name": "Protein Recipes",
        "is_recurring": true,
        "active": true,
        "cadence": "weekly",
        "next_run_at": "2026-07-10T14:00:00Z",
        "last_run_at": "2026-07-03T14:18:00Z",
        "is_processing": false
      },
      {
        "id": "b2e4d9f3-1c8e-4b02-8d3f-3a6e7c9f2b55",
        "name": "Jeep Wrangler Mods",
        "is_recurring": false,
        "active": true,
        "cadence": null,
        "next_run_at": null,
        "last_run_at": "2026-07-02T09:31:00Z",
        "is_processing": false
      }
    ]
  }
}

GET/v1/agents/:id

Get agent

Returns the agent config, autonomy state, latest run, and async status. finalized: true means the latest run (scrape + AI analysis) is fully settled; while false, inspect pending_jobs[].

Request

GET
/v1/agents/:id
curl https://api.virlo.ai/v1/agents/{agent_id} \
  -H "Authorization: Bearer {token}"

Response

{
  "data": {
    "id": "a1f3c8e2-0b7d-4a91-9c2e-2f5d6b8e1a44",
    "name": "Protein Recipes",
    "is_recurring": true,
    "active": true,
    "cadence": "weekly",
    "next_run_at": "2026-07-10T14:00:00Z",
    "last_run_at": "2026-07-03T14:18:00Z",
    "is_processing": false,
    "finalized": true,
    "latest_run": {
      "id": "5c8b1f20-3a4d-4e7f-9b12-6d2a1c7e8f90",
      "status": "completed",
      "videos_linked": 283,
      "outliers_identified": 12,
      "started_at": "2026-07-03T14:00:00Z",
      "completed_at": "2026-07-03T14:18:00Z"
    },
    "pending_jobs": [],
    "analysis": "Protein desserts and no-cook overnight recipes are driving the most outsized reach this week...",
    "analysis_data": { "key_highlight": "...", "themes": [] },
    "analysis_batch_start": "2026-06-26T14:00:00Z",
    "analysis_batch_end": "2026-07-03T14:00:00Z",
    "autonomy_level": "suggest",
    "autopilot_unlocked": false,
    "cognition_enabled": true
  }
}

PUT/v1/agents/:id

Update agent

Updates mutable config. Only fields you pass change. Collection scope (min_views/time_range) is system-managed and cannot be set here.

  • Name
    name
    Type
    string
    Description
    New name.
  • Name
    active
    Type
    boolean
    Description
    Activate or pause the agent.
  • Name
    keywords
    Type
    string[]
    Description
    Replace the keyword set.
  • Name
    platforms
    Type
    string[]
    Description
    Replace the platform set.
  • Name
    cadence
    Type
    string
    Description
    New cadence (recurring only).
  • Name
    exclude_keywords
    Type
    string[]
    Description
    Replace excludes.
  • Name
    exclude_keywords_strict
    Type
    boolean
    Description
    Toggle transcript-level exclusion.
  • Name
    meta_ads_enabled
    Type
    boolean
    Description
    Toggle Meta ads collection (future runs).
  • Name
    intent
    Type
    string
    Description
    Update the agent's intent.
  • Name
    data_intelligence_enabled
    Type
    boolean
    Description
    Toggle per-video intelligence for future runs.

Request

PUT
/v1/agents/:id
curl -X PUT https://api.virlo.ai/v1/agents/{agent_id} \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{ "keywords": ["high protein recipe", "protein meal prep", "protein desserts"] }'

DELETE/v1/agents/:id

Delete agent

Soft-deletes the agent (stops future runs; previously collected data remains accessible until purged). Returns 204 No Content.

Request

DELETE
/v1/agents/:id
curl -X DELETE https://api.virlo.ai/v1/agents/{agent_id} \
  -H "Authorization: Bearer {token}"

GET/v1/agents/:id/videos

Get videos

Videos collected by the agent. This is where you filter the broad collection — for free, any way you like.

  • Name
    min_views
    Type
    integer
    Description

    Minimum view count (read-time filter). Example: 100000.

  • Name
    platforms
    Type
    string[]
    Description

    Filter by platform(s).

  • Name
    start_date
    Type
    string
    Description

    Only videos published on/after this ISO date. Example: 2026-01-01T00:00:00Z.

  • Name
    end_date
    Type
    string
    Description

    Only videos published on/before this ISO date.

  • Name
    order_by
    Type
    string
    Description

    views | publish_date | created_at.

  • Name
    sort
    Type
    string
    Description

    asc | desc.

  • Name
    intent_match
    Type
    boolean
    Description

    When the agent used data_intelligence_enabled, return only intent-matching (true) or non-matching (false) videos.

  • Name
    page
    Type
    integer
    Description
    1-indexed page. Default 1.
  • Name
    limit
    Type
    integer
    Description
    Results per page (max 100). Default 50.

Request

GET
/v1/agents/:id/videos
curl -G https://api.virlo.ai/v1/agents/{agent_id}/videos \
  -H "Authorization: Bearer {token}" \
  -d min_views=100000 \
  -d start_date=2026-01-01T00:00:00Z \
  -d order_by=views \
  -d sort=desc \
  -d limit=50

Response

{
  "data": {
    "agent_id": "a1f3c8e2-0b7d-4a91-9c2e-2f5d6b8e1a44",
    "agent_name": "Protein Recipes",
    "total": 283,
    "limit": 50,
    "offset": 0,
    "videos": [
      {
        "id": "e7c2a1b4-9f3d-4e88-8a12-5b6c7d8e9f01",
        "url": "https://www.tiktok.com/@fitcoachjen/video/7412345678901234567",
        "description": "3-ingredient protein brownies that actually taste good 🍫",
        "platform": "tiktok",
        "views": 2140000,
        "likes": 312000,
        "shares": 41200,
        "comments": 8900,
        "bookmarks": 128000,
        "publish_date": "2026-06-28T18:22:00Z",
        "author": {
          "id": "c3d4e5f6-a7b8-4901-9c2d-3e4f5a6b7c8d",
          "username": "fitcoachjen",
          "avatar_url": "https://.../avatar.jpg",
          "url": "https://www.tiktok.com/@fitcoachjen",
          "verified": false
        },
        "hashtags": ["proteinrecipe", "highprotein", "healthydessert"],
        "thumbnail_url": "https://.../thumb.jpg",
        "keyword_found_by": "high protein recipe",
        "intent_match": true,
        "intelligence": { "hook_type": "problem_solution", "content_format": "tutorial" },
        "intelligence_status": "ready",
        "is_duet": false,
        "is_stitch": false,
        "sound": { "id": "8f6e5c50-1451-4007-a120-92744e632dad", "title": "original sound - fitcoachjen" }
      }
    ]
  }
}

GET/v1/agents/:id/slideshows

Get slideshows

Slideshows (image carousels) collected by the agent. Same read-time filters as videos.

Request

GET
/v1/agents/:id/slideshows
curl -G https://api.virlo.ai/v1/agents/{agent_id}/slideshows \
  -H "Authorization: Bearer {token}" \
  -d limit=50

GET/v1/agents/:id/ads

Get ads

Meta ads collected when meta_ads_enabled was set. Supports order_by (created_at | page_like_count), sort, page, limit.

Request

GET
/v1/agents/:id/ads
curl -G https://api.virlo.ai/v1/agents/{agent_id}/ads \
  -H "Authorization: Bearer {token}" \
  -d limit=50

GET/v1/agents/:id/creators/outliers

Get creator outliers

Creators who significantly outperform their follower count within the agent's corpus. Prefer order_by=weighted_score. Also supports platform, page, limit, sort, plus follower_tier (nano / micro / mid / macro) and category (topic substring) filters.

order_by=rising ranks by true run-over-run velocity (follower/view growth) and adds growth_followers / growth_views / growth_video_count fields per row; on a young agent without two snapshots it transparently falls back to the weighted-outlier ranking. Pass a row's author_id to similar creators to explore adjacent creators.

Request

GET
/v1/agents/:id/creators/outliers
curl -G https://api.virlo.ai/v1/agents/{agent_id}/creators/outliers \
  -H "Authorization: Bearer {token}" \
  -d order_by=weighted_score \
  -d limit=25

Response

{
  "data": {
    "agent_id": "a1f3c8e2-0b7d-4a91-9c2e-2f5d6b8e1a44",
    "agent_name": "Protein Recipes",
    "total": 12,
    "limit": 25,
    "offset": 0,
    "hasMore": true,
    "outliers": [
      {
        "id": "c3d4e5f6-a7b8-4901-9c2d-3e4f5a6b7c8d",
        "author": {
          "id": "c3d4e5f6-a7b8-4901-9c2d-3e4f5a6b7c8d",
          "username": "fitcoachjen",
          "avatar_url": "https://.../avatar.jpg",
          "url": "https://www.tiktok.com/@fitcoachjen",
          "verified": false
        },
        "follower_count": 48200,
        "avg_views": 512000,
        "outlier_ratio": 10.6,
        "weighted_score": 25.4,
        "videos_analyzed": 14
      }
    ]
  }
}

GET/v1/agents/:id/sounds

Get sounds

Top sounds across the agent's collected videos, ranked by usage. Free. Use sort=rising (or growth_7d) to rank by run-over-run momentum — rows carry growth_video_count, growth_views, and a new / rising / steady / fading lifecycle label (growth fields are null until the agent has two run snapshots). Pivot to GET /v1/sounds/:sound_id/usage-history for a sound's full time-series.

Request

GET
/v1/agents/:id/sounds
curl -G https://api.virlo.ai/v1/agents/{agent_id}/sounds \
  -H "Authorization: Bearer {token}" \
  -d sort=rising \
  -d limit=20

Response

{
  "data": [
    {
      "id": "8f6e5c50-1451-4007-a120-92744e632dad",
      "title": "Saxophones getting louder",
      "platform": "tiktok",
      "usage_count": 138106,
      "video_count": 42,
      "avg_views": 612000,
      "growth_video_count": 18,
      "growth_views": 240000,
      "lifecycle": "rising"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 20,
    "total": 63,
    "total_pages": 4,
    "has_next_page": true,
    "has_prev_page": false
  }
}

GET/v1/agents/:id/hashtags

Get hashtags

Per-hashtag analytics computed over the agent's collected videos: volume, total/average views, average engagement, run-over-run growth with a new / rising / steady / fading lifecycle label, and the most active creators per hashtag. Free. Sort with sort=volume (default), growth, or avg_views.

Request

GET
/v1/agents/:id/hashtags
curl -G https://api.virlo.ai/v1/agents/{agent_id}/hashtags \
  -H "Authorization: Bearer {token}" \
  -d sort=growth \
  -d limit=25

Response

{
  "data": [
    {
      "hashtag": "proteinrecipe",
      "video_count": 96,
      "total_views": 41200000,
      "avg_views": 429166,
      "avg_engagement": 0.081,
      "growth_video_count": 34,
      "lifecycle": "rising",
      "top_creators": [
        { "username": "fitcoachjen", "video_count": 8, "avg_views": 512000 }
      ]
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 25,
    "total": 118,
    "total_pages": 5,
    "has_next_page": true,
    "has_prev_page": false
  }
}

GET/v1/agents/:id/benchmarks

Get benchmarks

Median engagement rate, follower count, niche video count, and posting frequency across the agent's creators, bucketed by follower tier (nano / micro / mid / macro). Free. Useful for "how does a creator compare to the genre norm?".

Request

GET
/v1/agents/:id/benchmarks
curl https://api.virlo.ai/v1/agents/{agent_id}/benchmarks \
  -H "Authorization: Bearer {token}"

GET/v1/agents/:id/affinity

Get affinity

Beta — directional signal; methodology may evolve. Genre-adjacency derived from the agent's corpus: dominant creator topics, co-occurring hashtags, and co-occurring sounds. Not a follow-graph. Free.

Request

GET
/v1/agents/:id/affinity
curl https://api.virlo.ai/v1/agents/{agent_id}/affinity \
  -H "Authorization: Bearer {token}"

GET/v1/agents/:id/creators/:creator_id/similar

Get similar creators

Beta — directional signal; methodology may evolve. Other creators in the agent's corpus ranked by shared hashtags and shared sounds with the target creator (co-occurrence; use the author_id from creator outliers). Free. Supports limit (default 20, max 100).

Request

GET
/v1/agents/:id/creators/:creator_id/similar
curl -G https://api.virlo.ai/v1/agents/{agent_id}/creators/{author_id}/similar \
  -H "Authorization: Bearer {token}" \
  -d limit=20

GET/v1/agents/:id/analysis/latest

Get latest analysis

The full structured AI analysis from the most recent completed cycle — analysis is a short human-readable highlight, analysis_data carries the full object (themes, connecting_thread, viral_tactics, timing_analysis, top_10_breakdown, …). Free.

The latest analysis fields are also merged into GET /v1/agents/:id (analysis, analysis_data, analysis_batch_start, analysis_batch_end). For the full history, use GET /v1/agents/:id/analysis with page / limit / start_date / end_date.

Request

GET
/v1/agents/:id/analysis/latest
curl https://api.virlo.ai/v1/agents/{agent_id}/analysis/latest \
  -H "Authorization: Bearer {token}"

Response

{
  "data": {
    "id": "d5e6f7a8-b9c0-4d12-8e34-5f6a7b8c9d01",
    "insight_type": "content_research_agent",
    "reference_id": "a1f3c8e2-0b7d-4a91-9c2e-2f5d6b8e1a44",
    "batch_start": "2026-06-26T14:00:00Z",
    "batch_end": "2026-07-03T14:00:00Z",
    "analysis": "Protein desserts and no-cook overnight recipes are driving the most outsized reach this week...",
    "analysis_data": {
      "key_highlight": "No-cook 'overnight' protein recipes are the breakout format.",
      "overview": "...",
      "themes": [
        {
          "why_it_works": "Zero-effort framing lowers the barrier to trying the recipe.",
          "tactics": ["show the jar in the first frame", "call out the protein grams in text"],
          "confidence": 0.82,
          "evidence_video_ids": ["e7c2a1b4-9f3d-4e88-8a12-5b6c7d8e9f01"]
        }
      ],
      "top_10_breakdown": []
    },
    "video_count": 283,
    "analyzed_video_ids": ["e7c2a1b4-9f3d-4e88-8a12-5b6c7d8e9f01"],
    "model_used": "claude-sonnet",
    "created_at": "2026-07-03T14:17:00Z"
  }
}

GET/v1/agents/:id/trends/latest

Every trend detected in the most recent completed analysis cycle. Each trend carries evidence videos, aggregate engagement stats, a stable_key for time-series joins, and a new / rising / steady / fading status versus the previous cycle. Free.

For the full history, use GET /v1/agents/:id/trends with page / limit / stable_key / start_date / end_date — filtering by stable_key gives you one trend's trajectory over time.

Request

GET
/v1/agents/:id/trends/latest
curl https://api.virlo.ai/v1/agents/{agent_id}/trends/latest \
  -H "Authorization: Bearer {token}"

Response

{
  "data": {
    "reference_id": "a1f3c8e2-0b7d-4a91-9c2e-2f5d6b8e1a44",
    "insight_type": "content_research_agent",
    "viral_insight_id": "f1a2b3c4-d5e6-4f78-9a01-2b3c4d5e6f70",
    "batch_start": "2026-06-26T14:00:00Z",
    "batch_end": "2026-07-03T14:00:00Z",
    "total": 6,
    "trends": [
      {
        "id": "aa11bb22-cc33-4d44-8e55-6f778899aa00",
        "rank": 1,
        "stable_key": "overnight-protein-oats",
        "name": "No-cook overnight protein oats",
        "why_it_works": "Zero-effort framing + high protein payoff in one scroll.",
        "tactics": ["show the jar first", "text-overlay the protein grams"],
        "confidence": 0.86,
        "evidence_video_ids": ["e7c2a1b4-9f3d-4e88-8a12-5b6c7d8e9f01"],
        "video_count": 38,
        "total_views": 18400000,
        "total_likes": 2100000,
        "total_comments": 54000,
        "total_shares": 210000,
        "avg_virality_score": 22.7,
        "platform_breakdown": { "tiktok": 24, "instagram": 9, "youtube": 5 },
        "top_creators": ["fitcoachjen"],
        "peak_hour_utc": 18,
        "status": "rising",
        "first_seen_at": "2026-06-19T14:00:00Z",
        "prev_video_count": 21,
        "prev_total_views": 9200000,
        "created_at": "2026-07-03T14:17:00Z"
      }
    ]
  }
}

GET/v1/agents/:id/runs

List runs

Lists the agent's runs (one for a one-shot agent, one per cycle for a recurring agent). Fetch a single run with GET /v1/agents/:id/runs/:run_id.

Request

GET
/v1/agents/:id/runs
curl -G https://api.virlo.ai/v1/agents/{agent_id}/runs \
  -H "Authorization: Bearer {token}" \
  -d limit=50

Response

{
  "data": {
    "agent_id": "a1f3c8e2-0b7d-4a91-9c2e-2f5d6b8e1a44",
    "agent_name": "Protein Recipes",
    "total": 3,
    "limit": 50,
    "offset": 0,
    "runs": [
      {
        "id": "5c8b1f20-3a4d-4e7f-9b12-6d2a1c7e8f90",
        "status": "completed",
        "finalized": true,
        "videos_linked": 283,
        "outliers_identified": 12,
        "started_at": "2026-07-03T14:00:00Z",
        "completed_at": "2026-07-03T14:18:00Z"
      },
      {
        "id": "4b7a0e19-2c3d-4d6e-8a01-5c1b0d6e7f89",
        "status": "partial_failure",
        "finalized": true,
        "videos_linked": 194,
        "outliers_identified": 8,
        "started_at": "2026-06-26T14:00:00Z",
        "completed_at": "2026-06-26T14:21:00Z"
      }
    ]
  }
}

Autonomy overview

Agents don't just collect — they reflect on their own performance and propose safe changes to keep finding content without manual babysitting. When an agent's yield drops, it can propose refreshing stale keywords or widening a starved collection window (and, as a last resort, dropping the view floor).

Self-optimization applies to recurring agents only — a one-shot runs once, so there's no future run to improve. One-shot agents won't produce proposals or activity.

How it works

  • Every proposed change is surfaced as a proposal with a human-readable rationale and a before/after diff.
  • autonomy_level: "suggest" (default) — proposals wait for your approval via apply/dismiss.
  • autonomy_level: "autopilot" — safe changes auto-apply. Autopilot must be unlocked first by approving one proposal manually, and it only ever widens collection — it never restricts what you see.
  • cognition_enabled: false pauses self-optimization entirely (the agent keeps collecting).
  • Everything the agent decides is recorded in its activity log, and any applied change is revertible.

GET/v1/agents/:id/activity

Get activity

The agent's decision log — reflections, milestones, and the changes it has made over time. Free. Supports limit (max 100).

Request

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

GET/v1/agents/:id/proposals

List proposals

Self-optimization proposals. Filter with status (pending | applied | auto_applied | dismissed | reverted). Each proposal includes its type (keyword_refresh | filter_change), a rationale, and a diff.

Request

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

POST

Apply / dismiss / revert a proposal

  • POST /v1/agents/:id/proposals/:proposal_id/apply — approve and apply a pending proposal. The first manual apply unlocks autopilot for the agent.
  • POST /v1/agents/:id/proposals/:proposal_id/dismiss — reject a pending proposal.
  • POST /v1/agents/:id/proposals/:proposal_id/revert — roll an applied change back to the prior config.

Request

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

PUT/v1/agents/:id/autonomy

Set autonomy

Choose how much the agent may change on its own.

  • Name
    autonomy_level
    Type
    string
    Description

    suggest (changes wait for approval) or autopilot (safe widenings auto-apply; must be unlocked first).

  • Name
    cognition_enabled
    Type
    boolean
    Description

    Master switch for self-optimization. false pauses all reflection + proposals.

Request

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

Was this page helpful?