审批开放 API
通过 Muse Open 转发地址创建审批、查询状态并接收回调事件。
审批开放接口
POST https://muse-open.test.tezign.com/api/muse/approval-api
说明:调用方只需按本文档组织请求体;下游鉴权与内部路由由转发链路处理。
调用约定
- 请求方法:
POST Content-Type:application/json- 请求体必须为合法 JSON
- 使用
service字段区分能力
Service 能力总览
公共字段:
service(必填,string)- 可选值:
subscriptionQuery/subscriptionChange/listTemplates/createApproval/taskStatusQuery
公共请求示例:
json
{"service": "listTemplates"}
统一响应结构:
json
{"code": "0","message": "成功","result": {},"traceId": "trace_20261012_001"}
常见失败码:
400:参数错误(如invalid_payload:*)401:鉴权失败403:权限不足404:资源不存在(如team_not_found/template_not_found/task_not_found/asset_not_found)500:服务内部错误
查询模板(listTemplates)
请求:
json
{"service": "listTemplates"}
响应 result 为模板数组,核心字段:
id/name/description/enabledusageCount/autoImportEnabledtargetFolder(可能为空)workflow(节点定义,包含审批人、抄送、签审模式、超时配置等)
创建审批任务(createApproval)
请求字段:
service(必填,固定createApproval)templateId(必填,string)createdBy(必填,string,发起人用户 ID)assets(必填,array,至少 1 条)name(建议传原始文件名)url(必填,公网可访问http/https)size/mimeType/extension(可选)
sourceContext(可选,业务上下文)reason(可选,发起原因)
请求示例:
json
{"service": "createApproval","templateId": "101","createdBy": "10086","assets": [{"name": "demo.mp4","url": "https://cdn.example.com/m/asset_1.mp4"}]}
成功响应:
json
{"code": "0","message": "成功","result": {"taskId": "9527"}}
调用限制:
assets不能为空assets[].url不能为空且同一请求内不能重复assets[].url必须是公网地址,不能是localhost、内网域名或私网 IP- 下载超时或超大小限制会返回
invalid_payload:*
查询任务状态(taskStatusQuery)
请求:
json
{"service": "taskStatusQuery","taskId": "9527"}
响应 result 关键字段:
taskIdstatus:PENDING | PROCESSING | APPROVED | REJECTED | CANCELEDstatusLabelprogressPercentcurrentNodeName/currentNodeSignModecurrentNodeProgress(通过、拒绝、待审统计)currentNodeApprovers(节点内审批人状态)operator/updatedAt
订阅与回调(subscriptionQuery / subscriptionChange)
查询是否订阅:
json
{"service": "subscriptionQuery"}
订阅回调:
json
{"service": "subscriptionChange","operation": "ADD","callbackUrl": "https://example.com/webhook/approval"}
取消订阅:
json
{"service": "subscriptionChange","operation": "DELETE"}
订阅回调 payload 说明
当调用 subscriptionChange 且 operation = ADD 成功后,审批状态变化会回调到你传入的 callbackUrl。
回调请求基础约定
- 请求方法:
POST Content-Type:application/json
回调体结构:
json
{"eventId": "evt_xxx","eventType": "APPROVAL","eventTime": "1760258885000","payload": {"eventName": "TASK_APPROVED","orgId": "233","taskId": "9527","taskStatus": "APPROVED"}}
字段说明:
eventId:事件唯一 ID(接收方建议按此做幂等)eventType:固定为APPROVALeventTime:毫秒时间戳字符串payload:业务数据体
payload 公共字段
每个回调事件的 payload 至少包含:
eventName:事件名orgId:组织 ID(由系统填充)taskId:审批任务 IDtaskStatus:任务状态(PROCESSING/APPROVED/TIMEOUT)
事件全集(触发条件)
NODE_STARTED:节点开始处理时触发(创建任务首节点、节点流转到下一节点、流转到重提节点都会触发)APPROVER_SUBMITTED:审批人在节点提交审批意见时触发(同一节点可多次触发,不同审批人各触发一次)NODE_COMPLETED:某节点办结时触发(通过 / 拒绝)NODE_TRANSFERRED:节点办结后流转到下一处理节点时触发(正常流转、重提流转、超时转交流转)SUBMITTER_RESUBMITTED:发起人在“待重新提交”节点提交新素材时触发TASK_APPROVED:任务终态通过时触发NODE_TIMEOUT:节点超时时触发(超时转交或超时待重新分配)
字段定义(完整)
payload.node(节点对象)
nodeId(string):节点 ID(必有)nodeOrder(number):节点顺序(部分事件存在)nodeRole(string):节点角色(normal/resubmit_submitter/resubmit_approver,部分事件存在)nodeStatus(string):节点状态- 常见值:
pending/processing/approved/rejected/timeout_transferred/re_assigned
- 常见值:
signMode(string):签审模式(and/or,按场景出现)approverCount(number):审批人数(按场景出现)approvers(array):节点办结时审批人结果(NODE_COMPLETED)- 子项:
id、name、status
- 子项:
startedAt(string):节点开始时间(ISO)completedAt(string):节点完成时间(ISO)timeoutHours(number):节点超时阈值小时数(超时事件出现)
payload.approver(审批人提交对象,APPROVER_SUBMITTED)
id(string):审批人用户 IDname(string):审批人名称decision(approved|rejected):审批决定comment(string):审批意见submittedAt(string):提交时间(ISO)
payload.submitter(提交人对象,SUBMITTER_RESUBMITTED)
id(string):提交人用户 IDname(string):提交人名称comment(string):重提备注submittedAt(string):提交时间(ISO)
payload.timeout(超时对象)
transferTo(object | null):超时转交目标- 有转交人时:
{ "id": "...", "name": "..." } - 无转交人时:
null
- 有转交人时:
payload.latestAssets(重提素材快照,SUBMITTER_RESUBMITTED)
assetId(string)filePathId(string)version(number)oldStorePath(string):旧版本存储路径newStorePath(string):新版本存储路径updatedAt(string):更新时间(ISO)
payload.transferReason(NODE_TRANSFERRED)
NEXT_NODE:正常流转到下一审批节点RESUBMIT_SUBMITTER:流转到待重新提交节点RESUBMIT_APPROVER:重提后流转到重审节点TIMEOUT_TRANSFER:超时后转交
回调示例(全事件)
TASK_APPROVED 示例:
json
{"eventId": "evt_2a93f5f5f4d64907a2f3b73cb8f0f201","eventType": "APPROVAL","eventTime": "1760258885000","payload": {"eventName": "TASK_APPROVED","orgId": "233","taskId": "9527","taskStatus": "APPROVED","operator": {"id": "10086","name": "张三"},"finishedAt": "2026-10-12T08:08:05.000Z"}}
NODE_STARTED 示例:
json
{"eventId": "evt_d3a39140643a4d73afedf0a47c0a601d","eventType": "APPROVAL","eventTime": "1760258700000","payload": {"eventName": "NODE_STARTED","orgId": "233","taskId": "9527","taskStatus": "PROCESSING","node": {"nodeId": "12001","nodeOrder": 1,"nodeRole": "normal","nodeStatus": "processing","signMode": "and","approverCount": 2,"startedAt": "2026-10-12T08:05:00.000Z"}}}
APPROVER_SUBMITTED 示例:
json
{"eventId": "evt_8bc9f92f83564f10adf8270b598f95d2","eventType": "APPROVAL","eventTime": "1760258820000","payload": {"eventName": "APPROVER_SUBMITTED","orgId": "233","taskId": "9527","taskStatus": "PROCESSING","node": {"nodeId": "12001","nodeOrder": 1,"nodeStatus": "processing","signMode": "and"},"approver": {"id": "10086","name": "张三","decision": "approved","comment": "同意","submittedAt": "2026-10-12T08:07:00.000Z"},"approverCount": 2}}
NODE_COMPLETED 示例:
json
{"eventId": "evt_652d4d8c09d24477af9fdb21624fd070","eventType": "APPROVAL","eventTime": "1760258855000","payload": {"eventName": "NODE_COMPLETED","orgId": "233","taskId": "9527","taskStatus": "PROCESSING","node": {"nodeId": "12001","nodeOrder": 1,"nodeStatus": "approved","signMode": "and","approvers": [{"id": "10086","name": "张三","status": "approved"},{"id": "10087","name": "李四","status": "approved"}],"completedAt": "2026-10-12T08:07:35.000Z"}}}
NODE_TRANSFERRED 示例(正常流转):
json
{"eventId": "evt_21881077723e4a17aa4e2e58ee78ce79","eventType": "APPROVAL","eventTime": "1760258856000","payload": {"eventName": "NODE_TRANSFERRED","orgId": "233","taskId": "9527","taskStatus": "PROCESSING","transferReason": "NEXT_NODE","fromNode": {"nodeId": "12001","nodeOrder": 1,"nodeStatus": "approved"},"node": {"nodeId": "12002","nodeOrder": 2,"nodeRole": "normal","nodeStatus": "pending"}}}
NODE_TRANSFERRED 示例(拒绝后流转到待重提):
json
{"eventId": "evt_e612f8b5e1de4960bb712d3df18e2617","eventType": "APPROVAL","eventTime": "1760258900000","payload": {"eventName": "NODE_TRANSFERRED","orgId": "233","taskId": "9527","taskStatus": "PROCESSING","transferReason": "RESUBMIT_SUBMITTER","fromNode": {"nodeId": "12001","nodeOrder": 1,"nodeStatus": "rejected"},"node": {"nodeId": "13001","nodeOrder": 2,"nodeRole": "resubmit_submitter","nodeStatus": "pending"}}}
NODE_TRANSFERRED 示例(重提后流转到重审):
json
{"eventId": "evt_f0c43dd5ca5d4fca9e4adbe536fbb8f5","eventType": "APPROVAL","eventTime": "1760259005000","payload": {"eventName": "NODE_TRANSFERRED","orgId": "233","taskId": "9527","taskStatus": "PROCESSING","transferReason": "RESUBMIT_APPROVER","fromNode": {"nodeId": "13001","nodeOrder": 2,"nodeStatus": "approved"},"node": {"nodeId": "13002","nodeOrder": 3,"nodeRole": "resubmit_approver","nodeStatus": "pending"}}}
NODE_TIMEOUT 示例(超时转交):
json
{"eventId": "evt_b0d67fb6f3de42e79a60b8f9df6ad20d","eventType": "APPROVAL","eventTime": "1760260000000","payload": {"eventName": "NODE_TIMEOUT","orgId": "233","taskId": "9527","taskStatus": "TIMEOUT","node": {"nodeId": "12002","nodeStatus": "timeout_transferred","timeoutHours": 24},"timeout": {"transferTo": {"id": "10100","name": "王五"}}}}
NODE_TIMEOUT 示例(超时未转交):
json
{"eventId": "evt_2e4372871f0c4a7eb95a9d7c638de8a0","eventType": "APPROVAL","eventTime": "1760260100000","payload": {"eventName": "NODE_TIMEOUT","orgId": "233","taskId": "9527","taskStatus": "TIMEOUT","node": {"nodeId": "12002","nodeStatus": "re_assigned","timeoutHours": 24},"timeout": {"transferTo": null}}}
SUBMITTER_RESUBMITTED 示例:
json
{"eventId": "evt_5d017db5b458443697f6c10d5f6370df","eventType": "APPROVAL","eventTime": "1760259000000","payload": {"eventName": "SUBMITTER_RESUBMITTED","orgId": "233","taskId": "9527","taskStatus": "PROCESSING","node": {"nodeId": "12002","nodeOrder": 3,"nodeStatus": "approved","signMode": "and"},"submitter": {"id": "10010","name": "李四","comment": "已更新素材,重新提交","submittedAt": "2026-10-12T08:10:00.000Z"},"latestAssets": [{"assetId": "asset_1","filePathId": "30001","version": 3,"oldStorePath": "assets/old/asset_1_v2.mp4","newStorePath": "assets/new/asset_1_v3.mp4","updatedAt": "2026-10-12T08:10:00.000Z"}],"latestAssetCount": 1}}
NODE_TRANSFERRED 示例(超时转交后):
json
{"eventId": "evt_15f4348dd4f34a4b9d51ec93313f4dff","eventType": "APPROVAL","eventTime": "1760260001000","payload": {"eventName": "NODE_TRANSFERRED","orgId": "233","taskId": "9527","taskStatus": "PROCESSING","transferReason": "TIMEOUT_TRANSFER","node": {"nodeId": "12002","nodeStatus": "timeout_transferred"},"timeout": {"transferTo": {"id": "10100","name": "王五"}}}}
事件时序(关键流程)
说明:回调为“至少一次投递”,消费方应按 eventId 做幂等去重,不要假设不会重复投递。
正常通过(多节点)
NODE_STARTED(首节点开始)APPROVER_SUBMITTED(每位审批人提交时触发,可能多次)NODE_COMPLETED(当前节点完成)- 若有下一节点:
NODE_TRANSFERRED->NODE_STARTED - 重复 2~4,直到最后一个节点完成
TASK_APPROVED(任务终态通过)
拒绝后重提
APPROVER_SUBMITTED(decision = rejected)NODE_COMPLETED(nodeStatus = rejected)NODE_TRANSFERRED(transferReason = RESUBMIT_SUBMITTER)NODE_STARTED(nodeRole = resubmit_submitter)SUBMITTER_RESUBMITTED(提交人重提)NODE_COMPLETED(重提节点完成)NODE_TRANSFERRED(transferReason = RESUBMIT_APPROVER)NODE_STARTED(重审节点开始)- 后续再次进入正常审批链路(
APPROVER_SUBMITTED...)
超时转交 / 超时未转交
- 节点超时触发
NODE_TIMEOUT(taskStatus = TIMEOUT) - 若配置了超时转交人:紧接着触发
NODE_TRANSFERRED(transferReason = TIMEOUT_TRANSFER),任务回到PROCESSING - 若无可转交人:仅有
NODE_TIMEOUT(常见nodeStatus = re_assigned),等待人工处理后再推进
curl 示例
查询模板:
bash
curl --location --request POST 'https://muse-open.test.tezign.com/api/muse/approval-api' \--header 'Content-Type: application/json' \--data-raw '{"service":"listTemplates"}'
创建审批任务:
bash
curl --location --request POST 'https://muse-open.test.tezign.com/api/muse/approval-api' \--header 'Content-Type: application/json' \--data-raw '{"service":"createApproval","templateId":"101","createdBy":"10086","assets":[{"name":"demo.mp4","url":"https://cdn.example.com/m/asset_1.mp4"}]}'
查询任务状态:
bash
curl --location --request POST 'https://muse-open.test.tezign.com/api/muse/approval-api' \--header 'Content-Type: application/json' \--data-raw '{"service":"taskStatusQuery","taskId":"9527"}'