Create a repeatable or manually triggered Template execution configuration.
POST /api/v1/forward/schedules
Creates a Schedule that binds one Identity, one Template, initial input events, trigger policy, and execution policy. Each trigger creates a separate Schedule Run.
Headers
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer <PAT or SAT> |
Content-Type | Yes | application/json |
Idempotency-Key | No | Optional idempotency key for unsafe requests. |
Body parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
identity_id | string | Yes | Forward Identity ID that owns the Schedule. |
template_id | string | Yes | Forward Template ID to execute. |
name | string | Yes | Schedule name. |
description | string | No | Schedule description. |
initial_events | array | Yes | Events injected at each execution. Current design supports user.message. |
execution | object | No | Execution policy. Defaults are applied when omitted. |
trigger_policy | object|null | No | Trigger policy. Omitted or null becomes {"type":"manual"}. |
environment_id | string | Yes | Execution environment. |
sinks | array|null | No | Delivery targets for execution results. The array form is retained for compatibility; currently, at most one item is allowed. |
metadata | object | No | Custom metadata for labels or pass-through data only. |
Trigger policy
trigger_policy is a typed object. Create and update requests accept only configuration fields: type, expression, and timezone. upcoming_runs_at and last_run_at are response-only fields computed by Forward.
| Field | Type | Required | Description |
|---|---|---|---|
type | string | Yes | cron, once, interval, or manual. |
expression | string | Conditional | Trigger expression. Required for cron, once, and interval; not needed for manual. |
timezone | string | Conditional | IANA timezone, such as Asia/Shanghai. Required for cron; required for once only when expression has no offset; optional for interval and manual. |
upcoming_runs_at | array | Response only | Upcoming trigger times in UTC ISO 8601. The current implementation returns [] or the next scheduled time only. |
last_run_at | string|null | Response only | Most recent trigger time. |
type | Required input | Description |
|---|---|---|
cron | type, expression, timezone | Repeats by a 5-field cron expression in the given IANA timezone. |
once | type, expression, optional timezone | Runs once at an ISO 8601 time. timezone is required when the expression has no offset. |
interval | type, expression | Repeats by an ISO 8601 duration, such as PT15M. |
manual | type | Never runs automatically. Trigger with the Run Schedule endpoint. |
type | expression format | Example | Notes |
|---|---|---|---|
cron | Standard 5-field cron | 0 9 * * * | Minute-level calendar schedule. Seconds and 6-field cron are not supported. |
once | ISO 8601 absolute time | 2026-06-23T09:00:00 or 2026-06-23T01:00:00Z | Runs once. The target time must be at least one minute in the future. |
interval | ISO 8601 duration | PT15M, PT1H, P1D | Fixed interval schedule. Minimum interval is one minute. |
manual | Omitted or empty | - | Does not create scheduled runs. |
Execution policy
| Field | Type | Default | Description |
|---|---|---|---|
session_mode | string | new_session | new_session or reuse_session. |
max_concurrent_runs | integer | 1 | Maximum concurrent runs for this Schedule. |
max_attempts | integer | 1 | Maximum actual attempts per Run. Allowed values: 1 or 2; values outside this range are rejected. |
timeout_ms | integer | 300000 | Timeout for one attempt. |
session_mode | Description |
|---|---|
new_session | Creates a new execution Session for each trigger. Previous context is not reused. |
reuse_session | Forward manages one fixed execution Session for this Schedule and reuses it across triggers. Callers cannot provide an arbitrary existing session_id. |
max_attempts=2 means the server may automatically retry once after the first execution fails. Whether a retry actually happens depends on the failure type — parameter errors, permission errors, concurrency limits, timeouts, or connection interruptions are not guaranteed to be retried. Clients can check the attempt field on a Schedule Run to see how many attempts were made.
Omitting execution.max_attempts at creation uses the default value 1. When explicitly provided, only integers 1 or 2 are accepted.
Delivery target
sinks retains its array form for compatibility. Omitting it, passing null, or passing [] creates a Schedule without a delivery target. One valid item configures delivery. Two or more items return HTTP 400 unsupported_sinks_input. Each item must use one of these public formats:
external_id is the channel-native opaque user or group ID. Reachability is not probed when the Schedule is created. target.type=group supports only a root group, not a thread or topic.
Send /target in a channel IM conversation to obtain the target configuration for that conversation.
Runtime delivery failures are recorded as push_status=failed on the corresponding Run and do not change the Agent Run status.
Pair mode delivery
First pair the channel, then obtain the id of an active record from List channel pairings and configure it as channel_pairing_id in sinks:
identity_id and template_id must match the pairing. After the pairing is unbound, delivery to that binding stops.
Example request
Example response
HTTP 200 OK
Response fields
Returns the complete Schedule object after creation. When created through this endpoint, source="api" and source_session_id=null. See Creation source for shared source semantics.
Errors
| HTTP | Type | Code | Trigger |
|---|---|---|---|
| 400 | invalid_request_error | invalid_trigger_policy | Trigger policy type or expression is invalid. |
| 400 | invalid_request_error | trigger_policy_too_frequent | Trigger interval is less than one minute. |
| 400 | invalid_request_error | trigger_policy_time_too_soon | once target time is too soon. |
| 400 | invalid_request_error | invalid_request | execution.max_attempts is not in the 1..2 range. |
| 400 | invalid_request_error | unsupported_sinks_input | The number, type, or fields of sinks are invalid, or the fixed Channel is unavailable, belongs to another user, or does not match the execution context. |
| 404 | not_found_error | identity_not_found | Identity does not exist. |
| 404 | not_found_error | template_not_found | Template does not exist. |
| 401 | authentication_error | authentication_required | The PAT or SAT is invalid or expired. |
Notes
- Omitting
sinks, passingnull, or passing[]returnssinks: []. manualschedules can only be triggered withPOST /api/v1/forward/schedules/{schedule_id}/run.onceschedules are automatically archived after their first scheduled run reaches a terminal state.

