---
title: "Create an automation"
method: POST
path: "/v1/programs/{program_id}/automations"
tags: ["Automations"]
---

# Create an automation

`POST /v1/programs/{program_id}/automations`

## Request

- Base URL: `https://api.scrip.dev`
- URL: `https://api.scrip.dev/v1/programs/{program_id}/automations`
- Auth: HTTP bearer

Create an automation within a program. The server stores `event_data` and submits it verbatim when the Trigger fires, once to the Program or to each matching Participant.

## Path parameters

- `program_id` string, uuid, required

## Headers

- `Idempotency-Key` string

## Request body

- CreateAutomationRequest: Creates an automation that sends an event on a schedule.
  - `description` string: Optional human-readable description of what this automation does
  - `event_data` object: JSON object stored with the automation and submitted unchanged when its trigger fires
  - `filter_hints` AutomationFilterHint[]: Checks derived from `participant_filter` that narrow the participants to consider. Scrip still evaluates the full CEL filter for each candidate.
    - `type` 'HAS_TAG' | 'HAS_ATTRIBUTE' | 'HAS_COUNTER', required: Check to apply before evaluating `participant_filter`. - HAS_TAG: value is a participant tag. - HAS_ATTRIBUTE: value is an attribute key. - HAS_COUNTER: value is a counter key.
    - `value` string, required: Non-empty tag value, attribute key, or counter key interpreted according to type.
  - `guard_condition` string: CEL expression evaluated at trigger time; skips the participant if false
  - `name` string, required: Human-readable label for this automation (1-255 chars)
  - `participant_filter` string: CEL expression that determines which participants are enrolled (scope=participants only)
  - `participant_id` string, uuid: Target participant for program-scoped one-time automations (scope=PROGRAM, ONE_TIME trigger only)
  - `scope` 'PROGRAM' | 'PARTICIPANTS', required: Whether the automation sends one event to the program (PROGRAM) or one event to each matching participant (PARTICIPANTS).
  - `trigger` AutomationTrigger, required: What starts an automation: a cron schedule, a single time, or a participant-state schedule.
    - `cron_expression` string: Cron expression for recurring schedules (cron trigger only)
    - `schedule_config` union: Configuration selected by schedule_type for a PARTICIPANT_STATE trigger. The matching anyOf branch documents its required fields; unrecognized keys are ignored.
      - object: Use when schedule_type is ATTRIBUTE_DATE. Reads a date from each participant's attribute.
        - `attribute_key` string, required: Participant attribute containing the date value.
        - `date_format` 'MM-DD' | 'YYYY-MM-DD', required: Format stored in the participant attribute: MM-DD recurs yearly; YYYY-MM-DD fires for one calendar date.
        - `offset` string: Optional signed duration applied to the attribute date. Accepted units are ns, us or µs, ms, s, m, h, d, and w; units may be combined, while months and years are not accepted.
      - object: Use when schedule_type is INTERVAL. Each participant fires relative to their own subscription time.
        - `interval` string, required: Positive duration between firings. Accepted units are ns, us or µs, ms, s, m, h, d, and w; units may be combined, while months and years are not accepted.
      - object: Use when schedule_type is CRON. All subscribed participants share the same calendar schedule.
        - `expression` string, required: Five-field cron expression or descriptor such as @hourly or @daily; inline timezone directives are not accepted.
      - object: Use when schedule_type is THRESHOLD. Fires after a participant counter satisfies the comparison.
        - `counter_key` string, required: Participant counter to compare.
        - `delay` string, required: Non-negative duration to wait after the comparison matches before firing. Accepted units are ns, us or µs, ms, s, m, h, d, and w; units may be combined, while months and years are not accepted.
        - `operator` '>=' | '>' | '==' | '<=' | '<', required: Comparison applied between the participant counter and threshold.
        - `threshold` number, required: Numeric counter value used by operator for the comparison; explicit 0 is valid.
    - `schedule_type` 'ATTRIBUTE_DATE' | 'INTERVAL' | 'CRON' | 'THRESHOLD': Subscription schedule for a PARTICIPANT_STATE trigger. - ATTRIBUTE_DATE: derive each participant's trigger time from a date-valued attribute. - INTERVAL: fire at a fixed duration relative to each participant's subscription. - CRON: fire all subscribed participants on a shared cron schedule. - THRESHOLD: fire after a participant counter satisfies a comparison.
    - `timezone` string: IANA timezone for schedule evaluation (defaults to UTC)
    - `trigger_at` string, date-time: When to fire (ONE_TIME trigger only, RFC 3339, must be in the future; omit to run now)
    - `type` 'CRON' | 'ONE_TIME' | 'PARTICIPANT_STATE', required: What starts the automation. - CRON: fires repeatedly on `cron_expression` in `timezone`. - ONE_TIME: fires once at `trigger_at`, or as soon as possible when `trigger_at` is omitted. - PARTICIPANT_STATE: keeps a subscription for each matching participant using `schedule_type` and `schedule_config`.

