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

NestJS集成Twilio Conversation API时onMessageAdded Webhook未触发

NestJS集成Twilio Conversation API时onMessageAdded Webhook未触发问题

我用NestJS搭建后端,集成Twilio Conversation API开发实时聊天应用。已配置Webhook地址,期望聊天新增消息时触发onMessageAdded事件并通过POST请求调用该Webhook,同时使用ngrok做本地请求转发。目前消息可成功发送,但onMessageAdded事件始终无法触发后端接口,处于开发环境。


相关代码

启动代码(main.ts)

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.enableCors();

  const twilioChatService = app.get(TwilioChatService);
  await twilioChatService.setupConversationsWebhook(ENV['TWILIO_CHAT_SID'], 'https://lab-amazing-seahorse.ngrok-free.app/');

  app.useGlobalPipes(new ValidationPipe());

  app.useGlobalPipes(
    new ValidationPipe({
      whitelist: true,
    }),
  );

  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('api', app, document);

  app.useGlobalPipes(new ValidationPipe());
  app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector)));
  await app.listen(ENV['PORT'], () =>
    console.log(`Server is running on port ${ENV['PORT']}`),
  );
}
bootstrap();

TwilioChatService

private readonly twilioClient: Twilio;

constructor() {
    this.twilioClient = new Twilio(process.env.TWILIO_ACCOUNT_SID, process.env.TWILIO_AUTH_TOKEN);
}

async setupConversationsWebhook(conversationSid: string, webhookUrl: string) {
    try {
     this.twilioClient.conversations.v1.conversations(conversationSid).webhooks(ENV['TWILIO_WEBHOOK_SID']).update({
            'configuration.method': 'POST',
            'configuration.filters': ['onMessageAdded'],
            'configuration.url': webhookUrl,
        }).then(webhook => console.log(webhook.sid));

        console.log(`Conversations webhook set up at ${webhookUrl}`);
    } catch (error) {
        console.error('Error setting up Conversations webhook:', error.message);
    }
}

ChatController(chat.controller.ts)

import { Controller, Post, Body, Req, Get, Param } from '@nestjs/common';
import { ChatService } from './chat.service';

@Controller('chat')
export class ChatController {
    constructor(private readonly chatService: ChatService) { }

    @Post('webhook')
    async handleWebhook(@Body() eventData: any): Promise<any> {
        console.log(" === handleWebhook called ===");
        console.log("Received webhook event:", eventData);
        await this.chatService.handleWebhookEvent(eventData);
    }
}

ChatService(chat.service.ts)

async handleWebhookEvent(eventData: any): Promise<void> {
        const eventType = eventData.EventType;
        if (eventType === 'onMessageAdded') {
            console.log("Called onMessageSent()");
            const conversationSid = eventData.ConversationSid;
            const messageSid = eventData.MessageSid;
            // Send HTTP request when a new message is sent
            console.log("=== GOT the Message ===", messageSid);
            this.sendMessage(conversationSid, eventData.SenderIdentity, eventData.Body);
        }
    }

ChatModule(chat.module.ts)

@Module({
    imports: [TwilioModule.forRoot({ accountSid: process.env.TWILIO_ACCOUNT_SID, authToken: process.env.TWILIO_AUTH_TOKEN })],
    controllers: [ChatController],
    providers: [TwilioChatService, ChatService],
})
export class ChatModule { }

已确认的配置/日志

  • Twilio Webhook地址与请求方式配置正确
  • Twilio端已勾选onMessageAdded事件
  • 前端消息发送成功,Twilio对话消息记录正常
  • ngrok本地转发服务运行中

排查与解决方案

  1. 补全Webhook URL路径
    当前代码传入的Webhook URL为https://lab-amazing-seahorse.ngrok-free.app/,但Controller的路由是/chat/webhook,完整URL应为https://lab-amazing-seahorse.ngrok-free.app/chat/webhook。路径缺失会导致Twilio无法找到正确的接口端点。

  2. 确保Webhook更新操作成功
    原代码用.then()处理异步操作但未捕获更新失败场景,改用await并添加详细日志,确认Webhook是否更新成功:

    async setupConversationsWebhook(conversationSid: string, webhookUrl: string) {
        try {
            const webhook = await this.twilioClient.conversations.v1.conversations(conversationSid)
                .webhooks(ENV['TWILIO_WEBHOOK_SID'])
                .update({
                    'configuration.method': 'POST',
                    'configuration.filters': ['onMessageAdded'],
                    'configuration.url': webhookUrl,
                });
            console.log(`Webhook updated successfully, SID: ${webhook.sid}`);
            console.log(`Conversations webhook set up at ${webhookUrl}`);
        } catch (error) {
            console.error('Error setting up Conversations webhook:', error);
        }
    }
    
  3. 验证Twilio控制台的Webhook配置
    登录Twilio控制台,进入目标Conversation的Webhook设置页面,确认:

    • 事件过滤器明确包含onMessageAdded
    • 请求方法设置为POST
    • URL与代码中配置的完整路径一致
  4. 检查ngrok转发有效性
    手动访问https://lab-amazing-seahorse.ngrok-free.app/chat/webhook,确认后端能接收请求(可临时添加GET接口测试)。若无法访问,检查ngrok是否正常运行,或是否有防火墙/代理拦截请求。

  5. 查看Twilio Webhook失败日志
    登录Twilio控制台,进入Monitor > Events,筛选Webhook相关事件,查看是否有失败记录,根据错误信息(如超时、4XX/5XX状态码)定位问题。

  6. 调整ValidationPipe配置
    代码中重复添加ValidationPipe且开启whitelist: true,Twilio的Webhook请求体可能包含未定义的字段,导致请求被拦截。建议:

    • 移除重复的ValidationPipe配置
    • 为Webhook接口单独关闭白名单校验:
      @Post('webhook')
      @UsePipes(new ValidationPipe({ whitelist: false }))
      async handleWebhook(@Body() eventData: any): Promise<any> {
          // ...
      }
      

内容的提问来源于stack exchange,提问作者Muhammad Umair Qaisar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 06:12:04