# 触发器 (/docs/cloud/services/automation/guides/triggers)



触发器把“何时执行”与脚本、工作流的内容分开。

执行本页前，先在同一个 Bash 会话完成快速开始第 1–3 步：[设置 `PRAXIS_API_URL` 与 `PRAXIS_API_KEY`](/docs/cloud/services/automation/quickstart#environment)、[定义 `SOURCE` 并临时运行](/docs/cloud/services/automation/quickstart#run-source)、[定义并调用 `read_result` 辅助函数](/docs/cloud/services/automation/quickstart#read-result)。该函数检查结果是否为 42，与本页的加一示例对应。再完成第 4–5 步：[保存并发布脚本](/docs/cloud/services/automation/quickstart#publish-script)、[设置 `TARGET`](/docs/cloud/services/automation/quickstart#run-version)。`TARGET` 必须指向这个接收 `n`、返回 `answer` 的已发布脚本。

## 定时执行 [#定时执行]

每个 schedule 只能选择一种规则：

| 规则                  | 用途                                     |
| ------------------- | -------------------------------------- |
| `cron: "0 9 * * *"` | 每天当地时间 09:00；五段，分钟精度，支持 L、W、#。         |
| `every: "PT5M"`     | 每隔五分钟；最短一分钟，按经过的时长计算。                  |
| `at`                | RFC 3339 绝对时间，必须带时区偏移或 Z；不能早于现在或晚于一年后。 |

`timezone` 使用 IANA 名称，API 默认 UTC。当地时间 cron 在夏令时春季跳过不存在的时刻，秋季重复的时刻只运行一次。`every` 按时长、`at` 按绝对时间执行，不使用这条 cron 夏令时规则。

先[预览下次时间](/docs/api/native/automation/previewTriggerSchedule)：

```bash
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)'
```

期望：输出五个未来时间，例如：

```json
{
  "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"
  ]
}
```

日期随预览时间变化。末尾的 `Z` 表示 UTC。例如 `2026-10-13T01:00:00.000Z` 换算为**上海时间（UTC+8）10 月 13 日 09:00**。`timezone` 决定 cron 按哪个当地时区计算，返回的时间统一用 UTC 表达。

再[创建触发器](/docs/api/native/automation/createTrigger)，并立即触发一次检查目标：

```bash
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")"
```

期望：三次断言输出 `true`。`started` 只表示启动成功；`read_result` 用返回的 `run_id` [读取运行](/docs/api/native/automation/getRun)，确认终态成功且结果为 42。

`target` 写 `@1.0.0` 固定版本，写 `@latest` 跟随标签；每条触发记录保存实际版本。创建和修改时检查静态输入，触发时再次检查，避免新版本不再接受原输入。

## 重叠、延迟与有效时间 [#重叠延迟与有效时间]

默认 `overlap: "skip"`：上一次运行未结束时，本次记录为 `skipped_overlap`；`allow` 允许重叠启动，仍受运行并发限制。没有排队补齐、取消旧运行或历史 backfill 策略。

`catchup_window_seconds` 默认 300。调度延迟超过窗口记为 `missed`，不会启动；这不是恢复后重放所有错过任务的承诺。暂停期间不执行，恢复后继续未来调度。

用 `start_at` 与 `end_at` 设置有效时间范围（RFC 3339）。一次性 `at` 执行后、或达到结束时间后变为 `expired`。连续 5 次触发失败会自动暂停。通过[暂停](/docs/api/native/automation/pauseTrigger)和[恢复](/docs/api/native/automation/resumeTrigger)控制后续触发。

暂停上面创建的触发器，再恢复：

```bash
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)'
```

期望：两次请求都输出 `true`；恢复后返回这个每日计划的下五次执行时间。

工作流 YAML 中的 cron/every schedule 在发布为 latest 时管理一个 `source=definition` 触发器；修改要重新发布定义，不能直接 PATCH 这个触发器。可以暂停或恢复它。`schedule.after` 与 `schedule.on` 暂不支持。

## Webhook [#webhook]

创建时选 `input_mode: "body"`，收到的 JSON 就是运行输入；非 JSON 请求体作为字符串。以下示例把 `n:41` 传给快速开始的脚本：

```bash
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_")'
```

期望：HTTP 202，断言输出 `true`，响应含触发记录 ID。异步启动结果需要再查触发历史。

完整 URL 只在创建或[轮换](/docs/api/native/automation/rotateTriggerWebhook)时返回；其中的 token 是凭据，无需 API Key，不要公开。旧 URL 在轮换后失效。错误 token、暂停或不存在的触发器都返回 404。请求体最多 1 MiB；相同 `webhook-id` 在 24 小时内去重。

## 签名头与 envelope [#签名头与-envelope]

当前 webhook 入口验证 secret URL，不自动验证 Standard Webhooks、GitHub 或 Stripe 的 HMAC 签名。传入 `webhook-signature` 不代表它已经通过验签。

若目标需要读取头部，创建时使用 `webhook: {input_mode:"envelope"}`，并让目标的输入 schema 接受下列形状（这不是快速开始的 `n` 输入）：

```json
{
  "body": { "n": 41 },
  "headers": { "content-type": "application/json", "webhook-id": "delivery-1" },
  "webhook_id": "delivery-1",
  "received_at": "2026-10-11T12:00:00.000Z"
}
```

`webhook_id` 未提供时为 null。只保留以下头：`content-type`、`user-agent`、`webhook-id`、`webhook-timestamp`、`webhook-signature`、`x-github-event`、`x-github-delivery`、`x-hub-signature-256`、`stripe-signature`；Authorization、Cookie 不会转交。

JSON body 会先被解析，envelope 不包含独立的原始请求字节。对于必须签署原始字节的协议，不能用 `JSON.stringify(body)` 代替原文验签。需要这种验证时，在你接收原始请求的服务中按发送方协议先验证，再转发到 secret URL。当前没有可直接套用到此 JSON envelope 的通用验签示例。

## 查看触发记录与清理 [#查看触发记录与清理]

```bash
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}'
```

期望：第一个请求输出 `true`，第二个列出记录。若异步处理尚未完成，稍后重新读取。`started` 表示已启动，`failed` 看 error，`missed` 表示超过窗口，`skipped_overlap` 表示前次未结束，`deduped` 表示命中去重。按 `run_id` 继续查看运行终态，记录保留 30 天。

测试完成后删除本页创建的两个触发器，避免继续定时执行：

```bash
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"
done
```

期望：curl 均退出 0。删除不影响已经启动的运行。

[API 参考 →](/docs/api/native/automation) · [Cookbook →](/docs/cookbook)