## Response `201`

Automation created

- AutomationResponse: An automation: an event that Scrip sends on a schedule, with its trigger, status and run history.
  - `created_at` string, date-time: When this automation was created
  - `description` string: Optional human-readable description of what this automation does
  - `error_message` string: Error message if the one-time automation failed
  - `event_data` object: Event data submitted when this automation fires
  - `execution_completed_at` string, date-time: When the current fan-out execution completed
  - `execution_error` string: Error message if the fan-out execution failed, or JSON-encoded diagnostics for completed runs with CEL eval skips
  - `execution_started_at` string, date-time: When the current fan-out execution started
  - `execution_status` 'IDLE' | 'EXECUTING' | 'COMPLETED' | 'FAILED': Progress of the current run that sends events to participants, separate from `status`. - IDLE: no run is in progress. - EXECUTING: Scrip is sending events to the matching participants. - COMPLETED: the latest run finished; the automation can run again when its trigger is next due. - FAILED: the latest run stopped with an error; the automation can still run again when its trigger is next due.
  - `filter_hints` AutomationFilterHint[]: Checks derived from `participant_filter` that narrow the participants to consider. Scrip still evaluates the full CEL filter for each candidate.
    - `type` 'HAS_TAG' | 'HAS_ATTRIBUTE' | 'HAS_COUNTER', required: Check to apply before evaluating `participant_filter`. - HAS_TAG: value is a participant tag. - HAS_ATTRIBUTE: value is an attribute key. - HAS_COUNTER: value is a counter key.
    - `value` string, required: Non-empty tag value, attribute key, or counter key interpreted according to type.
  - `guard_condition` string: CEL expression evaluated at trigger time; skips the participant if false
  - `id` string, uuid: Unique identifier for this automation
  - `last_error` string: Error message from the most recent cron execution, if any (cron trigger only)
  - `last_evaluated_at` string, date-time: When participant filters were last evaluated (PARTICIPANT_STATE trigger only)
  - `last_run_at` string, date-time: When this automation last fired (cron trigger only)
  - `name` string: Human-readable label for this automation
  - `next_run_at` string, date-time: When this automation will next fire (cron trigger only)
  - `participant_filter` string: CEL expression that determines which participants are enrolled
  - `participant_id` string, uuid: Target participant for program-scoped one-time automations
  - `participants_processed` integer: Participants Scrip has sent the event to in the current run so far.
  - `participants_skipped_error` integer: Participants skipped because participant_filter or guard_condition CEL evaluation errored
  - `participants_total` integer: Active participants in the program that the current run checks. When the automation has a `participant_filter` or `guard_condition`, `filter_hints` narrow this count; the filter and guard themselves are not applied, so a finished run can report fewer `participants_processed`.
  - `processed_at` string, date-time: When this one-time automation was processed
  - `program_id` string, uuid: The program this automation belongs to
  - `scope` 'PROGRAM' | 'PARTICIPANTS': Whether the automation sends one event to the program (PROGRAM) or one event to each matching participant (PARTICIPANTS).
  - `source` 'API' | 'RULE_ACTION' | 'SYSTEM': How the automation was created. - API: created through the automations API. - RULE_ACTION: created by a SCHEDULE_EVENT or BROADCAST rule action. - SYSTEM: created by Scrip for scheduled work such as tier expiration.
  - `source_event_id` string, uuid: The event whose processing created this automation. Omitted when no source event is recorded, for example on automations created through the API.
  - `source_rule_id` string, uuid: The rule whose action created this automation. Omitted when no rule action created it, including automations created through the API.
  - `status` 'ACTIVE' | 'PAUSED' | 'COMPLETED' | 'FAILED' | 'ARCHIVED': Whether the automation can fire. - ACTIVE: fires when its trigger is due. - PAUSED: does not fire until you set it back to ACTIVE. - COMPLETED: a one-time automation that has fired; it cannot fire again. - FAILED: stopped after a failure; set it to ACTIVE to try again. - ARCHIVED: removed from use; it never fires again.
  - `trigger` AutomationTrigger: What starts an automation: a cron schedule, a single time, or a participant-state schedule.
    - `cron_expression` string: Cron expression for recurring schedules (cron trigger only)
    - `schedule_config` union: Configuration selected by schedule_type for a PARTICIPANT_STATE trigger. The matching anyOf branch documents its required fields; unrecognized keys are ignored.
      - object: Use when schedule_type is ATTRIBUTE_DATE. Reads a date from each participant's attribute.
        - `attribute_key` string, required: Participant attribute containing the date value.
        - `date_format` 'MM-DD' | 'YYYY-MM-DD', required: Format stored in the participant attribute: MM-DD recurs yearly; YYYY-MM-DD fires for one calendar date.
        - `offset` string: Optional signed duration applied to the attribute date. Accepted units are ns, us or µs, ms, s, m, h, d, and w; units may be combined, while months and years are not accepted.
      - object: Use when schedule_type is INTERVAL. Each participant fires relative to their own subscription time.
        - `interval` string, required: Positive duration between firings. Accepted units are ns, us or µs, ms, s, m, h, d, and w; units may be combined, while months and years are not accepted.
      - object: Use when schedule_type is CRON. All subscribed participants share the same calendar schedule.
        - `expression` string, required: Five-field cron expression or descriptor such as @hourly or @daily; inline timezone directives are not accepted.
      - object: Use when schedule_type is THRESHOLD. Fires after a participant counter satisfies the comparison.
        - `counter_key` string, required: Participant counter to compare.
        - `delay` string, required: Non-negative duration to wait after the comparison matches before firing. Accepted units are ns, us or µs, ms, s, m, h, d, and w; units may be combined, while months and years are not accepted.
        - `operator` '>=' | '>' | '==' | '<=' | '<', required: Comparison applied between the participant counter and threshold.
        - `threshold` number, required: Numeric counter value used by operator for the comparison; explicit 0 is valid.
    - `schedule_type` 'ATTRIBUTE_DATE' | 'INTERVAL' | 'CRON' | 'THRESHOLD': Subscription schedule for a PARTICIPANT_STATE trigger. - ATTRIBUTE_DATE: derive each participant's trigger time from a date-valued attribute. - INTERVAL: fire at a fixed duration relative to each participant's subscription. - CRON: fire all subscribed participants on a shared cron schedule. - THRESHOLD: fire after a participant counter satisfies a comparison.
    - `timezone` string: IANA timezone for schedule evaluation (defaults to UTC)
    - `trigger_at` string, date-time: When to fire (ONE_TIME trigger only, RFC 3339, must be in the future; omit to run now)
    - `type` 'CRON' | 'ONE_TIME' | 'PARTICIPANT_STATE', required: What starts the automation. - CRON: fires repeatedly on `cron_expression` in `timezone`. - ONE_TIME: fires once at `trigger_at`, or as soon as possible when `trigger_at` is omitted. - PARTICIPANT_STATE: keeps a subscription for each matching participant using `schedule_type` and `schedule_config`.
  - `updated_at` string, date-time: When this automation was last updated
  - `warnings` RuleWarning[]: Advisories about `participant_filter` and `guard_condition`, such as a counter, tag or attribute key that no rule in the program writes. They appear on create and update only, and they never block the save.
    - `client_ref` string: The `client_ref` of the rule, when the rule is new.
    - `code` string: What the warning is about. On a rule: `unknown_state_key` (a key no rule writes), `deprecated_alias`, or `possible_unmarked_expression` (a literal that looks like CEL without `${{ }}`). On a simulation: `missing_participant_context`. On a configuration preview, also `stop_after_match_has_no_following_rule`, `unconditional_stop_shadows_following_rules`, or `later_rule_set_retained_empty`.
    - `key` string: The referenced key, as written in the condition.
    - `kind` string: Kind of reference: `counter`, `tag`, `attribute`, or `alias`.
    - `message` string: Human-readable explanation (includes the suggestion when one exists).
    - `rule_id` string: Rule identity is populated for complete-configuration validation. The single-rule authoring surfaces omit these fields.
    - `rule_set_id` string: The id of the rule set that holds the rule.
    - `scope` string: Where the referenced state belongs: `participant`, `program`, or `group`.
    - `suggestion` string: Closest known key, when a near-match exists. Empty otherwise.

## Other responses

- `400`: Validation failed (code: validation_error | invalid_json)
- `401`: The API key is missing or not valid. See [Authentication](https://docs.scrip.dev/authentication).
- `403`: The caller is not allowed to do this.
- `404`: Program not found (code: program_not_found)
- `409`: Name already exists in program (code: automation_name_already_exists)
- `429`: Too many requests. Wait the number of seconds in the `Retry-After` header, then retry. See [Limits and durations](https://docs.scrip.dev/limits#rate-limits).
- `500`: Something failed on Scrip's side. Retry the request, and quote `request_id` if it keeps failing. [Errors](https://docs.scrip.dev/errors) lists every code, and the [API introduction](https://docs.scrip.dev/api-reference/introduction) describes the error format.

---

[API](https://skmtc.dev/scrip/apis/scrip-api.md) · [All operations](https://skmtc.dev/scrip/apis/scrip-api/llms.txt) · [OpenAPI document](https://skmtc.dev/scrip/apis/scrip-api/revisions/c52d35da8d97?raw)
