You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vapi.ai ListenURL WebSocket连接报400错误,请求明确需携带的API Key

Vapi.ai Listen WebSocket 400错误:请求头需添加的API Key说明

问题概述

根据Vapi.ai实时通话控制文档,通过特定通话ID可获取listenURL,但使用该URL连接WebSocket时返回400 Bad Request错误,错误详情如下:

HTTP Status: 400 Bad Request 2025/01/02 14:36:09 HTTP Headers: map[Alt-Svc:[h3=":443"; ma=86400] Cf-Cache-Status:[DYNAMIC] Cf-Ray:[8fb99766dbf554a1-DEL] Connection:[keep-alive] Content-Length:[30] Content-Type:[application/json] Date:[Thu, 02 Jan 2025 09:06:10 GMT] Server:[cloudflare] Set-Cookie:[_cfuvid=FOE3xNcNAAQ9nAbYpUexjFBnvXPCcFLTmPQwo8qzv8c-1735808770005-0.0.1.1-604800000; path=/; domain=.vapi.ai; HttpOnly; Secure; SameSite=None]] 2025/01/02 14:36:09 Error connecting to WebSocket server: websocket: bad handshake

需要明确请求头中需携带的API Key类型,以成功建立连接并处理实时通话流。

解决方案

你需要在WebSocket握手请求头中添加Vapi.ai的主API密钥,格式为:

Authorization: Bearer YOUR_VAPI_API_KEY

这个密钥可在Vapi.ai控制台生成,用于验证身份权限,确保只有合法用户能访问通话流数据。

修改后的Go代码示例

将代码中注释的授权头部分取消注释,并替换为实际的API密钥:

func LiveCallStreamingWebsocket(serverURL string) {
    // 设置授权请求头
    header := http.Header{}
    header.Add("Authorization", "Bearer YOUR_VAPI_API_KEY") // 替换为你的实际API密钥

    // 连接WebSocket服务器
    c, resp, err := websocket.DefaultDialer.Dial(serverURL, header)
    if err != nil {
        if resp != nil {
            log.Printf("HTTP Status: %s", resp.Status)
            log.Printf("HTTP Headers: %v", resp.Header)
            // 建议读取响应体查看具体错误信息
            body, _ := io.ReadAll(resp.Body)
            log.Printf("Response Body: %s", body)
        }
        log.Fatalf("Error connecting to WebSocket server: %v", err)
    }
    defer c.Close()

    // 处理系统中断信号,优雅关闭
    interrupt := make(chan os.Signal, 1)
    signal.Notify(interrupt, os.Interrupt)

    // 连接关闭信号通道
    done := make(chan struct{})

    // 存储PCM数据的缓冲区
    var pcmBuffer []byte

    // 协程读取WebSocket消息
    go func() {
        defer close(done)
        for {
            messageType, message, err := c.ReadMessage()
            if err != nil {
                log.Println("Error reading message:", err)
                return
            }
            switch messageType {
            case websocket.BinaryMessage:
                pcmBuffer = append(pcmBuffer, message...)
                log.Printf("Received PCM data, buffer size: %d\n", len(pcmBuffer))
            case websocket.TextMessage:
                var msg interface{}
                if err := json.Unmarshal(message, &msg); err != nil {
                    log.Println("Error unmarshalling message:", err)
                    continue
                }
                log.Println("Received message:", msg)
            }
        }
    }()

    // 等待中断信号或连接关闭
    for {
        select {
        case <-done:
            return
        case <-interrupt:
            log.Println("Interrupt received, closing connection...")

            // 发送关闭消息
            err := c.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
            if err != nil {
                log.Println("Error during closing WebSocket:", err)
                return
            }

            // 保存PCM数据到文件
            if len(pcmBuffer) > 0 {
                err := os.WriteFile("audio.pcm", pcmBuffer, 0644)
                if err != nil {
                    log.Println("Error writing PCM data to file:", err)
                } else {
                    log.Println("Audio data saved to audio.pcm")
                }
            }

            select {
            case <-done:
            case <-time.After(time.Second):
            }
            return
        }
    }
}

额外排查提示

  • 确认API密钥是否有效,未过期且具备访问通话流的权限
  • 检查Authorization头格式:Bearer与密钥之间必须有一个空格
  • 打开代码中读取响应体的注释,查看服务器返回的具体错误信息,有助于快速定位问题

内容的提问来源于stack exchange,提问作者MOHD TARIK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 05:34:53