开发工具/API 参考/API 教程/回调管理/接收并处理回调
接收并处理回调
更新于: 2026-06-25 19:29:14
开发者服务器收到回调事件后,应校验签名、从回调消息中提取信息,进行业务处理后,返回处理结果给扣子服务端。
为了检验回调事件未被第三方篡改,扣子编程提供验证回调消息签名的方法。将请求头中的 X-Coze-Nonce、X-Coze-Timestamp 参数和回调 Token、回调消息 Body 进行拼接、编码、哈希计算,并校验最终的值是否和请求头中的签名字段 X-Coze-Signature 完全一致。若完全一致,表示验证通过,此消息为扣子编程官方发送的回调消息。
校验签名的方式如下:
获取参数。
获取请求头中的 X-Coze-Timestamp、X-Coze-Nonce 值,分别记为 timestamp、nonce。
获取原始请求 Body 的数据,记为 body。
获取配置回调地址时得到的 token。
你可以在回调管理页面,在对应的回调应用卡片中,选择更多 > 编辑,查看回调 Token。
拼接参数,并编码。
按照 timestamp、nonce、token、body 的顺序拼接这些参数,并对拼接后的字符串进行 UTF-8 编码,即 encode('utf-8') 编码,得到字节数组(byte[]),记为 encoded_bytes。
将编码结果进行哈希计算。
对 encoded_bytes 进行 SHA-1 哈希计算,得到哈希值,记为 hash_bytes,并将 hash_bytes 转换为 16 进制字符串,记为 sig。
校验签名的一致性。
校验 sig 与请求头中 X-Coze-Signature 的值是否一致。
通过 Go 进行签名校验的示例代码如下:
// 签名
func GenPostRequestSignature(nonce string, timestamp string, body string, token string) string {
var b strings.Builder
b.WriteString(timestamp)
b.WriteString(nonce)
b.WriteString(token)
b.WriteString(body)
bs := []byte(b.String())
h := sha1.New()
h.Write(bs)
bs = h.Sum(nil)
return fmt.Sprintf("%x", bs)
}
校验签名通过后,你可以从回调信息中提取出详细的事件信息。
event_type。event_type,解析不同的 event 结构。根据提取的事件信息,业务侧进行对应的业务处理,并在收到消息后的 3 秒内做出响应,否则扣子编程服务端会判定为响应超时并重发消息,你需要根据 header 中的 event_id 进行去重处理,以避免重复处理同一事件。
说明
智能体发布回调的超时时间为 10 秒,其余回调的超时时间为 3 秒。
在扣子编程中配置回调地址后,扣子编程通知服务器以 POST 请求方法向渠道指定的回调地址发送渠道事件通知回调,数据格式为 JSON,字符编码为 UTF-8,签名算法为 HMAC/SHA1。
回调消息的公共请求 Header 中包含以下字段:
|
字段 |
类型 |
说明 |
|---|---|---|
|
X-Coze-Nonce |
String |
随机数。 |
|
X-Coze-Timestamp |
Integer |
回调事件的触发时间,Unixtime 时间戳格式,单位为毫秒。 |
|
X-Coze-Signature |
String |
扣子服务端签名。你需要使用 |
消息通知回调的回调结构体 Body 中包括 header 和 event 两个字段。
Body 中的 header 字段:
|
字段 |
类型 |
说明 |
|---|---|---|
|
event_type |
String |
事件类型,目前支持的事件类型包括:
|
|
event_id |
String |
事件 ID。当开发者服务器响应超时或处理失败时,扣子编程会重发该事件的消息,你需要根据 |
|
created_at |
Integer |
回调事件的触发时间,Unixtime 时间戳格式,单位为毫秒。 |
|
coze_account_id |
string |
事件所属的扣子账号 ID。例如,在智能体发布事件中,即为该智能体所属空间的所有者的账号 ID。 |
|
api_app_id |
string |
回调应用的 ID。 |
|
connector_id |
string |
订阅渠道回调时,会返回智能体所属渠道的 ID。 |
Body 中的 event 字段:其中封装了消息的具体信息,其格式与 event_type 有关,各种事件类型的 event 结构不同,详细的回调事件参数请参见智能体发布回调事件、智能体删除回调事件、智能体下架回调事件、账单推送回调事件、申请插件扩容回调事件、插件扩容到期回调事件。
通过 Go 进行处理智能体发布回调的完整示例代码如下:
func EventCallback(w http.ResponseWriter, r *http.Request) {
// 读取请求体
body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, "Failed to read request body", http.StatusInternalServerError)
log.Printf("Failed to read request body: %v", err)
return
}
defer r.Body.Close()
// 记录请求体日志
log.Printf("EventCallback body %s", string(body))
reqSignature := r.Header.Get("X-Coze-Signature")
reqNonce := r.Header.Get("X-Coze-Nonce")
reqTimestamp := r.Header.Get("X-Coze-Timestamp")
// 校验签名
sign := Signature(reqNonce, reqTimestamp, string(body), "your_connector_token")
log.Printf("EventCallback sign gen: %s", sign)
log.Printf("EventCallback sign result %t", sign == reqSignature)
if sign != reqSignature{
// 签名不通过处理
http.Error(w, "Signature verification failed", http.StatusUnauthorized)
log.Printf("Signature verification failed")
return
}
// 反序列事件
req := &ConnectorCallbackEvent{}
err := json.Unmarshal(c.GetBodyBytes(), req)
if err != nil {
http.Error(w, "Failed to unmarshal ConnectorCallbackEvent", http.StatusInternalServerError)
log.Printf("Failed to unmarshal ConnectorCallbackEvent: %+v", err)
return
}
// 根据不同的 eventType,反序列化为不同的事件体
var event BotPublishedEvent
if req.Header.EventType == ConnectorCallbackEventTypeBotPublish {
err = json.Unmarshal(req.Event, &event)
if err != nil {
http.Error(w, "Failed to unmarshal BotPublishedEvent", http.StatusInternalServerError)
log.Printf("Failed to unmarshal BotPublishedEvent: %+v", err)
return
}
}
// 进行业务处理
log.Printf("EventCallback BotID %d", event.BotID)
log.Printf("EventCallback UserID %d", event.UserID)
log.Printf("EventCallback Description %s", event.Description)
log.Printf("EventCallback BotName %s", event.BotName)
// 返回处理结果
auditByte, _ := json.Marshal(ConnectorCallbackEventResponse{
Audit: audit,
})
log.Printf("EventCallback auditByte: %s", string(auditByte))
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_, _ = w.Write(auditByte)
}
// 签名
func Signature(nonce string, timestamp string, body string, token string) string {
var b strings.Builder
b.WriteString(timestamp)
b.WriteString(nonce)
b.WriteString(token)
b.WriteString(body)
bs := []byte(b.String())
h := sha1.New()
h.Write(bs)
bs = h.Sum(nil)
return fmt.Sprintf("%x", bs)
}
// 以下为事件定义:
// 回调的总结构体
type ConnectorCallbackEvent struct {
Header ConnectorCallbackEventHeader `json:"header,omitempty"`
Event json.RawMessage `json:"event,omitempty"`
}
// 事件类型
type ConnectorCallbackEventType string
const (
// 发布类型
ConnectorCallbackEventTypeBotPublish ConnectorCallbackEventType = "bot.published"
)
// 事件 header
type ConnectorCallbackEventHeader struct {
EventType ConnectorCallbackEventType `json:"event_type"` // 通用事件类型
EventID string `json:"event_id"` // 事件 ID
CreatedAt int64 `json:"created_at"` // 事件发生的UTC时间(毫秒)
}
// 根据 eventType 返回不同的结构
type BotPublishedEvent struct {
UserID int64 `json:"user_id,omitempty"` // 用户 ID
BotID int64 `json:"bot_id,omitempty"` // 机器人 ID
BotName string `json:"bot_name,omitempty"` // 机器人名称
Description string `json:"description,omitempty"` // 机器人描述
}
// 返回结构
type ConnectorCallbackEventResponse struct {
Audit Audit `json:"audit,omitempty"` // 审核
ShareLink string `json:"share_link,omitempty"` // 分享链接
}
type Audit struct{
AuditStatus AuditStatus `json:"audit_status,omitempty"` // 审核状态
Reason string `json:"reason,omitempty"` // 原因
}
type AuditStatus int32
const (
AuditStatus_Progress AuditStatus = 1 // 审核中
AuditStatus_Audited AuditStatus = 2 // 通过
AuditStatus_Reject AuditStatus = 3 // 拒绝
)
订阅回调
智能体发布回调事件
智能体删除回调事件
智能体下架回调事件
账单推送回调事件
申请插件扩容回调事件
插件扩容到期回调事件