Skip to main content
CanaryVaultsCanaryVaults home
ProductsPricingBlogDocs
Start Free

GET STARTED

OverviewQuickstart

SURFACES

CanaryVaultsCanaryRAGCanaryShieldCanaryHoneypotCanaryAuditCanaryAgent

REFERENCE

API referenceAlerts & webhooksEnterprise & teams

Surface 06 · CanaryAgent

Connect an agent

CanaryAgent adds heartbeat monitoring and skill scans to your agents. Post a heartbeat from your agent's loop, and submit each skill's code for a scan before it ships:

bash
# heartbeat — call it on a timer from the agent
curl -X POST https://api.canaryvaults.com/canaryagent/heartbeat \
  -H "X-API-Key: $CV_KEY" -H "Content-Type: application/json" \
  -d '{
    "user_id": "<your-user-uuid>",
    "agent_id": "billing-agent",
    "status": "running",
    "actions_last_60s": 12,
    "memory_tokens": 48210
  }'

# skill scan — submit a skill before the agent loads it
curl -X POST https://api.canaryvaults.com/canaryagent/skill-scan \
  -H "X-API-Key: $CV_KEY" -H "Content-Type: application/json" \
  -d '{"user_id": "<your-user-uuid>", "skill_name": "export-invoices", "skill_code": "…"}'

Heartbeats build a timeline per agent. A scheduled check runs every two minutes and flags any agent whose last heartbeat is older than five minutes — so a stopped agent surfaces within a few minutes of going quiet, not the instant it stops. Silent-agent alerts go to every channel on the account — built-in Telegram, built-in email, and every enabled Slack, Discord and signed-webhook channel — through the same shared dispatcher CanaryShield and CanaryHoneypot use, each delivery isolated so one failing channel never blocks the rest. A skill scan that comes back HIGH or CRITICAL raises its own alert down the same path. While alerts are paused, both built-in paths skip.

Get an API key

Both calls above authenticate with a workspace API key sent in the X-API-Key header. Mint one in the app — Settings → API keys → Generate API key — on any plan, Free included. The full key is shown once, at creation, so copy it off that screen, keep it server-side and export it as CV_KEY. Five active keys per account, and a leaked one is revoked from the same card while it is still on screen.

bash
# Settings → API keys → "Generate API key". Shown once; copy it there.
export CV_KEY="cv_<id>_<secret>"

# both calls above take it in the same header:
#   -H "X-API-Key: $CV_KEY"

You don't have to write the client yourself. The CanaryAgent console has a Download canary_middleware.py button that hands you a ready-made Python client: it posts heartbeats from a background thread via start_heartbeat() and wraps the CanaryShield and CanaryAudit calls. It reads CANARY_HEARTBEAT_URL, CANARY_SHIELD_URL, CANARY_AUDIT_URL, CANARY_USER_ID and CANARY_API_KEY from the environment, and the console prints the matching .env block next to the button. It does not scan skills — run scans from the console or the endpoint above.

Read the timeline

GET /canaryagent/timeline/{user_id} returns the whole agent view for one workspace. It takes the same X-API-Key header (a session JWT in Authorization: Bearer works too), and the id in the path must be the caller's own user — another workspace's id returns 403.

bash
curl "https://api.canaryvaults.com/canaryagent/timeline/<your-user-uuid>?agent_id=billing-agent&limit=100" \
  -H "X-API-Key: $CV_KEY"

Every query parameter is optional. agent_id and action_type match exactly; from_date and to_date take YYYY-MM-DD and are inclusive at both ends; limit defaults to 50 and accepts 1–500. They filter entries only — agents covers every agent that has ever sent a heartbeat, and skill_scans is always the 20 most recent scans.

json
{
  "count": 1,
  "limit": 100,
  "entries": [
    {
      "id": "…",
      "agent_id": "billing-agent",
      "action_type": "invoice_export",
      "input_trigger": "cron:invoice-run",
      "trigger_preview": "cron:invoice-run",
      "action_detail": "Exported invoice batch 2026-08 to S3",
      "detail_preview": "Exported invoice batch 2026-08 to S3",
      "outcome": "success",
      "outcome_preview": "success",
      "blockchain_tx_hash": null,
      "basescan_url": null,
      "logged_at": "2026-08-17T09:14:02Z"
    }
  ],
  "agents": [
    {
      "agent_id": "billing-agent",
      "status": "online",
      "reported_status": "running",
      "last_seen": "2026-08-17T09:15:41Z",
      "actions_last_60s": 12,
      "actions_today": 41,
      "memory_tokens": 48210,
      "uptime_seconds": 9321,
      "uptime_label": "2h 35m"
    }
  ],
  "skill_scans": [
    {
      "id": "…",
      "skill_name": "export-invoices",
      "risk_level": "LOW",
      "issues": [],
      "recommendation": "Skill scan finished. Review findings before installing.",
      "safe": true,
      "scanned_at": "2026-08-17T08:02:10Z"
    }
  ]
}

count is the number of entries in this response, not the total that matched. entries are CanaryAudit records — the actions your agent logged to /canaryaudit/log. agents is derived from the latest heartbeat per agent: status reads online while a heartbeat has arrived in the last two minutes and silent after that, and actions_today counts audit records logged since midnight UTC.

The default view shows only your own agents' actions. audit_logs is shared with the canary tripwire, which seals threat events into it under agent_id=canary-tripwire and action_type=canary.tripwire_event. Those are not CanaryAgent activity, so they are excluded from entries by default — pass ?agent_id=canary-tripwire explicitly to read them.

Skill scan limits

Heartbeats do not spend plan quota. Skill scans do: agent_skill_scans_per_month allows 5 a month on Free, 50 on Shield, 500 on Pro and 1,500 on Enterprise, and a scan past that answers a structured 402 naming the metric, your plan and your reset date.

A shorter per-account daily allowance sits underneath it — 3 scans a day on Free, 10 on Shield, 40 on Pro and 100 on Enterprise — so a CI job that queues a month's worth of scans in one afternoon is refused long before the monthly quota runs out. The daily 402 names the same metric, with resets_at set to the next UTC day. This meter fails closed: if your usage cannot be checked the scan answers 503 with daily_quota_unavailable rather than running unchecked.

A per-minute burst limit sits above both: 3 scans a minute on Free, 10 on Shield, 30 on Pro and 60 on Enterprise. It answers 429 with retry_after_seconds and a Retry-After header rather than a 402, and the burst check runs before either meter, so a scan it turns away spends no quota. On every plan this figure is at or below the daily one — the two are equal on Free and Shield — so a CI job firing scans back to back meets the 429 first, and the daily 402 only once the minute has rolled over.

Next

API reference →CanaryAudit trail →
CanaryVaults

Deception-based AI security. Decoys, trap facts, honeypots, prompt defense, and tamper-evident audit trails — one workspace.

Plant your first canary

PRODUCT

ProductsCanaryAgentDashboardPricingReferralGet started

RESOURCES

DocumentationQuickstartShieldEvidence formatAPIBlog

COMPANY

AboutSecurityReport a vulnerabilityContact

TRUST

Trust centerVerify evidenceStatusChangelogIncidentsDPA

COMPARE

vs Thinkst Canaryvs CanaryTokensFor SaaS teams

LEGAL

TermsPrivacyCookiesSubprocessorsSupport
deception-based AI security© CanaryVaults · canaryvaults.comsha-256 sealed · tamper-evident

CANARYVAULTS