Schedules & Triggers API
Automate agent execution with cron-based schedules, webhook triggers, and event-driven invocation. Schedule endpoints use the /api/agents/:name/schedules base path. Schedules are executed by a dedicated scheduler service.
All endpoints require JWT Bearer token unless noted. See Authentication for details. Full interactive API docs at http://localhost:8000/docs.
Schedule CRUD
/api/agents/:name/schedules
List all schedules for an agent.
curl -H "Authorization: Bearer <token>" \
http://localhost:8000/api/agents/research-agent/schedules
# Response
[
{
"id": "sch_abc123",
"agent_name": "research-agent",
"name": "Daily Report",
"cron_expression": "0 9 * * *",
"message": "Generate the daily research report",
"enabled": true,
"timezone": "UTC",
"timeout_seconds": 900,
"next_run_at": "2026-03-27T09:00:00Z",
"last_run_at": "2026-03-26T09:00:00Z"
}
]/api/agents/:name/schedules
Create a new schedule for an agent. The cron expression is validated server-side.
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | Yes | Display name for the schedule |
| cron_expression | string | Yes | Standard cron syntax, e.g. "0 9 * * *" |
| message | string | Yes | The task message sent to the agent |
| enabled | boolean | No | Whether schedule is active (default: true) |
| timezone | string | No | Any IANA zone name, legacy aliases included (e.g. "US/Eastern", "Europe/Kiev"); a zone the platform cannot resolve is rejected on create with a message naming the problem (default: "UTC") |
| description | string | No | Human-readable description |
| timeout_seconds | integer | No | Per-schedule timeout. Unset (null) inherits the agent's execution_timeout_seconds cap; a value above the cap returns 400 error=schedule_timeout_exceeds_agent_cap |
| allowed_tools | string[] | No | Restrict tools available during execution |
| model | string | No | Model override (Fable 5.1, Sonnet 5, Opus, Haiku, or custom) |
| deliver_to_workspace_email | string | No | Deliver the run's output as a message in this person's Workspace chat with the agent (see below) |
| max_retries | integer | No | Retry attempts on failure, 0-5 (default: 1; 0 disables) |
| retry_delay_seconds | integer | No | Delay between retries, 30-600 (default: 60) |
curl -X POST -H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"name": "Daily Report",
"cron_expression": "0 9 * * *",
"message": "Generate the daily research report",
"timezone": "America/New_York",
"timeout_seconds": 600
}' \
http://localhost:8000/api/agents/research-agent/schedules
# Response (201 Created)
{
"id": "sch_abc123",
"agent_name": "research-agent",
"name": "Daily Report",
"cron_expression": "0 9 * * *",
"enabled": true,
"timezone": "America/New_York",
"timeout_seconds": 600,
"created_at": "2026-03-26T12:00:00Z"
}/api/agents/:name/schedules/:id
Get a specific schedule by ID.
/api/agents/:name/schedules/:id
Update a schedule. Only include fields you want to change.
| Name | Type | Required | Description |
|---|---|---|---|
| name | string | No | Updated display name |
| cron_expression | string | No | Updated cron expression |
| message | string | No | Updated task message |
| enabled | boolean | No | Enable or disable |
| timezone | string | No | Updated timezone |
| timeout_seconds | integer | No | Updated timeout (cannot exceed the agent's cap) |
| deliver_to_workspace_email | string | null | No | Change the delivery recipient; null stops delivering. Leave it out and nothing changes |
Delivering a Run's Output to Someone's Workspace
By default a scheduled run ends in the executions list — an operator's view. If the run exists for one particular person (a morning brief, a weekly summary), you can name them and have the output arrive as a message in their Workspace conversation with that agent instead of only in the log. Set deliver_to_workspace_email on the schedule:
curl -X POST http://localhost:8000/api/agents/my-agent/schedules \
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
-d '{
"name": "morning-brief",
"cron_expression": "0 7 * * 1-5",
"message": "Write my morning brief.",
"timezone": "Europe/Kyiv",
"deliver_to_workspace_email": "person@example.com"
}'The same field is available on the create_agent_schedule and update_agent_schedule MCP tools; passing null on an update stops delivering. Leave it unset and nothing changes. The schedule form in the UI has no field for it.
- Where it lands. In that person's Main chat with the agent — the one constant conversation per person per agent. It reads like any other message from the agent, and it can be rated like one.
- Who you can name. Someone the agent is already shared with, or its owner. If the address cannot reach the agent — never shared, share revoked, blocked, or simply unknown — the run fails and says so on the execution row rather than running and quietly delivering nowhere. A malformed address is rejected when you save the schedule, not hours later on the first fire.
- Who can set it.You can always name yourself. Naming anyone else takes the agent's owner or an admin — on create and on update alike. Clearing the address is never restricted.
- When it shows up. The message is saved as soon as the run finishes, but the Workspace does not poll a conversation you already have open, so it appears the next time that person loads the Workspace or switches to the chat. It also avoids cutting into a turn already in progress in that chat: it waits up to two minutes for that reply to finish, then writes regardless.
- Delivered once. Each fire delivers at most one message, even if the platform has to retry internally.
- Rateable. The delivered message is an ordinary reply from the agent, so the person can rate it like any other — see Workspace.
/api/agents/:name/schedules/:id
Delete a schedule. Returns 204 No Content on success.
Schedule Control
/api/agents/:name/schedules/:id/enable
Enable a schedule (owner or admin). The dedicated scheduler picks up the change automatically.
/api/agents/:name/schedules/:id/disable
Disable a schedule without deleting it (owner or admin).
/api/agents/:name/schedules/:id/trigger
Manually trigger a schedule execution immediately (owner or admin). Delegates to the dedicated scheduler service which handles distributed locking, execution tracking, and agent execution. A manual trigger bypasses the template's pre-check hook.
curl -X POST -H "Authorization: Bearer <token>" \
http://localhost:8000/api/agents/research-agent/schedules/sch_abc123/trigger
# Response
{
"status": "triggered",
"schedule_id": "sch_abc123",
"schedule_name": "Daily Report",
"agent_name": "research-agent",
"message": "Execution started"
}Execution History
Enabling, disabling, and manually triggering a schedule are owner or admin actions — someone the agent is merely shared with can read its schedules and their history but cannot start or stop them. The same applies to the toggle_agent_schedule and trigger_agent_schedule MCP tools.
/api/agents/:name/schedules/:id/executions
Get execution history for a specific schedule.
| Name | Type | Required | Description |
|---|---|---|---|
| limit | integer | No | Max results (default: 50) |
/api/agents/:name/schedules/:id/analytics
Per-schedule analytics: execution counts by status, success rate, duration p50/p95/p99, cost totals, the top 5 tool calls, and a gap-filled daily timeline (UTC day buckets). Read-only — works even when the agent is stopped. On high-traffic schedules the duration percentiles are computed over the newest 5,000 successful runs; the response reports sampled: true when this cap applies.
| Name | Type | Required | Description |
|---|---|---|---|
| window_hours | integer | No | 24, 168 (7 days), or 720 (30 days). Default: 168 |
/api/agents/:name/schedules/analytics-summary
Per-schedule performance rollup for the whole agent — one row per schedule, including zero-run schedules (success rate, average duration, run count, tool-call count). Feeds the Schedules tab inline stats and the Overview tab's "Schedules performance" section.
| Name | Type | Required | Description |
|---|---|---|---|
| window | string | No | "7d", "14d", or "30d" |
/api/agents/:name/executions
Get execution summaries for an agent. Returns lightweight objects optimized for list views (large fields like response and logs excluded).
curl -H "Authorization: Bearer <token>" \
http://localhost:8000/api/agents/research-agent/executions
# Response
[
{
"id": "exec_xyz789",
"schedule_id": "sch_abc123",
"agent_name": "research-agent",
"status": "success",
"started_at": "2026-03-26T09:00:00Z",
"completed_at": "2026-03-26T09:05:30Z",
"duration_ms": 330000,
"message": "Generate the daily research report",
"triggered_by": "schedule",
"cost": 0.045,
"model_used": "claude-sonnet-4-20250514"
}
]/api/agents/:name/executions/:id
Get full details of a specific execution including response text, errors, and tool calls.
/api/agents/:name/executions/:id/log
Get the full execution transcript as a JSON array. Includes all tool calls, thinking steps, and responses from the Claude Code session.
Webhook Triggers
External event triggers and internal execution endpoints for programmatic agent invocation.
Internal Execution (No Auth)
The /api/internal/* endpoints are not authenticated and should only be accessible within the Docker network. They are used by the scheduler service and agent containers.
/api/internal/execute-task
Execute a task internally. Used by the scheduler service, supports async_mode for background execution.
Slack Events
/api/public/slack/events
Slack event receiver endpoint. Handles incoming Slack events and routes them to the appropriate agent.
Event Emission
/api/events
Emit an event that triggers agent subscriptions. Any agent subscribed to the event type will be invoked.
/api/agents/:name/emit-event
Emit an event for a specific agent. The event is scoped to that agent's subscriptions.
Cron Expression Format
Trinity uses standard 5-field cron expressions. The dedicated scheduler evaluates them with timezone support. In the UI, the presets Daily 9 AM, Weekly Mon, Every 6h and Every 30m fill the cron field for the common cadences, and the form validates the expression as you type with the same grammar the scheduler uses. The server remains the authority: an expression the form accepts but the scheduler rejects still fails on save with the reason, and a stored schedule whose expression the scheduler cannot register is flagged Invalid cron expression in the list and never fires until you fix it.
# Format: minute hour day-of-month month day-of-week
#
# Examples:
# Every day at 9 AM: 0 9 * * *
# Every hour: 0 * * * *
# Every 15 minutes: */15 * * * *
# Weekdays at 8:30 AM: 30 8 * * 1-5
# First day of month at noon: 0 12 1 * *Scheduler Behaviour
- Execution flow. The scheduler fires and sends a POST to
/api/internal/execute-taskwithasync_mode=True; the backend spawns a background task and returns immediately; the scheduler polls the database every 10 seconds until the execution completes and the record is updated with response, cost, and duration. - Misfire handling. If the scheduler restarts, missed jobs within a 1-hour grace window are caught up and fired immediately (
misfire_grace_time=3600,coalesce=True,max_instances=1). Missed jobs outside that window are not caught up. - Autonomy gate.Nothing fires while the agent's autonomy is off. It is a gate, not a bulk edit — each schedule keeps its own enabled/disabled state across the toggle.
- Capacity. Parallel execution is controlled by per-agent capacity slots (default 3); retries count against those slots. Execution timeout is per-agent configurable (default 60 minutes, max 2 hours).
- Per-schedule timeout. Creating or updating a schedule with
timeout_secondsabove the agent's cap returns400 error=schedule_timeout_exceeds_agent_cap; lowering the agent cap below an active schedule's timeout returns400 error=agent_timeout_below_active_schedules. Raise the agent cap first, then the schedule timeout. - Retries. A failed execution (error or timeout) retries after
retry_delay_secondswhile attempts remain; rate-limit errors (429) use 2x delay, capped at 300 seconds. Each retry is a new execution record linked to the original viaretry_of_execution_idwith anattempt_number; a failed run waiting to retry has statuspending_retry. - Template limits. A template may declare at most 20 schedules; beyond that the list is truncated, with the reason reported. If the agent has freeze schedules if sync failing enabled and its git sync has failed three times in a row, the scheduler skips firing until sync recovers.
Schedule Model
| Field | Type | Description |
|---|---|---|
| id | string | Unique schedule ID |
| agent_name | string | Owning agent name |
| name | string | Display name |
| cron_expression | string | 5-field cron expression |
| message | string | Task message sent to agent |
| enabled | boolean | Whether schedule is active |
| timezone | string | IANA timezone |
| timeout_seconds | integer? | Per-schedule timeout; null inherits the agent's cap |
| model | string? | Model override for this schedule |
| deliver_to_workspace_email | string? | Recipient whose Workspace chat receives the run's output |
| max_retries | integer | Retry attempts on failure (default: 1, 0-5) |
| retry_delay_seconds | integer | Delay between retries (default: 60, 30-600) |
| next_run_at | datetime? | Next scheduled execution time |
| last_run_at | datetime? | Last execution time |
Error Responses
| Status | Meaning |
|---|---|
| 400 | Invalid cron expression, unresolvable timezone, malformed deliver_to_workspace_email, or schedule_timeout_exceeds_agent_cap |
| 401 | Invalid or missing JWT token |
| 403 | Access denied to agent or schedule |
| 404 | Schedule or agent not found |
| 503 | Scheduler service unavailable |
| 504 | Scheduler service timeout |