Documentation

API Reference

The Vigil API lets you manage watchers, threads, memory, and custom tools programmatically. All endpoints return JSON and require an Authorization header.

Authentication

Use either a JWT access token (from login) or an API key from the Developer page.

Authorization: Bearer vk_your_api_key_here

API keys use the prefix vk_. The full key is never stored — only a SHA-256 hash.

Base URL

https://api.vigil.run/api

Auth

POST/auth/register

Create a new account.

Request body

email*stringValid email address (max 254 chars)
password*stringMin 8 characters
namestringDisplay name

Response 201

{
  "user": { "user_id": "abc123", "email": "you@example.com" },
  "tokens": {
    "access_token": "eyJ...",
    "refresh_token": "eyJ...",
    "expires_in": 900
  }
}
POST/auth/login

Sign in and receive tokens.

Request body

email*string
password*string

Response 200

{
  "user": { "user_id": "abc123", "email": "you@example.com" },
  "tokens": { "access_token": "...", "refresh_token": "...", "expires_in": 900 }
}
POST/auth/refresh

Exchange a refresh token for a new access token.

Request body

refresh_token*stringAlso accepts refreshToken

Response 200

{
  "tokens": { "access_token": "...", "refresh_token": "...", "expires_in": 900 }
}
GET/auth/me

Get the current authenticated user.

Response 200

{
  "user": { "user_id": "abc123", "account_id": "abc123", "email": "you@example.com", "role": "owner" }
}

Watchers

GET/watchers

List all watchers for the account.

Response 200

{
  "watchers": [
    {
      "id": "w_123", "name": "Work Email", "ingest_token": "tok_...",
      "ingestion_address": "work-tok_...@vigil.run", "status": "active", ...
    }
  ]
}
POST/watchers

Create a new watcher.

Request body

name*stringDisplay name for the watcher
system_prompt*stringInstructions for the agent
toolsstring[]Enabled tools. Default: ["send_alert"]
silence_hoursnumberHours before silence alert. Default: 48
tick_intervalnumberScheduled check interval in minutes. Default: 60
modelstringLLM model. Default: "gpt-4.1-mini"
template_idstringWatcher template to use

Response 201

{ "watcher": { "id": "w_123", "name": "Work Email", ... } }
GET/watchers/:id

Get a single watcher by ID.

PUT/watchers/:id

Update watcher settings. All fields optional, at least one required.

Request body

namestring
system_promptstring
toolsstring[]
silence_hoursnumber
tick_intervalnumber
modelstring
statusstring"active" or "paused"
reactivitynumberAgent reactivity level
memory_sensitivitynumberMemory storage sensitivity
DELETE/watchers/:id

Soft-delete a watcher and all associated data.

POST/watchers/:id/invoke

Manually invoke the agent. All fields optional.

Request body

messagestringChat mode — natural language response. Mutually exclusive with query.
querystringQuery mode — structured JSON response. Defaults to reviewing active threads if neither field is set.

Response 200

// Chat mode
{ "response": "You have 3 active threads..." }

// Query mode
{ "actions": [...], "thread_updates": [...], "email_analysis": {...} }
POST/watchers/:id/digest

Trigger a digest email for this watcher. No request body.

Threads

GET/watchers/:watcherId/threads

List threads. Filter with ?status=active, ?status=watching, etc.

Response 200

{
  "threads": [
    { "id": "t_123", "subject": "Invoice #4521", "status": "active", "summary": "...", ... }
  ]
}
GET/watchers/:watcherId/threads/:threadId

Get a single thread with full details.

PUT/watchers/:watcherId/threads/:threadId

Update thread status or metadata. All fields optional, at least one required.

Request body

statusstring"active" | "watching" | "resolved" | "ignored"
summarystringThread summary text
flagsobjectCustom flags as key-value pairs
DELETE/watchers/:watcherId/threads/:threadId

Delete a thread.

POST/watchers/:watcherId/threads/:threadId/close

Mark a thread as resolved. No request body. Returns 400 if already resolved or ignored.

Memory

GET/watchers/:id/memory

List all non-obsolete memories for a watcher.

Response 200

{
  "memories": [
    { "id": "m_123", "content": "User's rent is $1,450/mo", "importance": 4, "obsolete": false, "created_at": "..." }
  ]
}
POST/watchers/:id/memory

