# Create a trigger (/en/docs/api/native/automation/createTrigger)





Schedule recurring or one-time runs, or start runs from a webhook, for a published script or workflow. For one immediate run without a persistent trigger, use praxis.automation.runs.create. There may be at most 100 enabled triggers per tenant. Targets accept only exact versions or tags; webhook_url is returned only when creating a webhook or rotating its URL, so save it securely.

`POST /automation/v1/triggers`

## Authentication [#authentication]

`Authorization: Bearer $PRAXIS_API_KEY`

## Request fields [#request-fields]

| Parameter | Type | Description |
| --- | --- | --- |
| header: Idempotency-Key | `string` | optional; Optional; 1–256 characters. Within 24 hours, the same key replays the first response with Idempotent-Replayed: true; a different request body returns 422; an in-flight request returns 409. minLength: 1; maxLength: 256 |

### application/json [#applicationjson]

no additional fields.

| Field | Type | Description |
| --- | --- | --- |
| name | `string` | required; Trigger display name, 1–100 characters and not all whitespace; uniqueness is not required. minLength: 1; maxLength: 100 |
| description | `string` | optional; Description, at most 1,000 characters. maxLength: 1000 |
| labels | `object` | optional; At most 64 user labels; the praxis. prefix is reserved and internal labels do not count. Keys 1–40 characters, values at most 500. |
| target | `string` | required; ns/name@tag or ns/name@version, defaulting to latest when @ is omitted. Triggers do not accept version ranges. |
| input | `null` / `string` / `number` / `boolean` / `object` / `null` / `string` / `number` / `boolean` / `object` / `JSON[][]` | optional; Static JSON input for scheduled firings, default &#123;&#125;; checked against the target schema on create/update. Omitted webhook input is not validated at creation. |
| input (variant 5).* (variant 5) | `object` | Recursive JSON value. |
| input (variant 5).* (variant 6)[] | `null` / `string` / `number` / `boolean` / `object` / `JSON[]` | Recursive JSON value. |
| input (variant 6)[] (variant 5).* | `null` / `string` / `number` / `boolean` / `object` / `JSON[]` | Recursive JSON value. |
| input (variant 6)[] (variant 6)[] | `null` / `string` / `number` / `boolean` / `object` / `JSON[]` | Recursive JSON value. |
| kind | `"schedule"` / `"webhook"` | required; schedule fires on a schedule; webhook fires on an HTTP delivery. Immutable after creation. |
| schedule | `object` / `object` / `object` | optional; Choose one: a five-field cron (L/W/# supported), every as an ISO 8601 duration or duration object resolving to positive integer milliseconds from one minute to 366 days, or at as a one-time RFC 3339 timestamp within the next year. |
| schedule (variant 1).cron | `string` | required; Five fields; supports L, W, #. DST gaps skip the day and overlaps run once. |
| schedule (variant 2).every | `string` / `object` | required; A duration: an ISO 8601 string such as PT5M, or an object with days, hours, minutes, seconds and milliseconds. It must convert to a whole number of milliseconds between 60000 and 31622400000 (1 minute to 366 days). |
| schedule (variant 2).every (variant 2).days | `integer` | optional; Number of days, a non-negative integer. minimum: 0 |
| schedule (variant 2).every (variant 2).hours | `integer` | optional; Number of hours, a non-negative integer. minimum: 0 |
| schedule (variant 2).every (variant 2).minutes | `integer` | optional; Number of minutes, a non-negative integer. minimum: 0 |
| schedule (variant 2).every (variant 2).seconds | `integer` | optional; Number of seconds, a non-negative integer. minimum: 0 |
| schedule (variant 2).every (variant 2).milliseconds | `integer` | optional; Number of milliseconds, a non-negative integer. minimum: 0 |
| schedule (variant 3).at | `string` | required; One-time firing instant with a Z or ±HH:MM timezone, between the request time and one calendar year later. Replace the example with a future instant in that range. pattern: "(Z\|[+-]\\d\\d:\\d\\d)$"; format: "date-time" |
| timezone | `string` | optional; IANA timezone, default UTC. DST gaps are skipped and repeated local times fire once. default: "UTC" |
| start_at | `string` / `null` | optional; Optional RFC 3339 start time with timezone; null means no lower bound. |
| end_at | `string` / `null` | optional; Optional RFC 3339 end time with timezone; cannot precede start_at. null means no upper bound. |
| overlap | `"skip"` / `"allow"` | optional; skip (default) skips when a previous run is still active; allow permits overlapping runs. default: "skip" |
| catchup_window_seconds | `integer` | optional; Nonnegative integer seconds, default 300. Only scheduled firings delayed beyond the window are recorded as missed; webhooks ignore this window. default: 300; minimum: 0 |
| webhook | `object` | optional; input_mode is body (default) or envelope. The latter contains body, raw_body, raw_body_encoding, allowlisted headers, webhook_id and received_at; full URLs are returned only on creation or rotation. no additional fields. |
| webhook.input_mode | `"body"` / `"envelope"` | required; Shape of the input a webhook delivery gives the target. body (default) passes only the request body; envelope passes an object with body, raw_body, raw_body_encoding, headers, webhook_id and received_at. default: "body" |

## Examples [#examples]

[SDK installation and example environment](/en/docs/api/native/automation)

Set these environment variables to your own resources:

| Variable | Source |
| --- | --- |
| TARGET | [Published docs-samples/name@1.0.0](/en/docs/api/native/automation/publishScriptVersion) |

```bash
: "${TARGET:?Set TARGET}"
BODY=$(jq -n --arg TARGET "$TARGET" '{
  "name": "sample-webhook",
  "target": $TARGET,
  "kind": "webhook",
  "labels": {
    "namespace": "docs-samples"
  },
  "webhook": {
    "input_mode": "body"
  }
}')
curl --fail-with-body -X POST "https://api.deepraxis.ai/automation/v1/triggers" \
  -H "Authorization: Bearer $PRAXIS_API_KEY" \
  -H "Content-Type: application/json" \
  --data "$BODY"
```

```typescript
import { createAutomationClient } from '@deepraxis/sdk/automation';

function env(name: string): string {
  const value = process.env[name];
  if (!value) throw new Error(`Set ${name}`);
  return value;
}
const automation = createAutomationClient({
  apiKey: env('PRAXIS_API_KEY'),
  baseUrl: 'https://api.deepraxis.ai',
  maxRetries: 0,
  timeoutMs: 90_000,
});
const result = await automation.triggers.create({
  "body": {
    "name": "sample-webhook",
    "target": env("TARGET"),
    "kind": "webhook",
    "labels": {
      "namespace": "docs-samples"
    },
    "webhook": {
      "input_mode": "body"
    }
  }
});
console.log(result);
```

## Response [#response]

### HTTP 200 [#http-200]

Success

| Header | Type | Description |
| --- | --- | --- |
| ETag | `string` | Current resource revision for conditional updates. |
| Idempotent-Replayed | `boolean` | Present and true when replaying the first response for this key. |

`application/json`

| Field | Type | Description |
| --- | --- | --- |
| name | `string` | required; Trigger name, 1–100 characters. minLength: 1; maxLength: 100 |
| description | `string` | required; Trigger description, at most 1000 characters. maxLength: 1000 |
| labels | `object` | required; Trigger tags as string key and value pairs, at most 64; keys must not start with the reserved prefix praxis. (400 INPUT_INVALID). |
| target | `string` | required; Script or workflow to run, as ns/name@tag-or-version; it follows latest by default, and @1.2.0 pins a version. |
| input | `null` / `string` / `number` / `boolean` / `object` / `JSON[]` | required; Any JSON value: null, boolean, number, string, array or object. |
| input (variant 5).* | `null` / `string` / `number` / `boolean` / `object` / `JSON[]` | Recursive JSON value. |
| input (variant 6)[] | `null` / `string` / `number` / `boolean` / `object` / `JSON[]` | Recursive JSON value. |
| kind | `"schedule"` / `"webhook"` | required; Trigger kind. schedule fires on a plan (cron, every or at); webhook fires on an inbound HTTP request. |
| schedule | `object` / `object` / `object` / `null` | required; Schedule rule for kind=schedule, exactly one of cron, every or at; null for webhooks. |
| schedule (variant 1) (variant 1).cron | `string` | required; Five fields; supports L, W, #. DST gaps skip the day and overlaps run once. |
| schedule (variant 1) (variant 2).every | `string` / `object` | required; A duration: an ISO 8601 string such as PT5M, or an object with days, hours, minutes, seconds and milliseconds. It must convert to a whole number of milliseconds between 60000 and 31622400000 (1 minute to 366 days). |
| schedule (variant 1) (variant 2).every (variant 2).days | `integer` | optional; Number of days, a non-negative integer. minimum: 0 |
| schedule (variant 1) (variant 2).every (variant 2).hours | `integer` | optional; Number of hours, a non-negative integer. minimum: 0 |
| schedule (variant 1) (variant 2).every (variant 2).minutes | `integer` | optional; Number of minutes, a non-negative integer. minimum: 0 |
| schedule (variant 1) (variant 2).every (variant 2).seconds | `integer` | optional; Number of seconds, a non-negative integer. minimum: 0 |
| schedule (variant 1) (variant 2).every (variant 2).milliseconds | `integer` | optional; Number of milliseconds, a non-negative integer. minimum: 0 |
| schedule (variant 1) (variant 3).at | `string` | required; Instant of a one-time firing (RFC 3339 UTC); it cannot be in the past or more than one year ahead. The trigger becomes expired after it runs. format: "date-time" |
| timezone | `string` | required; IANA time zone name used to interpret the schedule; UTC by default through the API. default: "UTC" |
| start_at | `string` / `null` | required; Start time of a scheduled trigger (RFC 3339 UTC); it does not fire before then. null for no limit. |
| end_at | `string` / `null` | required; End time of a scheduled trigger (RFC 3339 UTC); after it the trigger becomes expired. null for no end. |
| overlap | `"skip"` / `"allow"` | required; Overlap policy when a firing is due while the previous run is still active: skip (default) skips it, allow starts it anyway. default: "skip" |
| catchup_window_seconds | `integer` | required; Catch-up window in seconds, default 300, for scheduled triggers only: if a firing is later than this (downtime, pause) it is recorded as missed and not run. default: 300; minimum: 0 |
| webhook | `object` / `null` | required; Webhook settings for kind=webhook; null for scheduled triggers. |
| webhook (variant 1).input_mode | `"body"` / `"envelope"` | required; Shape of the input a webhook delivery gives the target. body (default) passes only the request body; envelope passes an object with body, raw_body, raw_body_encoding, headers, webhook_id and received_at. |
| webhook (variant 1).url_hint | `string` | required; Masked webhook address with an ellipsis replacing the secret token; it cannot be invoked. Newly created triggers use a full public URL; historical records may contain a relative /automation/v1/hooks/&#123;id&#125;/… path. |
| id | `string` | required; Trigger id, prefixed trg_; used in the webhook address and by every trigger endpoint. |
| owner | `string` | required; Tenant that owns the trigger, injected by the gateway; treat it as an opaque string. |
| state | `"enabled"` / `"paused"` / `"expired"` | required; Trigger state. enabled fires on schedule or request; paused does not fire; expired is final, reached after a one-time trigger has run or end_at has passed, and no longer fires. |
| source | `"api"` / `"definition"` | required; Origin of the trigger. api was created through the API and can be changed and deleted; definition was generated from the schedule in a workflow YAML and can only be paused or resumed, changes return 409 TRIGGER_MANAGED. |
| created_by | `object` | required; Principal &#123;type, id&#125; injected by the gateway that says who initiated or acted. This service does not resolve names; the console resolves them in batches. no additional fields. |
| created_by.type | `"user"` / `"api_key"` / `"agent"` / `"run"` / `"trigger"` | required; Principal type. user is a signed-in user; api_key is a platform API key; agent is an agent; run is an action performed by a run itself (for example a parent run); trigger is a trigger. |
| created_by.id | `string` | required; Principal identifier whose meaning follows type: a user, API key, agent, run or trigger id. Treat it as an opaque string. |
| created_by_user_id | `string` | required; Real user id of the creator, forwarded by the gateway; runs the trigger starts take their token from this user, for audit and usage attribution. |
| next_fire_times | `string[]` | required; Up to the next 5 firing times (RFC 3339 UTC); empty for webhook, paused or expired triggers. maxItems: 5 |
| last_firing | `object` / `null` | required; The most recent firing record, or null if it has never fired. |
| last_firing (variant 1).id | `string` | required; Firing record id; records are kept for 30 days. |
| last_firing (variant 1).trigger_id | `string` | required; Id (trg_) of the trigger this firing belongs to. |
| last_firing (variant 1).scheduled_at | `string` | required; Planned firing time (RFC 3339 UTC); for webhooks, the time the request arrived. format: "date-time" |
| last_firing (variant 1).fired_at | `string` | required; Time the firing actually happened (RFC 3339 UTC). format: "date-time" |
| last_firing (variant 1).outcome | `"accepted"` / `"started"` / `"skipped_overlap"` / `"missed"` / `"failed"` / `"deduped"` | required; Firing outcome. accepted means persisted and awaiting processing; started means a run was created; skipped_overlap means skipped because the previous run was still active; missed means it was later than the catch-up window and was not run; failed means creating the run failed; deduped means it repeated a webhook-id seen within 24 hours. |
| last_firing (variant 1).run_id | `string` / `null` | required; Id (run_) of the run this firing created, or null if no run was created. |
| last_firing (variant 1).resolved_version | `string` / `null` | required; Exact version this firing resolved to; null when not yet resolved or when it failed before resolving. |
| last_firing (variant 1).error | `object` / `null` | required; Reason &#123;code, message&#125; when the firing failed; null otherwise. |
| last_firing (variant 1).error (variant 1).code | `string` | required; Error code of the failure. |
| last_firing (variant 1).error (variant 1).message | `string` | required; Message describing the failure. |
| counts | `object` | required; Cumulative firing counts &#123;started, skipped, missed, failed&#125;. no additional fields. |
| counts.started | `integer` | required; Number of firings that started a run. minimum: 0 |
| counts.skipped | `integer` | required; Number of firings skipped because overlap is skip and the previous run was still active. minimum: 0 |
| counts.missed | `integer` | required; Number of firings missed because they were later than the catch-up window. minimum: 0 |
| counts.failed | `integer` | required; Number of firings where creating the run failed. minimum: 0 |
| created_at | `string` | required; Creation time (RFC 3339 UTC). format: "date-time" |
| updated_at | `string` | required; Time of the last update (RFC 3339 UTC). format: "date-time" |
| etag | `string` | required; Current revision tag of the trigger; send it as If-Match when updating, and a mismatch returns 412. |
| usage_30d | `object` / `null` | optional; Always null in a create response; the 30-day usage is only returned by GET /triggers/&#123;id&#125;. |
| usage_30d (variant 1).currency | `"USD"` | required; Charge currency, always USD. |
| usage_30d (variant 1).cost | `string` | required; Rated actual charges as an exact dollar decimal string with six fractional digits; 0.000000 when there is no usage. pattern: "^[0-9]+\\.[0-9]&#123;6&#125;$" |
| usage_30d (variant 1).pending_events | `integer` | required; Number of unrated usage events whose charges will be included later. minimum: 0 |
| webhook_url | `string` | optional; Returned only on webhook creation or rotation. format: "uri" |

### Measured response [#measured-response]

HTTP 200

```json
{
  "id": "trg_00mv67mzzqa0d63b98d72446dd9d1331051a3e100e",
  "owner": "tnt_3a9759864ccb4e00",
  "name": "sample-webhook-1791867412840-b05e8a",
  "description": "",
  "labels": {
    "namespace": "docs-samples"
  },
  "target": "docs-samples/sample-scripts-1791867412840-b05e8a@1.0.0",
  "input": {},
  "kind": "webhook",
  "schedule": null,
  "timezone": "UTC",
  "start_at": null,
  "end_at": null,
  "overlap": "skip",
  "catchup_window_seconds": 300,
  "webhook": {
    "input_mode": "body",
    "url_hint": "https://api-develop.deepraxis.ai/automation/v1/hooks/trg_00mv67mzzqa0d63b98d72446dd9d1331051a3e100e/…"
  },
  "state": "enabled",
  "source": "api",
  "created_by": {
    "type": "api_key",
    "id": "key_3e00b8984967"
  },
  "created_by_user_id": "usr_49d4fcb99517",
  "next_fire_times": [],
  "last_firing": null,
  "counts": {
    "started": 0,
    "skipped": 0,
    "missed": 0,
    "failed": 0
  },
  "created_at": "2026-10-13T04:58:09.974Z",
  "updated_at": "2026-10-13T04:58:09.974Z",
  "etag": "etag_00mv67mzzqc6ef5487c69f4857b65b91dab497104c",
  "webhook_url": "https://api-develop.deepraxis.ai/automation/v1/hooks/trg_00mv67mzzqa0d63b98d72446dd9d1331051a3e100e/${WEBHOOK_TOKEN}"
}
```

## Errors [#errors]

| HTTP | Codes and description |
| --- | --- |
| 400 | INVALID_PARAMS: Invalid request parameters, path, cursor, or body format. SCHEDULE_INVALID: The schedule or time boundaries are invalid. TIMEZONE_INVALID: The timezone is not a valid IANA timezone. INTERVAL_TOO_SHORT: The schedule interval is shorter than one minute. INPUT_INVALID: The input does not satisfy the target contract or validation rules. |
| 404 | TARGET_NOT_FOUND: The target does not exist or has an incompatible type. VERSION_NOT_FOUND: The requested version or revision does not exist. |
| 409 | IDEMPOTENCY_IN_FLIGHT: A request with the same idempotency key is still in progress. TARGET_ARCHIVED: The target is archived and cannot be used for this operation; action execution error detail includes ns/name. |
| 412 | PRECONDITION_FAILED: The revision, source digest, or concurrent write precondition does not match. |
| 422 | IDEMPOTENCY_KEY_MISMATCH: The idempotency key was already used for different request content. NO_MATCHING_VERSION: No published version matches the requested tag, version, or range. |
| 429 | TRIGGER_LIMIT: The tenant has reached the limit of 100 enabled triggers. |
| default | RFC 9457 automation problem. 429 includes Retry-After. |

[Authentication and common errors](/en/docs/api/errors) · Retain the `X-Request-Id` response header for troubleshooting.
