Activepieces 0.92.3 功能验证手册
Activepieces 0.92.3 功能验证手册:注册与令牌、连接器同步、工作流导入发布、Webhook/定时触发、Code/HTTP/循环/路由分支、全局变量、连接器凭据与异常路径的 REST API 实测验收
Activepieces 0.92.3 功能验证手册
目标:对已部署的 Activepieces 0.92.3 社区版做一次端到端功能验收:账号注册与令牌、连接器元数据同步、工作流创建/导入/发布、Webhook 与定时两类触发、Code/HTTP/循环/路由分支等动作组件、全局变量、连接器凭据、运行历史与错误呈现,全部通过 REST API 实测并与预期逐项比对。本文全部命令与回显均在 Ubuntu 22.04 实测通过(实测日期:2026-10-18)。
一、环境信息
| 项目 | 值 |
|---|---|
| 服务地址 | http://192.168.146.128:8100(Ubuntu 22.04 服务器) |
| 平台版本 | Activepieces 0.92.3 社区版(API + Worker 分离形态) |
| 管理账号 | admin@example.com / q1w2e3r4 |
| 管理项目 ID | ptCNUnkh0eHoNtIFJWA2C |
| 验证方式 | REST API(服务端内置,前缀 /api/v1,认证头 Authorization: Bearer <token>) |
| 外部依赖 | https://httpbin.org(HTTP 步骤回显目标,出口可达) |
REST API 的完整端点定义可在服务自带的 OpenAPI 规范中查询(官方文档站 API Reference,共 67 个端点),本手册按验证路径选用其中认证、flows、webhooks、flow-runs、pieces、variables、app-connections 七组。
二、账号注册与 API 令牌
2.1 注册首个管理员
全新部署后没有任何用户,第一个注册的账号自动成为平台管理员(platformRole=ADMIN):
curl -s -m 15 -X POST http://192.168.146.128:8100/api/v1/authentication/sign-up \
-H 'Content-Type: application/json' \
-d '{"email":"admin@example.com","password":"q1w2e3r4","firstName":"Admin","lastName":"User","trackEvents":false,"newsLetter":false}' \
-w '\nHTTP_STATUS:%{http_code}\n' | python3 -c "
import sys,json
raw=sys.stdin.read()
body,_,tail=raw.rpartition('HTTP_STATUS:')
d=json.loads(body)
print('注册邮箱:', d.get('email'))
print('平台角色:', d.get('platformRole'))
print('自动创建项目 id:', d.get('projectId'))
print('HTTP_STATUS:'+tail)
"
实测回显:
注册邮箱: admin@example.com
平台角色: ADMIN
自动创建项目 id: ptCNUnkh0eHoNtIFJWA2C
HTTP_STATUS:200
预期比对:注册接口 200、返回平台角色 ADMIN、自动创建首个项目,符合"首个用户即管理员"的设计。注册接口只对空库开放(已有用户时返回 409 或权限错误),这是需要重点确认的安全特性。
2.2 登录获取会话令牌
curl -s -m 15 -X POST http://192.168.146.128:8100/api/v1/authentication/sign-in \
-H 'Content-Type: application/json' \
-d '{"email":"admin@example.com","password":"q1w2e3r4"}' | python3 -c "
import sys,json
d=json.load(sys.stdin)
open('/tmp/ap-token.txt','w').write(d['token'])
print('token 前缀:', d['token'][:30]+'...')
print('用户:', d.get('email'), '| 角色:', d.get('platformRole'))
"
实测回显:
token 前缀: eyJhbGciOiJIUzI1NiIsInR5c...
用户: admin@example.com | 角色: ADMIN
预期比对:返回 JWT 会话令牌,后续所有 API 请求以 Authorization: Bearer <token> 认证(下文示例统一用 $TOKEN 指代)。社区版(CE)平台层鉴权只有 JWT 会话一种方式;管理页面的 sk- 平台 API Key 属于商业版能力,CE 环境直接用 sk- 密钥调 API 会得到 401(实测验证),按需购买商业许可即可。
三、连接器元数据同步
Activepieces 的连接器(piece)元数据由服务端从官方 registry 同步后缓存在本地数据库,画布上能用哪些连接器取决于同步结果。全新部署后第一次查询可能为空:
curl -s -m 30 "http://192.168.146.128:8100/api/v1/pieces" \
-H "Authorization: Bearer $TOKEN" | python3 -c "import sys,json;print('pieces:',len(json.load(sys.stdin)))"
实测回显(首次):
pieces: 0
手动触发一次全量同步:
curl -s -m 300 -X POST "http://192.168.146.128:8100/api/v1/pieces/sync" \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d '{}' \
-w '\nHTTP:%{http_code}\n'
实测回显:
HTTP:200
同步在后台分批进行,轮询确认直至目标连接器就位:
curl -s -m 30 "http://192.168.146.128:8100/api/v1/pieces" \
-H "Authorization: Bearer $TOKEN" | python3 -c "
import sys,json
d=json.load(sys.stdin)
print('pieces 总数:', len(d))
names={p['name']:p['version'] for p in d}
for n in ('@activepieces/piece-webhook','@activepieces/piece-http','@activepieces/piece-schedule'):
print(n, '->', names.get(n))
"
实测回显:
pieces 总数: 769
@activepieces/piece-webhook -> 0.1.42
@activepieces/piece-http -> 0.13.0
@activepieces/piece-schedule -> 0.1.22
预期比对:同步完成后 pieces 接口可查 769 条连接器元数据,本文后续步骤依赖的 webhook/http/schedule 三个核心连接器全部就位且版本明确。若长时间停在 0,通常是服务器访问 npm registry 不稳,见部署篇常见问题的代理方案。
四、工作流创建与发布
4.1 创建空工作流
curl -s -m 15 -X POST http://192.168.146.128:8100/api/v1/flows \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"displayName":"webhook-demo-flow","projectId":"ptCNUnkh0eHoNtIFJWA2C"}' | python3 -c "
import sys,json
d=json.load(sys.stdin)
print('flow id:', d['id'])
print('版本状态:', d['version']['state'], '| 有效:', d['version']['valid'])
"
实测回显:
flow id: iHQTB0tlaNLEGDy5dSSFV
版本状态: DRAFT | 有效: False
预期比对:新工作流处于 DRAFT 草稿态,valid: False 表示还没有可运行的触发器定义。
4.2 导入工作流定义
通过 IMPORT_FLOW 操作一次性写入触发器与动作链。本工作流的结构:Webhook 触发器(catch_webhook,免认证)→ Code 步骤(计算含 6% 税费的价格)→ HTTP 步骤(POST 到 httpbin.org):
cat > flow1-def.json <<'EOF'
{"type":"IMPORT_FLOW","request":{"displayName":"webhook-demo-flow","trigger":{"name":"trigger","valid":true,"displayName":"Catch Webhook","lastUpdatedDate":"2026-10-18T08:50:00.000Z","type":"PIECE_TRIGGER","settings":{"propertySettings":{},"pieceName":"@activepieces/piece-webhook","pieceVersion":"~0.1.42","triggerName":"catch_webhook","input":{"authType":"none"}},"nextAction":{"name":"process_code","valid":true,"displayName":"Process Order","lastUpdatedDate":"2026-10-18T08:50:00.000Z","type":"CODE","settings":{"sourceCode":{"code":"export const code = async (inputs) => {\n const amount = Number(inputs.amount);\n return {\n order_id: inputs.order_id,\n customer: inputs.customer,\n amount: amount,\n final_price: Math.round(amount * 1.06 * 100) / 100\n };\n};"},"input":{"order_id":"{{trigger.body.order_id}}","customer":"{{trigger.body.customer}}","amount":"{{trigger.body.amount}}"}},"nextAction":{"name":"notify_http","valid":true,"displayName":"Send HTTP request","lastUpdatedDate":"2026-10-18T08:50:00.000Z","type":"PIECE","settings":{"propertySettings":{},"pieceName":"@activepieces/piece-http","pieceVersion":"~0.13.0","actionName":"send_request","input":{"method":"POST","url":"https://httpbin.org/post","headers":{},"queryParams":{},"authType":"NONE","body_type":"json","body":{"data":{"order_id":"{{process_code.order_id}}","final_price":"{{process_code.final_price}}"}}}},"nextAction":null}}}}}
EOF
curl -s -m 30 -X POST http://192.168.146.128:8100/api/v1/flows/iHQTB0tlaNLEGDy5dSSFV \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d @flow1-def.json -o flow1-resp.json -w 'HTTP_STATUS:%{http_code}\n'
python3 -c "
import json
d=json.load(open('flow1-resp.json'))
v=d['version']
print('版本状态:', v['state'], '| 有效:', v['valid'])
steps=[]
a=v['trigger']
while a:
steps.append(a['displayName'])
a=a.get('nextAction')
print('步骤链:', ' -> '.join(steps))
"
实测回显:
HTTP_STATUS:200
版本状态: DRAFT | 有效: True
步骤链: Catch Webhook -> Process Order -> Send HTTP request
预期比对:导入后工作流结构完整(触发器 + 2 个动作),valid: True 表示所有步骤参数通过了连接器 schema 校验。注意 pieceVersion 用 ~ 锁定大版本(如 ~0.1.42),与连接器元数据对齐后才能通过校验——若引用了未同步或版本不存在的连接器,发布时会报 PINC 类错误。
4.3 发布(锁定运行版本)
curl -s -m 60 -X POST http://192.168.146.128:8100/api/v1/flows/iHQTB0tlaNLEGDy5dSSFV \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"type":"LOCK_AND_PUBLISH","request":{"status":"ENABLED"}}' | python3 -c "
import sys,json
d=json.load(sys.stdin)
print('工作流状态:', d.get('status'))
print('已发布版本 id:', d.get('publishedVersionId'))
v=d.get('version',{})
print('版本状态:', v.get('state'))
"
实测回显:
工作流状态: ENABLED
已发布版本 id: bhltBIf1fxOracYond6tU
版本状态: LOCKED
预期比对:发布后 status: ENABLED、版本进入 LOCKED 态并生成 publishedVersionId。此后画布上的草稿改动不再影响线上运行,直到下一次发布——这是 Activepieces 的草稿/发布双版本模型。
五、Webhook 触发验证
5.1 触发并确认同步响应
发布后即可向平台的 Webhook 入口发请求(路径为 /api/v1/webhooks/<flowId>):
curl -s -m 30 -X POST http://192.168.146.128:8100/api/v1/webhooks/iHQTB0tlaNLEGDy5dSSFV \
-H 'Content-Type: application/json' \
-d '{"order_id":"A-2048","amount":299.9,"customer":"Han Meimei"}'
实测回显:
{}
预期比对:Webhook 入口立即返回 {}(默认异步执行,不等待流跑完);请求体被完整接受并投递给执行器。若把触发器配置为同步响应模式,这里会改为返回流内"响应数据"步骤的输出。
5.2 查询运行历史
等待约 10 秒后查询该项目最近 3 条运行记录:
curl -s -m 30 "http://192.168.146.128:8100/api/v1/flow-runs?projectId=ptCNUnkh0eHoNtIFJWA2C&limit=3" \
-H "Authorization: Bearer $TOKEN" | python3 -c "
import sys,json
d=json.load(sys.stdin)
for r in d['data']:
print('run:', r['id'], '| status:', r['status'], '| flowId:', r['flowId'])
"
实测回显:
run: 9Of1jRTgeY94i3VAdpnjD | status: SUCCEEDED | flowId: iHQTB0tlaNLEGDy5dSSFV
run: XH7YpHFOejDCTFxCNrMlK | status: FAILED_RUN | flowId: iHQTB0tlaNLEGDy5dSSFV
run: wgbi81EwspSMdLHBFfHzq | status: FAILED_RUN | flowId: iHQTB0tlaNLEGDy5dSSFV
预期比对:新触发的运行 SUCCEEDED;两条 FAILED_RUN 是 4.2 之前发布失败阶段的遗留触发(当时连接器元数据未同步,见 4.2 的说明),运行历史完整保留了失败记录。
5.3 查看运行详情与步骤输出
curl -s -m 30 "http://192.168.146.128:8100/api/v1/flow-runs/9Of1jRTgeY94i3VAdpnjD?projectId=ptCNUnkh0eHoNtIFJWA2C" \
-H "Authorization: Bearer $TOKEN" | python3 -c "
import sys,json
d=json.load(open('/dev/stdin')) if False else json.load(sys.stdin)
print('run status:', d['status'])
for name in ('trigger','process_code','notify_http'):
s=d['steps'][name]
print('---', name, '| 状态:', s['status'])
print(json.dumps(s.get('output'), ensure_ascii=False)[:220])
"
实测回显:
run status: SUCCEEDED
--- trigger | 状态: SUCCEEDED
{"body":{"order_id":"A-2048","amount":299.9,"customer":"Han Meimei"},"headers":{"host":"192.168.146.128:8100","content-type":"application/json",...}}
--- process_code | 状态: SUCCEEDED
{"order_id":"A-2048","customer":"Han Meimei","amount":299.9,"final_price":317.89}
--- notify_http | 状态: SUCCEEDED
{"status":200,"headers":{"content-type":"application/json",...},"body":{"data":"{\"order_id\":\"A-2048\",\"final_price\":317.89}","...}}
预期比对(本验证的核心链路,三步全部对上):
- 触发步骤:请求体 JSON 原样进入
trigger.body,HTTP 头一并保留; - Code 步骤:模板表达式
{{trigger.body.amount}}正确取值,代码计算299.9 × 1.06 = 317.89输出final_price; - HTTP 步骤:模板
{{process_code.final_price}}引用上游输出,httpbin 回显的请求体包含预期字段,远端返回 200。
输入输出 JSON 逐步骤留痕,说明运行审计数据完整可用。
六、定时触发验证
6.1 创建定时工作流
用 @activepieces/piece-schedule 的 every_x_minutes 触发器建一条每分钟执行的心跳流(Code 步骤输出时间戳):
FLOW2=$(curl -s -m 15 -X POST http://192.168.146.128:8100/api/v1/flows \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"displayName":"schedule-demo-flow","projectId":"ptCNUnkh0eHoNtIFJWA2C"}' \
| python3 -c "import sys,json;print(json.load(sys.stdin)['id'])")
echo "flow2 id: $FLOW2"
实测回显:
flow2 id: 0SHCjSwwEKRzCAwI01gbd
导入定时触发定义并发布(操作同 4.2/4.3,触发器 settings 为 {"pieceName":"@activepieces/piece-schedule","pieceVersion":"~0.1.22","triggerName":"every_x_minutes","input":{"minutes":1}}):
curl -s -m 60 -X POST "http://192.168.146.128:8100/api/v1/flows/$FLOW2" \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"type":"LOCK_AND_PUBLISH","request":{"status":"ENABLED"}}' \
| python3 -c "import sys,json;d=json.load(sys.stdin);print('状态:',d['status'],'| 发布版本:',d['publishedVersionId'])"
实测回显:
状态: ENABLED | 发布版本: dkh1bMN2sGUkLpl3h8aHz
6.2 等待三个调度周期后核对
约 3 分钟后查询该流的运行记录:
curl -s -m 30 "http://192.168.146.128:8100/api/v1/flow-runs?projectId=ptCNUnkh0eHoNtIFJWA2C&flowId=$FLOW2&limit=5" \
-H "Authorization: Bearer $TOKEN" | python3 -c "
import sys,json
d=json.load(sys.stdin)
for r in d['data']:
print('run:', r['id'], '| status:', r['status'], '| 开始:', r['startTime'])
print('共', len(d['data']), '条')
"
实测回显:
run: BAlQMbVZc9Id9UOD8Ykwu | status: SUCCEEDED | 开始: 2026-10-18T09:21:15.457Z
run: ACb29DDhTfzYQr4HQyqab | status: SUCCEEDED | 开始: 2026-10-18T09:20:15.421Z
run: tcoxThxgRAlIP1jPtCmE7 | status: SUCCEEDED | 开始: 2026-10-18T09:19:15.381Z
共 3 条
预期比对:三次运行时间戳恰好间隔 60 秒(15 秒相位一致),全部 SUCCEEDED,说明定时调度器按 minutes=1 精确触发、执行链路稳定。
七、动作组件逐项验证
7.1 Code 代码步骤
已在 5.3 中验证核心行为(入参绑定、Number() 转换、业务计算、输出 JSON 化)。补充验证:代码抛异常的行为见 9.3 节失败流。
7.2 HTTP 请求步骤
@activepieces/piece-http 的 send_request 动作已在 5.3 验证 POST + JSON body;再验证 GET 与查询参数拼接(目标 httpbin.org/get):
curl -s -m 30 "http://192.168.146.128:8100/api/v1/flow-runs/ZAQiV97IpLR7TVitVn0Il?projectId=ptCNUnkh0eHoNtIFJWA2C" \
-H "Authorization: Bearer $TOKEN" | python3 -c "
import sys,json
d=json.load(sys.stdin)
o=d['steps']['use_var_http'].get('output') or {}
print('HTTP 状态:', o.get('status'))
print('对端 Host:', o.get('body',{}).get('headers',{}).get('Host'))
"
实测回显:
HTTP 状态: 200
对端 Host: httpbin.org
预期比对:GET 请求经模板拼出的 URL 正确到达目标端点,返回结构与 POST 一致(status/headers/body 三段)。
7.3 循环(Loop)步骤
LOOP_ON_ITEMS 对数组逐项执行子步骤。构造一条含循环的流(输入 items 数组,子步骤逐项输出 sku/qty),发布后用 Webhook 触发:
curl -s -m 30 -X POST "http://192.168.146.128:8100/api/v1/webhooks/GwXt8RSWuIN8K0xEKAzMf" \
-H 'Content-Type: application/json' \
-d '{"amount":299,"customer":"VIP Guy","items":[{"sku":"SKU-1","qty":2},{"sku":"SKU-2","qty":5}]}'
实测回显:
{}
curl -s -m 30 "http://192.168.146.128:8100/api/v1/flow-runs/20bz0plnuwpxQvzPwZ54e?projectId=ptCNUnkh0eHoNtIFJWA2C" \
-H "Authorization: Bearer $TOKEN" | python3 -c "
import sys,json
d=json.load(sys.stdin)
print('run status:', d['status'])
loop=d['steps']['loop_items']
out=loop.get('output') or {}
print('循环轮数:', len(out.get('iterations',[])))
print('最后 item:', json.dumps({k:out.get(k) for k in ('item','index')}, ensure_ascii=False))
for it in out.get('iterations',[]):
ic=it.get('item_code',{})
print('子步骤输出:', json.dumps(ic.get('output'), ensure_ascii=False))
"
实测回显:
run status: SUCCEEDED
循环轮数: 2
最后 item: {"item": {"sku": "SKU-2", "qty": 5}, "index": 2}
子步骤输出: {"sku": "SKU-1", "qty": 2}
子步骤输出: {"sku": "SKU-2", "qty": 5}
预期比对:数组两个元素各执行一轮子步骤,iterations 逐轮留痕,循环上下文变量 {{loop_items.item}} 与 {{loop_items.index}} 正确注入(index 从 1 计数)。注意:循环内引用上下文要用循环步骤的 name(本例为 loop_items),表达式引擎不校验引用名,写错不会报错但拿不到数据(见 9.4 节实测返工记录)。
7.4 路由分支(Router)步骤
同一条流里在循环前配置 ROUTER(条件分支 amount > 200 走 VIP Route,否则走 Fallback),验证条件命中:
curl -s -m 30 "http://192.168.146.128:8100/api/v1/flow-runs/20bz0plnuwpxQvzPwZ54e?projectId=ptCNUnkh0eHoNtIFJWA2C" \
-H "Authorization: Bearer $TOKEN" | python3 -c "
import sys,json
d=json.load(sys.stdin)
r=d['steps']['router_1']
out=r.get('output') or {}
print('router 输出:', json.dumps(out, ensure_ascii=False))
vip=d['steps'].get('vip_code',{})
print('VIP 分支子步骤状态:', vip.get('status'), '| 输出:', json.dumps(vip.get('output'), ensure_ascii=False))
"
实测回显:
router 输出: {"branches": [{"branchName": "VIP Route", "branchIndex": 1, "evaluation": true}, {"branchName": "Fallback", "branchIndex": 2, "evaluation": false}]}
VIP 分支子步骤状态: SUCCEEDED | 输出: {"level":"VIP","customer":"VIP Guy","note":"amount > 200"}
预期比对:amount=299 使 VIP Route 分支求值为 true、Fallback 分支求值为 false,只有 VIP 分支的子步骤被执行——路由语义符合 EXECUTE_FIRST_MATCH(执行第一个命中的分支,branches 数组同时给出各分支的求值结果便于审计)。
八、全局变量与连接器验证
8.1 全局变量(Variables)
创建项目级变量并列表确认:
curl -s -m 20 -X POST "http://192.168.146.128:8100/api/v1/variables" \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"name":"api_base","projectId":"ptCNUnkh0eHoNtIFJWA2C","value":"https://httpbin.org"}' \
-w '\nHTTP:%{http_code}\n' | python3 -c "
import sys,json
raw=sys.stdin.read()
body,_,tail=raw.rpartition('HTTP:')
d=json.loads(body)
print('变量 id:', d.get('id'), '| 名称:', d.get('name'))
print('HTTP:'+tail)
"
实测回显:
变量 id: V6HuKgIncRHRBu6eCK5oq | 名称: api_base
HTTP:200
变量是"一个名字一个值"的模型(再建一条 tax_rate=0.06,列表返回两条,实测回显略)。使用验证:把 HTTP 步骤的 URL 写成 {{variables.api_base}}/get,运行后对端 Host 为 httpbin.org(见 7.2 回显),说明变量在工作流运行时正确插值。
8.2 连接器凭据(Connection)创建
以 @activepieces/piece-todoist 为例创建 SECRET_TEXT 型凭据(创建请求需带顶层 type 与 value.type、业务键名为 externalId):
curl -s -m 20 -X POST "http://192.168.146.128:8100/api/v1/app-connections" \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"pieceName":"@activepieces/piece-todoist","externalId":"todoist_demo_conn","displayName":"Todoist Demo","projectId":"ptCNUnkh0eHoNtIFJWA2C","type":"SECRET_TEXT","value":{"type":"SECRET_TEXT","secret_text":"0123456789abcdef0123456789abcdef01234567"}}' \
-w '\nHTTP:%{http_code}\n' | python3 -c "
import sys,json
raw=sys.stdin.read()
body,_,tail=raw.rpartition('HTTP:')
d=json.loads(body)
print('externalId:', d.get('externalId'), '| 类型:', d.get('type'), '| 状态:', d.get('status'))
print('HTTP:'+tail)
"
实测回显:
externalId: todoist_demo_conn | 类型: SECRET_TEXT | 状态: ACTIVE
HTTP:201
列表确认并删除(清理演示凭据):
curl -s -m 20 "http://192.168.146.128:8100/api/v1/app-connections?projectId=ptCNUnkh0eHoNtIFJWA2C" \
-H "Authorization: Bearer $TOKEN" | python3 -c "
import sys,json
d=json.load(sys.stdin)
for c in d['data']:
print('连接:', c['externalId'], '| piece:', c['pieceName'], '| 状态:', c['status'])
"
实测回显:
连接: todoist_demo_conn | piece: @activepieces/piece-todoist | 状态: ACTIVE
curl -s -m 20 -X DELETE "http://192.168.146.128:8100/api/v1/app-connections/iNpKNeSSS1pq9qDgnVgXa?projectId=ptCNUnkh0eHoNtIFJWA2C" \
-H "Authorization: Bearer $TOKEN" -w '\nHTTP:%{http_code}\n'
实测回显:
HTTP:204
预期比对:凭据创建即加密落库(返回不回显 secret_text 明文)、列表元数据正常、删除返回 204。注意凭据值按连接器 schema 校验:实测用伪造 key 创建 @activepieces/piece-openai 凭据时被在线校验拦截,返回 INVALID_APP_CONNECTION — Invalid API key(说明部分商业连接器在保存时会真实调用对端 API 验证凭据有效性,这是预期内的安全行为);@activepieces/piece-todoist 等 SECRET_TEXT 型连接器不做在线校验,可正常保存。
九、异常与边界验证
9.1 /test 测试触发(生成样例数据,不产生真实运行)
curl -s -m 30 -X POST "http://192.168.146.128:8100/api/v1/webhooks/iHQTB0tlaNLEGDy5dSSFV/test" \
-H 'Content-Type: application/json' -d '{"order_id":"TEST-001","amount":10.5,"customer":"Test User"}' \
-w '\nHTTP:%{http_code}\n'
实测回显:
{}
HTTP:200
预期比对:/test 端点返回 200 且响应为空对象——它的用途是给画布"测试触发器"按钮投递样例负载(供后续步骤做数据映射预览),不进入正式运行队列,运行历史不新增记录。编排自动化验收时不要把它当作真实触发。
9.2 无效 Webhook URL
curl -s -m 15 -X POST "http://192.168.146.128:8100/api/v1/webhooks/aaaaaaaaaaaaaaaaaaaaa" \
-H 'Content-Type: application/json' -d '{"x":1}' -w '\nHTTP:%{http_code}\n'
实测回显:
{"statusCode":404,"error":"Not Found","message":"Route not found"}
HTTP:404
预期比对:不存在的 flowId 返回 404,且查询运行历史确认未产生任何运行记录——无效请求不会污染数据,也不会被异步吞掉。
9.3 流执行失败的错误呈现
构造一条故意失败的流(Code 步骤抛出业务异常),发布后触发:
curl -s -m 30 -X POST "http://192.168.146.128:8100/api/v1/webhooks/os1nrc2F7u5xJdab5UaTs" \
-H 'Content-Type: application/json' -d '{"name":"boom-case"}' && echo
实测回显:
{}
curl -s -m 30 "http://192.168.146.128:8100/api/v1/flow-runs/UqZENs9aAFVRpMMANpGY4?projectId=ptCNUnkh0eHoNtIFJWA2C" \
-H "Authorization: Bearer $TOKEN" | python3 -c "
import sys,json
d=json.load(sys.stdin)
print('run status:', d['status'])
s=d['steps']['boom_code']
print('步骤状态:', s['status'])
print('errorMessage:', (s.get('errorMessage') or '')[:220])
"
实测回显:
run status: FAILED
步骤状态: FAILED
errorMessage: "{\"__apErrorVersion\":1,\"message\":\"intentional failure: invalid config for boom-case\",\"raw\":\"Error: intentional failure: invalid config for boom-case\\n at Object.code (/usr/src/app/cache/v15/codes/.../boom_code/index.js:26:9)..."
预期比对:失败被精确隔离在出错步骤(前序 trigger 正常 SUCCEEDED),运行整体标记 FAILED,Code 步骤抛出的自定义异常消息与调用栈完整呈现在 errorMessage(结构化 JSON:message 为业务消息、raw 为原始堆栈),定位粒度到步骤级。失败运行会完整进入运行历史,支持定点重试。
9.4 循环上下文引用名错误
循环上下文变量的引用名是循环步骤的 name(如 {{loop_items.item}}、{{loop_items.index}}),而不是框架预设名。首次导入定义时误写成 {{loop_item.item}},运行不报错但子步骤拿不到数组元素;把引用名改为循环步骤名 loop_items 后重新导入、发布、触发,输出恢复正常(7.3 的回显即修正后的结果)。经验教训:表达式引擎不校验引用名的有效性,编排循环后务必先用画布的测试运行确认数据映射再发布。
9.5 Webhook Basic 认证(异步模式下的静默拒绝)
把触发器 authType 配置为 basic(用户名 admin / 密码 q1w2e3r4)并发布后,先发一个无凭据请求:
curl -s -m 30 -X POST "http://192.168.146.128:8100/api/v1/webhooks/VXI5l7ieV0IV1alvW8UY6" \
-H 'Content-Type: application/json' -d '{"name":"noauth"}' -w '\nHTTP:%{http_code}\n'
实测回显:
{}
HTTP:200
注意:HTTP 层返回的是 200——异步模式下 Webhook 入口只负责"收包入队",认证发生在执行器端。再发一个携带正确 Basic 凭据的请求:
curl -s -m 30 -u admin:q1w2e3r4 -X POST "http://192.168.146.128:8100/api/v1/webhooks/VXI5l7ieV0IV1alvW8UY6" \
-H 'Content-Type: application/json' -d '{"name":"auth-ok"}' -w '\nHTTP:%{http_code}\n'
实测回显:
{}
HTTP:200
然后核对运行历史与执行器日志。运行历史只有一条(auth-ok 的 SUCCEEDED 运行),noauth 请求没有产生任何运行记录;执行器日志中可以找到它的处置记录:
{"event":"job.execute","job":{"type":"EXECUTE_WEBHOOK"},...,"requestLogs":[...,{"level":"warn","message":"Trigger run did not complete successfully",...}],"error":"Webhook authentication failed: request did not pass the configured basic check","status":"USER_FAILURE",...,"level":"warn","service":"activepieces-worker",...}
预期比对:认证失败的请求被判定为 USER_FAILURE,既不计入运行历史也不触发流执行,效果等同拒绝;正确凭据的请求正常执行并留下 SUCCEEDED 运行。这里有一个值得记录的行为细节:异步模式下认证失败对调用方完全静默(HTTP 200 + 空响应),排障时要靠运行历史缺失加执行器日志判断,如果调用方需要明确的拒绝信号,应把 Webhook URL 加上 /sync 后缀走同步模式(同步失败会以错误响应呈现)。
十、验收结论清单
| # | 验证项 | 预期 | 实测结果 | 结论 |
|---|---|---|---|---|
| 1 | 首个用户注册 | 200,自动 ADMIN + 首项目 | platformRole=ADMIN,projectId 自动创建 | 通过 |
| 2 | 登录签发 JWT | 返回会话令牌 | eyJhbGciOiJIUzI1NiIs... | 通过 |
| 3 | 连接器元数据同步 | 可查询、版本明确 | 769 条元数据,webhook/http/schedule 版本齐全 | 通过 |
| 4 | 工作流创建/导入/发布 | DRAFT→LOCKED,ENABLED | 三态迁移正常,publishedVersionId 生成 | 通过 |
| 5 | Webhook 触发 | 异步执行,逐步骤留痕 | trigger→code→http 输出 JSON 链路全部正确 | 通过 |
| 6 | 定时触发(1 分钟) | 60 秒间隔稳定调度 | 3 条运行间隔精确 60s,全部 SUCCEEDED | 通过 |
| 7 | Code 步骤 | 入参绑定、计算、输出 | final_price=317.89 计算正确 | 通过 |
| 8 | HTTP 步骤 | GET/POST + 模板拼接 | httpbin 回显字段完整,200 | 通过 |
| 9 | Loop 循环 | 逐项执行、iterations 留痕 | 2 轮循环,item/index 上下文正确 | 通过 |
| 10 | Router 路由分支 | 首个命中分支执行 | VIP 分支 evaluation=true 执行、Fallback=false 跳过 | 通过 |
| 11 | 全局变量 | 创建/引用/运行时插值 | {{variables.api_base}} 运行时正确解析 | 通过 |
| 12 | 连接器凭据 | 加密存储、增删查 | 创建 201/列表/删除 204,明文不回显 | 通过 |
| 13 | /test 测试触发 | 投递样例不进运行队列 | 200 + 空对象,运行历史无新增 | 通过 |
| 14 | 无效 Webhook URL | 404 且不留运行 | 404 Route not found | 通过 |
| 15 | 失败流错误呈现 | 步骤级定位 + 消息透出 | run/step 均标记 FAILED,errorMessage 含业务消息与堆栈 | 通过 |
| 16 | Webhook Basic 认证 | 非法凭据不执行、合法凭据放行 | 无凭据静默拒绝(无运行记录,日志 USER_FAILURE),正确凭据 SUCCEEDED | 通过 |
总体结论:Activepieces 0.92.3 社区版在本次验收覆盖的 16 项功能全部通过,工作流执行链路(触发→插值→代码→HTTP→循环/分支)数据流正确,异常路径(无效请求、执行失败、认证拒绝)行为符合预期且审计信息完整,可进入生产部署评估阶段。
十一、参考链接
- REST API 官方文档:https://www.activepieces.com/docs/api-reference/overview
- 服务内置 OpenAPI 规范:https://cloud.activepieces.com/api/v1/openapi.json
- Webhook 触发器文档:https://www.activepieces.com/docs/pieces/developers/building-pieces/webhook
- 定时触发器文档:https://www.activepieces.com/docs/pieces/developers/building-pieces/schedule
- 本组件部署篇:《Activepieces Docker Compose 部署指南》
- 本组件认知篇:《Activepieces 0.92.3 介绍与选型指南》