diff --git a/apps/server/src/api/v2/slack/index.ts b/apps/server/src/api/v2/slack/index.ts index 37f70a8a9..bfd8e6bc1 100644 --- a/apps/server/src/api/v2/slack/index.ts +++ b/apps/server/src/api/v2/slack/index.ts @@ -16,9 +16,37 @@ const app = new Hono() .delete('/integration', requireAuth, (c) => slackHandler.removeIntegration(c)) // Events endpoint (no auth required for Slack webhooks) .post('/events', async (c) => { - const body = await c.req.json(); - const response = await eventsHandler(body); - return c.json(response); + try { + // Slack sends different content types for different events + // For URL verification, it's application/x-www-form-urlencoded + // For actual events, it's application/json + const contentType = c.req.header('content-type'); + + if (contentType?.includes('application/x-www-form-urlencoded')) { + // Handle URL verification challenge + const formData = await c.req.parseBody(); + if (formData.challenge) { + return c.text(formData.challenge as string); + } + } + + // For JSON payloads, try to parse but don't fail + let body = null; + if (contentType?.includes('application/json')) { + try { + body = await c.req.json(); + } catch { + // If JSON parsing fails, just continue + } + } + + const response = await eventsHandler(body); + return c.json(response); + } catch (error) { + // Log the error but always return 200 OK for Slack + console.error('Error processing Slack event:', error); + return c.json({ success: true }); + } }) // Error handling .onError((e, c) => {