Scheduled and triggered runs
A deployment starts agent sessions for you, either on a schedule or when an event arrives. Every firing creates a new session with a message you define, so the run shows up in session lists, executions and usage exactly like a session you started yourself.
This page is about the /v1/deployments API. Giving a Console project its runtime node is a different thing, covered in Deployment.
When to use it
| Trigger | Example |
|---|---|
schedule | Every weekday at 07:00, summarise yesterday’s support tickets and post the digest. |
webhook | When your shop system posts a new order, check it for fraud signals. |
manual | A button in your admin panel that runs a fixed task on demand, with a fixed agent and settings. |
If your own code already decides when to run, call POST /v1/sessions directly; a deployment adds nothing.
Endpoints
| Method | Path | Purpose |
|---|---|---|
POST | /v1/deployments | Create a deployment. |
GET | /v1/deployments | List deployments. |
GET | /v1/deployments/{deployment_id} | Read one, including next and last run. |
PATCH | /v1/deployments/{deployment_id} | Change it. |
POST | /v1/deployments/{deployment_id}/pause | Stop firing. |
POST | /v1/deployments/{deployment_id}/resume | Start firing again. |
POST | /v1/deployments/{deployment_id}/run | Fire once now, whatever the trigger. |
GET | /v1/deployments/{deployment_id}/runs | History of firings and the sessions they created. |
DELETE | /v1/deployments/{deployment_id} | Delete. Sessions it created are kept. |
POST | /v1/deployments/{deployment_id}/trigger | The inbound URL for webhook deployments (see below). |
Base URL https://app.syntic.ai/v1 (the paths above already include /v1). Authenticate with a private key and send syntic-beta: agents-2026-11-01, except on the inbound /trigger URL, which uses its own signing secret.
The deployment object
{
"id": "dep_01JC9E2G4J6L8N0Q2S4U",
"type": "deployment",
"name": "daily-ticket-digest",
"agent_id": "agt_01JC4R8W6Q2N9X3T5V7Y",
"agent_version": null,
"status": "active",
"trigger": {
"type": "schedule",
"cron": "0 7 * * 1-5",
"timezone": "Europe/London"
},
"input_template": "Summarise the support tickets opened on {{run.date_previous_day}}. Post the digest to #support-leads.",
"session_options": { "memory_scope": "team:support", "limits": { "max_turns": 20 } },
"concurrency": "skip_if_running",
"next_run_at": "2026-11-06T07:00:00Z",
"last_run": { "id": "drn_01JC9F...", "status": "succeeded", "session_id": "sesn_01JC9F...", "fired_at": "2026-11-05T07:00:00Z" },
"created_at": "2026-11-01T12:00:00Z"
}| Field | Required on create | Description |
|---|---|---|
name | Yes | Unique in the workspace. |
agent_id | Yes | An active agent definition. |
agent_version | No | Pin a version; null means latest at firing time. |
trigger | Yes | See Triggers. |
input_template | Yes | The first user message of each session. {{...}} placeholders are filled at firing time; see Templates. Up to 20,000 characters. |
session_options | No | Any POST /v1/sessions options except agent_id and input: memory_scope, environment_id, limits, metadata. |
concurrency | No | What happens if the previous run’s session is still running: skip_if_running (default), queue (start after it ends, at most one waiting) or allow (start in parallel). |
status | active or paused. A deployment is paused automatically after 5 failed firings in a row. |
Triggers
Schedule
{ "type": "schedule", "cron": "0 7 * * 1-5", "timezone": "Europe/London" }cron uses the usual five-field syntax; 0 7 * * 1-5, for example, means 07:00 on weekdays. timezone is an IANA name; default UTC. Daylight-saving changes follow the time zone, so a 07:00 job stays at 07:00 local time. The minimum interval is 5 minutes. Firings happen within 60 seconds of the scheduled time.
Webhook
{ "type": "webhook" }When you create a webhook deployment, the response includes a trigger_url and a signing_secret (shown once). Your system calls:
POST https://app.syntic.ai/v1/deployments/dep_01JC9E2G4J6L8N0Q2S4U/trigger
Content-Type: application/json
syntic-signature: t=1762300800,v1=5d41402abc4b2a76b9719d911017c592...
{"order_id": "88123", "total": 412.50}The signature is an HMAC-SHA256 of "<t>.<raw body>" with the signing secret, hex-encoded, the same scheme Syntic uses for outgoing webhooks. Requests older than 5 minutes or with a bad signature are refused with 401. The JSON body (up to 256 KB) is available to the template as {{payload}}. The response is 202 Accepted with the run id.
Manual
{ "type": "manual" }Fires only through POST /v1/deployments/{id}/run. That endpoint also accepts an optional payload object for any trigger type.
Templates
| Placeholder | Value |
|---|---|
{{run.id}} | The firing’s id. |
{{run.fired_at}} | Firing time, RFC 3339 in the deployment’s time zone. |
{{run.date}}, {{run.date_previous_day}} | Dates as YYYY-MM-DD in the deployment’s time zone. |
{{payload}} | The webhook or manual payload as pretty-printed JSON. |
{{payload.some.path}} | One value from the payload. Missing paths become an empty string. |
Placeholders are substituted as text. Treat webhook payloads as untrusted input: they become part of a user message, and the agent’s instructions should say how to handle content it did not expect.
Create a scheduled deployment
curl -sS https://app.syntic.ai/v1/deployments \
-H "Authorization: Bearer $SYNTIC_API_KEY" \
-H "Content-Type: application/json" \
-H "syntic-beta: agents-2026-11-01" \
-d '{
"name": "daily-ticket-digest",
"agent_id": "agt_01JC4R8W6Q2N9X3T5V7Y",
"trigger": {"type": "schedule", "cron": "0 7 * * 1-5", "timezone": "Europe/London"},
"input_template": "Summarise the support tickets opened on {{run.date_previous_day}}.",
"concurrency": "skip_if_running"
}'Run history
GET /v1/deployments/{id}/runs lists firings, newest first, with cursor pagination (limit, after_id, before_id). Each run has id, fired_at, trigger_type, status (started, skipped, queued, succeeded, failed), session_id (when a session was created), skip_reason (for example previous_run_active) and error. A run is succeeded when its session’s turn ends with outcome end_turn, and failed for any other outcome or if the session could not be started.
Subscribe to deployment.run.failed with webhooks to hear about failures without polling.
Errors
Errors use the error envelope.
| Status | type | code | When |
|---|---|---|---|
| 400 | invalid_request | invalid_cron | The expression cannot be parsed or fires more often than every 5 minutes. |
| 400 | invalid_request | unknown_timezone | Not an IANA time zone name. |
| 400 | invalid_request | invalid_template | Unclosed or unknown placeholder. |
| 401 | authentication | invalid_signature | Inbound /trigger call with a missing, stale or wrong signature. |
| 404 | not_found | deployment_not_found | No such deployment. |
| 409 | invalid_request | agent_archived | The agent is archived. |
| 409 | invalid_request | deployment_paused | /trigger called while paused. /run still works. |
| 413 | invalid_request | payload_too_large | Inbound payload over 256 KB. |
Limits and billing
| Limit | Value |
|---|---|
| Deployments per workspace | 200 |
| Minimum schedule interval | 5 minutes |
Inbound /trigger calls | 60 per minute per deployment |
| Run history kept | 90 days |
Deployments are free; the sessions they start are billed like any other session.
Build notes for the platform team
- Reuse the workflow trigger tick and activation functions for schedules and inbound webhooks.
- Each firing calls the same internal path as
POST /v1/sessions, so limits, billing and audit stay identical. - The auto-pause after 5 consecutive failures writes an audit event and sends
deployment.paused.
Related
- Sessions
- Agent definitions API
- Webhooks
- Deployment: runtime nodes for Console agents