Manually create a memory.

Request body

content*stringMemory text. Must be non-empty.
importancenumber1-5 scale. Default: 3

Response 201

{ "memory": { "id": "m_123", "content": "...", "importance": 4 } }
PUT/watchers/:id/memory/:memoryId

Update a memory. All fields optional, at least one required.

Request body

contentstringUpdated memory text
importancenumber1-5 scale
obsoletebooleanMark memory as obsolete
DELETE/watchers/:id/memory/:memoryId

Delete a memory.

Actions

GET/watchers/:id/actions

List agent actions (invocations, alerts, tool calls). Supports ?limit=N and ?thread_id=X.

Response 200

{
  "actions": [
    {
      "id": "a_123", "trigger_type": "email_received", "tool": "send_alert",
      "result": "...", "cost_usd": 0.003, "created_at": "..."
    }
  ]
}

Custom Tools

Custom tools are webhook-backed extensions the agent can call. Configured per watcher.

GET/watchers/:id/tools

List custom tools for a watcher.

POST/watchers/:id/tools

Create a custom tool.

Request body

name*stringTool name (e.g. "notify_slack")
description*stringWhat the tool does — shown to the agent
webhook_url*stringURL to POST to when the agent calls the tool
headersobjectCustom headers sent with the webhook. Default: {}
parameter_schemaobjectJSON schema for the tool parameters. Default: {}

Response 201

{
  "tool": {
    "id": "ct_123", "name": "notify_slack",
    "description": "Send a message to Slack",
    "webhook_url": "https://hooks.slack.com/...",
    "headers": {},
    "parameter_schema": { "message": { "type": "string", "description": "The alert message" } },
    "enabled": true
  }
}
PUT/watchers/:id/tools/:toolId

Update a custom tool. All fields optional, at least one required.

Request body

namestring
descriptionstring
webhook_urlstring
headersobject
parameter_schemaobject
enabledbooleanEnable or disable the tool
DELETE/watchers/:id/tools/:toolId

Delete a custom tool.

POST/watchers/:id/tools/:toolId/test

Fire a test webhook with a sample payload. No request body.

Response 200

{ "success": true, "status": 200, "response_body": "ok" }

Channels

Channels control where alerts are delivered.

GET/watchers/:id/channels

List alert channels.

POST/watchers/:id/channels

Add an alert channel.

Request body

type*string"email" or "webhook"
destination*stringEmail address or webhook URL

Response 201

{ "channel": { "id": "ch_123", "type": "email", "destination": "me@example.com", "enabled": true } }
PUT/watchers/:id/channels/:channelId

Update a channel. All fields optional, at least one required.

Request body

destinationstringNew email address or webhook URL
enabledbooleanEnable or disable the channel
DELETE/watchers/:id/channels/:channelId

Remove a channel.

API Keys

GET/keys

List your API keys. The full key is never returned.

Response 200

{
  "keys": [
    { "id": "k_123", "name": "my-integration", "key_prefix": "vk_a1b2c3d", "usage_count": 42, "created_at": "..." }
  ]
}
POST/keys

Create a new API key. The full key is returned only once.

Request body

name*stringLabel for the key
permissionsstring[]Permission scopes. Default: ["read"]

Response 201

{
  "key": {
    "id": "k_123", "name": "my-integration",
    "key_prefix": "vk_a1b2c3d", "full_key": "vk_a1b2c3d4e5f6..."
  }
}
DELETE/keys/:id

Revoke an API key.

Usage

GET/usage

Get usage and cost data for your account.

Response 200

{
  "usage": {
    "total_cost": 0.42,
    "total_invocations": 312,
    "total_alerts": 28,
    "total_emails": 156,
    "current_month": { "cost": 0.08, "invocations": 45 },
    "watchers": [
      { "watcher_id": "w_123", "watcher_name": "Work Email", "cost": 0.25, "invocations": 200, "alerts": 15, "emails": 100 }
    ]
  }
}

Errors

All errors return a JSON body with an error field:

{ "error": "Watcher not found" }
400Bad request — missing or invalid fields401Missing or invalid authentication404Resource not found or not owned by your account429Rate limited