Triggers
Start published scripts or workflows on a schedule or webhook.
Triggers separate when work runs from its definition.
Before running this page, complete quickstart steps 1–3 in the same Bash session: set PRAXIS_API_URL and PRAXIS_API_KEY, define SOURCE and run it, then define and run read_result. The helper checks for answer 42, as expected by the arithmetic examples here. Also save and publish the script and set TARGET in steps 4–5. TARGET must reference that published script, which accepts n and returns answer.
Scheduled execution
Choose exactly one schedule form:
| Form | Use |
|---|---|
cron: "0 9 * * *" | Daily at 09:00 local time; five fields, minute precision, with L, W and # support. |
every: "PT5M" | Every five elapsed minutes; the minimum is one minute. |
at | An absolute RFC 3339 timestamp with offset or Z, between now and one year from now. |
timezone accepts an IANA name and defaults to UTC. For local cron schedules, nonexistent spring DST times are skipped and repeated autumn times execute once. every uses elapsed time and at uses an absolute instant; neither uses the cron DST rule.
curl --fail-with-body -sS "$PRAXIS_API_URL/automation/v1/triggers/preview" \
-H "Authorization: Bearer $PRAXIS_API_KEY" \
-H 'Content-Type: application/json' \
--data '{"schedule":{"cron":"0 9 * * *"},"timezone":"Asia/Shanghai"}' \
| jq -e '{next_fire_times} | select(.next_fire_times | length == 5)'Expected: five future timestamps, for example:
{
"next_fire_times": [
"2026-10-13T01:00:00.000Z",
"2026-10-14T01:00:00.000Z",
"2026-10-15T01:00:00.000Z",
"2026-10-16T01:00:00.000Z",
"2026-10-17T01:00:00.000Z"
]
}Dates depend on when you preview. Z means UTC. For example, 2026-10-13T01:00:00.000Z is October 13 at 09:00 in Shanghai (UTC+8). The timezone controls the cron schedule; returned timestamps are always expressed in UTC.
Create the trigger, then fire it once to check the target:
TRIGGER=$(curl --fail-with-body -sS "$PRAXIS_API_URL/automation/v1/triggers" \
-H "Authorization: Bearer $PRAXIS_API_KEY" \
-H 'Content-Type: application/json' \
--data "$(jq -n --arg target "$TARGET" '{
name:"daily-hello",target:$target,kind:"schedule",input:{n:41},
schedule:{cron:"0 9 * * *"},timezone:"Asia/Shanghai",
overlap:"skip",catchup_window_seconds:300
}')")
SCHEDULE_ID=$(jq -er '.id' <<< "$TRIGGER")
jq -e '.state == "enabled" and (.next_fire_times | length == 5)' <<< "$TRIGGER"
FIRING=$(curl --fail-with-body -sS -X POST \
"$PRAXIS_API_URL/automation/v1/triggers/$SCHEDULE_ID/fire" \
-H "Authorization: Bearer $PRAXIS_API_KEY" \
-H 'Content-Type: application/json' --data '{}')
jq -e '.outcome == "started"' <<< "$FIRING"
read_result "$(jq -er '.run_id' <<< "$FIRING")"Expected: all three assertions print true. started confirms launch; read_result uses the returned run_id to retrieve the run, check completion and confirm answer 42.
Pin target with @1.0.0 or follow a tag with @latest. Every firing records the resolved version. Static input is validated on creation and update, then checked again when firing in case the target's schema changed.
Overlap, delays and active windows
The default overlap: "skip" records skipped_overlap if the preceding run is unfinished. allow starts overlapping runs subject to concurrency limits. Buffering, cancelling previous runs and historical backfill are not available.
catchup_window_seconds defaults to 300. A scheduling delay beyond this window produces missed without starting a run. This does not promise to replay every occurrence after resuming. Paused triggers do not execute; resuming continues future scheduling.
Set RFC 3339 start_at and end_at timestamps to bound the active window. A one-shot at expires after firing; reaching the end time also makes the trigger expired. 5 consecutive firing failures pause it automatically. Use pause and resume to control future firings.
Pause the trigger created above, then resume it:
curl --fail-with-body -sS -X POST \
"$PRAXIS_API_URL/automation/v1/triggers/$SCHEDULE_ID/pause" \
-H "Authorization: Bearer $PRAXIS_API_KEY" \
-H 'Content-Type: application/json' --data '{}' \
| jq -e '.state == "paused" and (.next_fire_times | length == 0)'
curl --fail-with-body -sS -X POST \
"$PRAXIS_API_URL/automation/v1/triggers/$SCHEDULE_ID/resume" \
-H "Authorization: Bearer $PRAXIS_API_KEY" \
-H 'Content-Type: application/json' --data '{}' \
| jq -e '.state == "enabled" and (.next_fire_times | length == 5)'Expected: both requests print true. Resuming this daily schedule returns its next five occurrences.
A workflow YAML cron/every schedule manages a source=definition trigger when published as latest. Change it by republishing the definition, not by PATCHing the trigger. You can pause or resume it. schedule.after and schedule.on are not supported yet.
Webhooks
With input_mode: "body", incoming JSON becomes run input; non-JSON bodies become strings. This sends n:41 to the quickstart script:
HOOK=$(curl --fail-with-body -sS "$PRAXIS_API_URL/automation/v1/triggers" \
-H "Authorization: Bearer $PRAXIS_API_KEY" \
-H 'Content-Type: application/json' \
--data "$(jq -n --arg target "$TARGET" '{
name:"webhook-hello",target:$target,kind:"webhook",webhook:{input_mode:"body"}
}')")
WEBHOOK_ID=$(jq -er '.id' <<< "$HOOK")
WEBHOOK_URL=$(jq -er '.webhook_url' <<< "$HOOK")
curl --fail-with-body -sS "$WEBHOOK_URL" \
-H 'Content-Type: application/json' \
-H "webhook-id: hello-$(date +%s)-$RANDOM" \
--data '{"n":41}' | jq -e '.id | startswith("frg_")'Expected: HTTP 202 and true, with a firing ID in the response. Check firing history for the asynchronous launch result.
The full URL is returned only on creation or rotation. Its token is the credential: no API Key is needed, and the URL must stay private. Rotation invalidates the old URL. Invalid tokens, paused triggers and nonexistent triggers return 404. Bodies are limited to 1 MiB. The same webhook-id is deduplicated for 24 hours.
Signature headers and envelopes
The endpoint authenticates the secret URL. It does not automatically verify Standard Webhooks, GitHub or Stripe HMAC signatures. Supplying webhook-signature does not mean it was verified.
To read headers, create the trigger with webhook: {input_mode:"envelope"}. The target schema must accept the following shape rather than the quickstart's n input:
{
"body": { "n": 41 },
"headers": { "content-type": "application/json", "webhook-id": "delivery-1" },
"webhook_id": "delivery-1",
"received_at": "2026-10-11T12:00:00.000Z"
}An absent webhook_id becomes null. Only these headers are forwarded: content-type, user-agent, webhook-id, webhook-timestamp, webhook-signature, x-github-event, x-github-delivery, x-hub-signature-256 and stripe-signature. Authorization and Cookie are excluded.
JSON bodies are parsed before delivery; the envelope has no separate original-byte field. For protocols that sign raw bytes, JSON.stringify(body) cannot substitute for the original payload. Verify such signatures in your receiving service before forwarding to the secret URL. There is currently no generic verification example that can safely be applied directly to this JSON envelope.
Inspect firings and clean up
curl --fail-with-body -sS \
"$PRAXIS_API_URL/automation/v1/triggers/$WEBHOOK_ID/firings" \
-H "Authorization: Bearer $PRAXIS_API_KEY" \
| jq -e '.data | length > 0'
curl --fail-with-body -sS \
"$PRAXIS_API_URL/automation/v1/triggers/$WEBHOOK_ID/firings" \
-H "Authorization: Bearer $PRAXIS_API_KEY" \
| jq '.data[] | {outcome,run_id,resolved_version,error}'Expected: the first request prints true; the second lists records. Read again shortly if asynchronous processing is still underway. started means launched; inspect error for failed. missed exceeds the catchup window, skipped_overlap has an unfinished prior run, and deduped matched a prior delivery. Follow run_id to inspect terminal execution status. Firing records are retained for 30 days.
Delete both tutorial triggers when finished to stop future scheduled execution:
for id in "$SCHEDULE_ID" "$WEBHOOK_ID"; do
curl --fail-with-body -sS -X DELETE \
"$PRAXIS_API_URL/automation/v1/triggers/$id" \
-H "Authorization: Bearer $PRAXIS_API_KEY"
doneExpected: both curl requests exit 0. Deleting a trigger does not affect runs already started.