OpenAPI Specification
openapi: 3.0.3
info:
title: Coval Agents Monitors API
version: 1.0.0
description: '
Manage configurations for simulations and evaluations.
'
contact:
name: Coval API Support
email: support@coval.dev
url: https://docs.coval.ai
license:
name: Proprietary
url: https://coval.dev/terms
servers:
- url: https://api.coval.dev/v1
description: Production API
security:
- ApiKeyAuth: []
tags:
- name: Monitors
description: CRUD operations for monitor definitions
paths:
/monitors:
get:
operationId: listMonitors
summary: List monitors
description: 'Returns all active monitors for the authenticated organization.
Supports optional filtering by scope and pagination.
'
tags:
- Monitors
parameters:
- name: scope
in: query
schema:
type: string
enum:
- ALL
- MONITORING
- SIMULATION
description: Filter monitors by scope
- name: page_size
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 50
description: Number of results per page
- name: page_token
in: query
schema:
type: string
description: Token for fetching the next page of results
responses:
'200':
description: List of monitors
content:
application/json:
schema:
$ref: '#/components/schemas/ListMonitorsResponse'
examples:
success:
$ref: '#/components/examples/ListMonitorsSuccess'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/PermissionDenied'
'500':
$ref: '#/components/responses/InternalError'
post:
operationId: createMonitor
summary: Create a monitor
description: 'Creates a new monitor with conditions and notification channels.
At least one condition is required. Channels are optional — a monitor
without channels will still evaluate and log events but won''t dispatch
notifications.
'
tags:
- Monitors
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateMonitorRequest'
examples:
simpleThreshold:
$ref: '#/components/examples/CreateMonitorSimple'
responses:
'201':
description: Monitor created
content:
application/json:
schema:
$ref: '#/components/schemas/MonitorResource'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/PermissionDenied'
'409':
description: Monitor name conflict
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error:
code: ALREADY_EXISTS
message: Monitor name conflict
details:
- field: name
description: An active monitor named 'Latency SLA' already exists
'500':
$ref: '#/components/responses/InternalError'
/monitors/{monitor_id}:
get:
operationId: getMonitor
summary: Get a monitor
description: Returns a single monitor with its conditions and channels.
tags:
- Monitors
parameters:
- $ref: '#/components/parameters/MonitorId'
responses:
'200':
description: Monitor details
content:
application/json:
schema:
$ref: '#/components/schemas/MonitorResource'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/PermissionDenied'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalError'
patch:
operationId: updateMonitor
summary: Update a monitor
description: 'Partially updates a monitor. All fields are optional; omitted fields
retain their current values (PATCH semantics).
When `conditions` or `channels` are provided, they **replace** the
existing set atomically (delete old, create new).
'
tags:
- Monitors
parameters:
- $ref: '#/components/parameters/MonitorId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateMonitorRequest'
responses:
'200':
description: Monitor updated
content:
application/json:
schema:
$ref: '#/components/schemas/MonitorResource'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/PermissionDenied'
'404':
$ref: '#/components/responses/NotFound'
'409':
description: Monitor name conflict
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'500':
$ref: '#/components/responses/InternalError'
delete:
operationId: deleteMonitor
summary: Delete a monitor
description: 'Soft-deletes a monitor by setting its status to DELETED.
The monitor and its channels are deactivated but retained for audit.
'
tags:
- Monitors
parameters:
- $ref: '#/components/parameters/MonitorId'
responses:
'204':
description: Monitor deleted
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/PermissionDenied'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalError'
/monitors/{monitor_id}/test-evaluate:
post:
operationId: testEvaluateMonitor
summary: Test evaluate a monitor
description: 'Dry-run evaluation of a monitor against a specific run.
Evaluates all conditions and builds the notification message,
but does **not** dispatch notifications or write an event record.
Use this to preview what would happen if the monitor evaluated
against a given run.
'
tags:
- Monitors
parameters:
- $ref: '#/components/parameters/MonitorId'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TestEvaluateRequest'
example:
run_id: hUWu3PxY6G7fTYbLzBAwZm
responses:
'200':
description: Test evaluation results
content:
application/json:
schema:
$ref: '#/components/schemas/TestEvaluateResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/PermissionDenied'
'404':
$ref: '#/components/responses/NotFound'
'500':
$ref: '#/components/responses/InternalError'
components:
responses:
InternalError:
description: Internal Server Error
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error:
code: INTERNAL
message: Internal server error
details:
- description: An unexpected error occurred
Unauthorized:
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error:
code: UNAUTHENTICATED
message: Authentication failed
details:
- field: X-API-Key
description: Invalid or missing API key
NotFound:
description: Not Found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error:
code: NOT_FOUND
message: Monitor not found
details:
- field: monitor_id
description: Monitor '01HZ0EXAMPLE00000000000000' not found
PermissionDenied:
description: Permission Denied
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error:
code: PERMISSION_DENIED
message: Insufficient permissions
details:
- field: permissions
description: 'Required scope: monitors:read'
BadRequest:
description: Bad Request
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
example:
error:
code: INVALID_ARGUMENT
message: Invalid request body
details:
- field: conditions
description: Value error, List should have at least 1 item after validation
schemas:
TestEvaluateRequest:
type: object
required:
- run_id
properties:
run_id:
type: string
description: Run ID to evaluate the monitor against
ListMonitorsResponse:
type: object
required:
- monitors
- total_count
properties:
monitors:
type: array
items:
$ref: '#/components/schemas/MonitorResource'
next_page_token:
type: string
nullable: true
description: Token for fetching the next page (null if no more results)
total_count:
type: integer
description: Total count of monitors matching filter
ChannelType:
type: string
enum:
- SLACK
- EMAIL
- WEBHOOK
- HUMAN_REVIEW
description: Notification channel type
MonitorChannel:
type: object
required:
- ulid
- channel_type
- config
properties:
ulid:
type: string
pattern: ^[0-9A-Z]{26}$
description: Channel ULID
channel_type:
$ref: '#/components/schemas/ChannelType'
config:
type: object
description: 'Channel-specific configuration.
**SLACK**: `{"channel_id": "C0123ABC", "channel_name": "#alerts"}`
**EMAIL**: `{"recipients": ["team@company.com"]}`
**WEBHOOK**: `{"url": "https://...", "method": "POST", "auth_token": "..."}`
**HUMAN_REVIEW**: `{"project_id": "...", "sample_rate": 0.1}`
'
ErrorResponse:
type: object
required:
- error
properties:
error:
type: object
required:
- code
- message
- details
properties:
code:
type: string
enum:
- INVALID_ARGUMENT
- UNAUTHENTICATED
- PERMISSION_DENIED
- NOT_FOUND
- ALREADY_EXISTS
- INTERNAL
message:
type: string
description: Human-readable error message
details:
type: array
items:
type: object
properties:
field:
type: string
nullable: true
description:
type: string
ConditionOperator:
type: string
enum:
- GT
- GTE
- LT
- LTE
- EQ
- NEQ
description: Comparison operator
CreateMonitorRequest:
type: object
required:
- name
- evaluation_type
- conditions
properties:
name:
type: string
maxLength: 200
description: Human-readable monitor name
description:
type: string
maxLength: 2000
default: ''
evaluation_type:
$ref: '#/components/schemas/MonitorEvaluationType'
scope:
$ref: '#/components/schemas/MonitorScope'
match_mode:
$ref: '#/components/schemas/MonitorMatchMode'
cooldown_seconds:
type: integer
minimum: 0
maximum: 86400
default: 0
custom_message_template:
type: string
nullable: true
maxLength: 5000
agent_ids:
type: array
nullable: true
items:
type: string
required_tags:
type: array
nullable: true
items:
type: string
scheduled_run_ids:
type: array
nullable: true
items:
type: string
conditions:
type: array
minItems: 1
items:
$ref: '#/components/schemas/ConditionInput'
channels:
type: array
items:
$ref: '#/components/schemas/ChannelInput'
default: []
MonitorEvaluationType:
type: string
enum:
- ON_RUN_COMPLETE
description: When the monitor evaluates
MonitorScope:
type: string
enum:
- ALL
- MONITORING
- SIMULATION
description: Which runs the monitor applies to
MonitorCondition:
type: object
required:
- ulid
- metric_id
- aggregation
- operator
properties:
ulid:
type: string
pattern: ^[0-9A-Z]{26}$
description: Condition ULID
metric_id:
type: string
description: ULID of the metric to evaluate
aggregation:
$ref: '#/components/schemas/ConditionAggregation'
operator:
$ref: '#/components/schemas/ConditionOperator'
threshold_float:
type: number
nullable: true
description: Numeric threshold
threshold_string:
type: string
nullable: true
description: String threshold
window_size_days:
type: integer
nullable: true
minimum: 1
maximum: 365
description: Rolling window size in days
window_size_runs:
type: integer
nullable: true
minimum: 1
maximum: 10000
description: Rolling window size in runs
match_value:
type: string
nullable: true
description: String match value for fraction conditions
match_mode:
type: string
nullable: true
enum:
- exact
- contains
- contains_case_insensitive
description: String match mode
TestEvaluateResponse:
type: object
required:
- monitor_id
- run_id
- triggered
properties:
monitor_id:
type: string
description: Monitor ULID
run_id:
type: string
description: Run that was evaluated
triggered:
type: boolean
description: Whether the monitor would have triggered
suppressed:
type: boolean
default: false
description: Whether cooldown would have suppressed the trigger
condition_results:
type: array
items:
type: object
description: Per-condition evaluation results
dispatch_results:
type: array
items:
type: object
description: Empty for dry-run (no dispatching)
message:
type: string
description: Generated notification message
ConditionAggregation:
type: string
enum:
- SINGLE
- RUN_AVERAGE
- RUN_FRACTION
description: 'Aggregation mode for a condition.
- SINGLE: Per-simulation value (average across sim outputs)
- RUN_AVERAGE: Average from pre-computed run aggregate
- RUN_FRACTION: Fraction matching a string value in the run
'
ChannelInput:
type: object
required:
- channel_type
- config
properties:
channel_type:
$ref: '#/components/schemas/ChannelType'
config:
type: object
description: Channel-specific configuration
MonitorMatchMode:
type: string
enum:
- ALL
- ANY
description: How multiple conditions are combined (AND vs OR)
ConditionInput:
type: object
required:
- metric_id
- aggregation
- operator
properties:
metric_id:
type: string
description: ULID of the metric to evaluate
aggregation:
$ref: '#/components/schemas/ConditionAggregation'
operator:
$ref: '#/components/schemas/ConditionOperator'
threshold_float:
type: number
nullable: true
threshold_string:
type: string
nullable: true
window_size_days:
type: integer
nullable: true
minimum: 1
maximum: 365
window_size_runs:
type: integer
nullable: true
minimum: 1
maximum: 10000
match_value:
type: string
nullable: true
match_mode:
type: string
nullable: true
enum:
- exact
- contains
- contains_case_insensitive
UpdateMonitorRequest:
type: object
properties:
name:
type: string
maxLength: 200
description:
type: string
maxLength: 2000
evaluation_type:
$ref: '#/components/schemas/MonitorEvaluationType'
scope:
$ref: '#/components/schemas/MonitorScope'
match_mode:
$ref: '#/components/schemas/MonitorMatchMode'
cooldown_seconds:
type: integer
minimum: 0
maximum: 86400
custom_message_template:
type: string
nullable: true
agent_ids:
type: array
nullable: true
items:
type: string
required_tags:
type: array
nullable: true
items:
type: string
scheduled_run_ids:
type: array
nullable: true
items:
type: string
conditions:
type: array
minItems: 1
items:
$ref: '#/components/schemas/ConditionInput'
description: Replaces all existing conditions
channels:
type: array
items:
$ref: '#/components/schemas/ChannelInput'
description: Replaces all existing channels
MonitorResource:
type: object
required:
- ulid
- name
- status
- evaluation_type
- scope
- match_mode
- cooldown_seconds
- trigger_count
- conditions
- channels
- create_time
- update_time
properties:
ulid:
type: string
pattern: ^[0-9A-Z]{26}$
description: Monitor ULID
example: 01HZ0EXAMPLE00000000000000
name:
type: string
maxLength: 200
description: Human-readable monitor name
example: Latency SLA Monitor
description:
type: string
default: ''
description: Optional description
status:
type: string
enum:
- ACTIVE
- DELETED
description: Monitor status
evaluation_type:
$ref: '#/components/schemas/MonitorEvaluationType'
scope:
$ref: '#/components/schemas/MonitorScope'
match_mode:
$ref: '#/components/schemas/MonitorMatchMode'
cooldown_seconds:
type: integer
minimum: 0
maximum: 86400
description: Minimum seconds between triggers
custom_message_template:
type: string
nullable: true
maxLength: 5000
description: Custom notification message template
agent_ids:
type: array
nullable: true
items:
type: string
description: Restrict to specific agent IDs
required_tags:
type: array
nullable: true
items:
type: string
description: Restrict to runs with these tags
scheduled_run_ids:
type: array
nullable: true
items:
type: string
description: Restrict to runs originating from these scheduled runs
trigger_count:
type: integer
description: Number of times this monitor has triggered
last_triggered_at:
type: string
format: date-time
nullable: true
description: Last trigger timestamp
conditions:
type: array
items:
$ref: '#/components/schemas/MonitorCondition'
description: Evaluation conditions
channels:
type: array
items:
$ref: '#/components/schemas/MonitorChannel'
description: Notification channels
create_time:
type: string
format: date-time
description: Creation timestamp
update_time:
type: string
format: date-time
description: Last update timestamp
examples:
CreateMonitorSimple:
summary: Simple threshold monitor
value:
name: Low Resolution Rate Alert
evaluation_type: ON_RUN_COMPLETE
scope: MONITORING
conditions:
- metric_id: 01HZMETRIC0000000000000000
aggregation: SINGLE
operator: LT
threshold_float: 0.7
channels:
- channel_type: SLACK
config:
channel_id: C12345
channel_name: '#alerts'
ListMonitorsSuccess:
summary: Successful list response
value:
monitors:
- ulid: 01HZ0EXAMPLE00000000000000
name: Latency SLA Monitor
description: Alert when average response latency exceeds 500ms
status: ACTIVE
evaluation_type: ON_RUN_COMPLETE
scope: MONITORING
match_mode: ALL
cooldown_seconds: 3600
trigger_count: 5
last_triggered_at: '2026-03-01T12:00:00Z'
conditions:
- ulid: 01HZCOND100000000000000000
metric_id: 01HZMETRIC0000000000000000
aggregation: RUN_AVERAGE
operator: GT
threshold_float: 500.0
channels:
- ulid: 01HZCHAN100000000000000000
channel_type: SLACK
config:
channel_id: C0123ABC
channel_name: '#sla-alerts'
create_time: '2026-02-15T10:30:00Z'
update_time: '2026-02-15T10:30:00Z'
next_page_token: null
total_count: 1
parameters:
MonitorId:
name: monitor_id
in: path
required: true
schema:
type: string
pattern: ^[0-9A-Z]{26}$
description: Monitor ULID
example: 01HZ0EXAMPLE00000000000000
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: x-api-key
description: API key for authentication
x-visibility: external