Create a trigger
Schedule recurring or one-time runs, or start runs from a webhook, for a published script or workflow.
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
Authorization: Bearer $PRAXIS_API_KEY
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
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 {}; 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
Set these environment variables to your own resources:
| Variable | Source |
|---|---|
| TARGET | Published docs-samples/name@1.0.0 |
: "${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"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
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/{id}/… 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 {type, id} 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 {code, message} 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 {started, skipped, missed, failed}. 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/{id}. |
| 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]{6}$" |
| 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
HTTP 200
{
"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
| 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 · Retain the X-Request-Id response header for troubleshooting.