Skip to main content
Trinity
API Reference

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

GET

/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"
  }
]
POST

/api/agents/:name/schedules

Create a new schedule for an agent. The cron expression is validated server-side.

NameTypeRequiredDescription
namestringYesDisplay name for the schedule
cron_expressionstringYesStandard cron syntax, e.g. "0 9 * * *"
messagestringYesThe task message sent to the agent
enabledbooleanNoWhether schedule is active (default: true)
timezonestringNoAny 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")
descriptionstringNoHuman-readable description
timeout_secondsintegerNoPer-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_toolsstring[]NoRestrict tools available during execution
modelstringNoModel override (Fable 5.1, Sonnet 5, Opus, Haiku, or custom)
deliver_to_workspace_emailstringNoDeliver the run's output as a message in this person's Workspace chat with the agent (see below)
max_retriesintegerNoRetry attempts on failure, 0-5 (default: 1; 0 disables)
retry_delay_secondsintegerNoDelay 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"
}
GET

/api/agents/:name/schedules/:id

Get a specific schedule by ID.

PUT

/api/agents/:name/schedules/:id

Update a schedule. Only include fields you want to change.

NameTypeRequiredDescription
namestringNoUpdated display name
cron_expressionstringNoUpdated cron expression
messagestringNoUpdated task message
enabledbooleanNoEnable or disable
timezonestringNoUpdated timezone
timeout_secondsintegerNoUpdated timeout (cannot exceed the agent's cap)
deliver_to_workspace_emailstring | nullNoChange 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.
DELETE

/api/agents/:name/schedules/:id

Delete a schedule. Returns 204 No Content on success.

Schedule Control

POST

/api/agents/:name/schedules/:id/enable

Enable a schedule (owner or admin). The dedicated scheduler picks up the change automatically.

POST

/api/agents/:name/schedules/:id/disable

Disable a schedule without deleting it (owner or admin).

POST

/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.

GET

/api/agents/:name/schedules/:id/executions

Get execution history for a specific schedule.

NameTypeRequiredDescription
limitintegerNoMax results (default: 50)
GET

/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.

NameTypeRequiredDescription
window_hoursintegerNo24, 168 (7 days), or 720 (30 days). Default: 168
GET

/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.

NameTypeRequiredDescription
windowstringNo"7d", "14d", or "30d"
GET

/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"
  }
]
GET

/api/agents/:name/executions/:id

Get full details of a specific execution including response text, errors, and tool calls.

GET

/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.

POST

/api/internal/execute-task

Execute a task internally. Used by the scheduler service, supports async_mode for background execution.

Slack Events

POST

/api/public/slack/events

Slack event receiver endpoint. Handles incoming Slack events and routes them to the appropriate agent.

Event Emission

POST

/api/events

Emit an event that triggers agent subscriptions. Any agent subscribed to the event type will be invoked.

POST

/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-task with async_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 returns 400 error=schedule_timeout_exceeds_agent_cap; lowering the agent cap below an active schedule's timeout returns 400 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_seconds while attempts remain; rate-limit errors (429) use 2x delay, capped at 300 seconds. Each retry is a new execution record linked to the original via retry_of_execution_id with an attempt_number; a failed run waiting to retry has status pending_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

FieldTypeDescription
idstringUnique schedule ID
agent_namestringOwning agent name
namestringDisplay name
cron_expressionstring5-field cron expression
messagestringTask message sent to agent
enabledbooleanWhether schedule is active
timezonestringIANA timezone
timeout_secondsinteger?Per-schedule timeout; null inherits the agent's cap
modelstring?Model override for this schedule
deliver_to_workspace_emailstring?Recipient whose Workspace chat receives the run's output
max_retriesintegerRetry attempts on failure (default: 1, 0-5)
retry_delay_secondsintegerDelay between retries (default: 60, 30-600)
next_run_atdatetime?Next scheduled execution time
last_run_atdatetime?Last execution time

Error Responses

StatusMeaning
400Invalid cron expression, unresolvable timezone, malformed deliver_to_workspace_email, or schedule_timeout_exceeds_agent_cap
401Invalid or missing JWT token
403Access denied to agent or schedule
404Schedule or agent not found
503Scheduler service unavailable
504Scheduler service timeout