---
title: "Slack Events"
method: POST
path: "/api/slack/events"
tags: ["slack"]
---

# Slack Events

`POST /api/slack/events`

Slack Events API request URL.

Ordering is deliberate and must not be reordered:
  1. Verify X-Slack-Signature (and reject a stale timestamp) against the
     RAW body, before any JSON parsing.
  2. Handle url_verification (Slack's one-time endpoint handshake).
  3. For a DM, app_mention, or assistant_thread_started event, resolve
     team_id -> the org that installed this workspace (a READ --
     required because `agent_background_jobs.org_id`
     is a NOT NULL FK, so there is no way to enqueue anything without it;
     a read has no "succeeded but orphaned" failure mode, unlike a write,
     so it is safe to keep ahead of the ACK). Everything else that is a
     genuine judgement call -- the slack_agent_app gate, Slack
     identity-link resolution -- stays in the worker
     (services/job_processor.py:_process_slack_agent_turn_job /
     _process_slack_assistant_thread_started_job), never here.
  4. Acknowledge with 200 after exactly ONE pre-ACK WRITE: an idempotent
     job insert (`AgentBackgroundJobService.create_background_job_idempotent`,
     dedupe_key=event_id) that carries the event-dedup uniqueness itself.
     A duplicate Slack delivery's insert simply conflicts and returns the
     existing job row -- no separate dedup write exists that a later
     failure could orphan (see the migration's design-note comment for
     the prior two-write design this replaced and why it lost events).
     If the insert raises, we do NOT ACK (500) -- nothing was durably
     written, so Slack's retry is the correct, safe recovery path.

## Response `200`

Successful Response

- unknown

---

[API](https://skmtc.net/every/apis/every-api.md) · [All operations](https://skmtc.net/every/apis/every-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/every/every-api/versions/9744001af85d/schema)
