Schedule Triggers
Schedule triggers execute agents autonomously on a cron schedule — no user interaction needed. Use cases: daily reports, expiration monitoring, proactive alerts.
Configuration
{
"trigger_type": "schedule",
"trigger_config": {
"cron": "0 8 * * 1-5",
"timezone": "America/Sao_Paulo",
"query_template": "List contracts expiring in the next 30 days and send a summary email to manager@company.com",
"enabled_days": ["monday", "tuesday", "wednesday", "thursday", "friday"],
"session_strategy": {
"mode": "new"
},
"output": {
"channel": "silent"
}
}
}
Schedule Config Fields
| Field | Type | Required | Description |
|---|---|---|---|
cron | string | Yes | Cron expression (5 or 6 fields). Example: "0 8 * * 1-5" = 8am weekdays |
timezone | string | No | IANA timezone (default: "America/Sao_Paulo") |
query_template | string | Yes | The task for the agent — natural language instruction (min 10 chars) |
enabled_days | string[] | No | Restrict execution to specific days: ["monday", "friday"]. Empty = all days |
session_strategy | object | No | Session behavior between executions |
output | object | No | How to deliver the agent's response |
memory_config | object | No | Save result to agent memory for context transport |
Session Strategy
| Mode | Description | Use When |
|---|---|---|
new (default) | New session per execution (UUID) | Reports, independent tasks |
persistent | Same session across executions (deterministic hash of trigger_id + agent_id) | Monitoring with context buildup |
With persistent mode, the agent accumulates context between runs — token usage decreases as the session grows (compaction handles long sessions automatically).
Output Channels
The agent executes the query_template using its configured tools. The output channel controls what happens with the agent's text response after execution.
| Channel | Description | When to Use |
|---|---|---|
silent (default) | No external delivery — the agent handles output via its own tools (e.g., send_email) | Email reports, API writes, database updates |
webhook | POST JSON to a URL (no auth) | Slack incoming webhooks, n8n triggers, simple automations |
callback | HTTP with auth + template + transform — same mechanism as webhook async callbacks | WhatsApp (Twilio), Slack API, Teams, any authenticated API |
Output: silent
The agent is responsible for delivering results via its tools. The schedule trigger only executes the agent — no external delivery.
{
"output": { "channel": "silent" }
}
Example: The query_template instructs the agent to "list contracts and send by email to manager@company.com". The agent calls manage_contracts + send_email tools autonomously.
Output: webhook
Simple JSON POST to a URL. No authentication, no template.
{
"output": {
"channel": "webhook",
"webhook_url": "https://hooks.slack.com/services/xxx/yyy/zzz"
}
}
Payload sent:
{
"trigger_id": "uuid",
"agent_id": "uuid",
"success": true,
"content": "Agent response text...",
"execution_time_ms": 12000,
"iterations": 2,
"timestamp": "2026-03-18T08:00:12Z"
}
Output: callback
Full HTTP callback with authentication, custom body template, response transform, and optional chunking. Reuses the same http-callback infrastructure as webhook async execution.
{
"output": {
"channel": "callback",
"callback": {
"url": "https://api.twilio.com/2010-04-01/Accounts/ACxxx/Messages.json",
"method": "POST",
"content_type": "application/x-www-form-urlencoded",
"auth": {
"type": "basic",
"secret_ref": "TWILIO_CREDENTIALS"
},
"body_template": {
"To": "whatsapp:+556298226333",
"Body": "{{agent_response}}",
"From": "whatsapp:+14155238886"
},
"response_transform": {
"strip_markdown": true,
"max_length": 1500
}
},
"split_config": {
"enabled": true,
"max_chunk_size": 1500,
"max_chunks": 6,
"inter_chunk_delay_ms": 800
}
}
}
Template placeholders: {{agent_response}}, {{trigger_id}}, {{trigger_name}}, {{agent_id}}, {{timestamp}}
Auth types: basic (secret = base64 of user:pass), bearer (secret = token), api_key (secret = HeaderName:value), none
Memory Config — Context Transport
Problem: Schedule creates an isolated session. When the user responds later (e.g., via WhatsApp), they land in a different session. The agent loses the schedule context.
Solution: memory_config saves the schedule result to agent_memory, linked to the output recipient. On the user's next turn, the memory is automatically injected into the agent's prompt.
{
"memory_config": {
"enabled": true,
"user_key_field": "To",
"strip_prefix": "whatsapp:",
"ttl_hours": 48
}
}
| Field | Type | Description |
|---|---|---|
enabled | boolean | Enable/disable memory saving |
user_key_field | string | Field from callback.body_template to derive user identity (e.g., "To") |
strip_prefix | string | Prefix to remove from the value (e.g., "whatsapp:") |
ttl_hours | number | Memory TTL in hours (1-720, default: 48). Memory auto-expires after this |
max_content_length | number | Truncate agent response before saving (default: 1000 chars) |
user_key | string | Fixed user key (alternative to user_key_field when there's no body_template) |
Flow:
Schedule → isolated session → agent executes → result saved to agent_memory
user_key derived from output recipient
expires_at = now + ttl_hours
User responds (WhatsApp/chat) → different session → load-session recalls memory
→ "Scheduled Reports" section in prompt
→ agent responds WITH schedule context
Schedule reports are saved by the system, not the agent. They are recalled automatically — the agent does not need skill_config enabled for this to work.
Management Endpoints
| Endpoint | Method | Auth | Description |
|---|---|---|---|
GET /api/triggers/schedule/status | GET | Hybrid | Active cron jobs and execution counts |
POST /api/triggers/schedule/reload | POST | JWT | Reload all schedule triggers (without server restart) |
Example: Daily Report via WhatsApp
Complete trigger configuration for a daily contract report delivered via WhatsApp at 8am on weekdays, with memory transport so the user can follow up:
{
"agent_id": "uuid",
"name": "Daily Contract Report",
"trigger_type": "schedule",
"trigger_config": {
"cron": "0 8 * * 1-5",
"timezone": "America/Sao_Paulo",
"query_template": "List contracts expiring in the next 30 days. Be concise: client name, value, expiration date.",
"enabled_days": ["monday", "tuesday", "wednesday", "thursday", "friday"],
"session_strategy": { "mode": "new" },
"output": {
"channel": "callback",
"callback": {
"url": "https://api.twilio.com/2010-04-01/Accounts/ACxxx/Messages.json",
"method": "POST",
"content_type": "application/x-www-form-urlencoded",
"auth": { "type": "basic", "secret_ref": "TWILIO_CREDENTIALS" },
"body_template": {
"To": "whatsapp:+556298226333",
"Body": "{{agent_response}}",
"From": "whatsapp:+14155238886"
},
"response_transform": { "strip_markdown": true, "max_length": 1500 }
}
},
"memory_config": {
"enabled": true,
"user_key_field": "To",
"strip_prefix": "whatsapp:",
"ttl_hours": 48
}
}
}
Example: Daily Report via Email (using agent tools)
The agent uses its own send_email tool — output is silent:
{
"agent_id": "uuid",
"name": "Daily Contract Report (Email)",
"trigger_type": "schedule",
"trigger_config": {
"cron": "0 8 * * 1-5",
"timezone": "America/Sao_Paulo",
"query_template": "List contracts expiring in the next 30 days. Send the result by email to manager@company.com with subject 'Daily Report: Expiring Contracts'. If none, send the email anyway saying there are no pending expirations.",
"session_strategy": { "mode": "new" },
"output": { "channel": "silent" }
}
}
For outbound messages to Brazilian numbers, use 12 digits (+55 + area code + 8-digit number, without the 9th digit):
| Format | Example | Works? |
|---|---|---|
+55 DD 9XXXX-XXXX (13 digits) | +5562998226333 | NO — Twilio accepts (201) but does not deliver |
+55 DD XXXX-XXXX (12 digits) | +556298226333 | YES |
Twilio Sandbox requires the recipient to send "join" to the sandbox number before receiving outbound messages. Without opt-in, messages are silently dropped (201 response, no error).