# `Slink.EventsApi.Plug`
[🔗](https://github.com/wkirschbaum/slink/blob/v0.6.0/lib/slink/events_api/plug.ex#L1)

Events API transport: a `Plug` that receives Slack's HTTP event callbacks.

It verifies Slack's request signature, answers the `url_verification`
challenge, responds `200` immediately, and dispatches the event off-process
(so your handler can't blow Slack's ~3s response budget).

Mount it in a router:

    forward "/slack/events", to: Slink.EventsApi.Plug,
      init_opts: [
        module: MyBot,
        signing_secret: System.fetch_env!("SLACK_SIGNING_SECRET"),
        bot_token: System.fetch_env!("SLACK_BOT_TOKEN")
      ]

Or run it standalone with Bandit:

    Bandit.start_link(
      plug: {Slink.EventsApi.Plug,
             module: MyBot,
             signing_secret: System.fetch_env!("SLACK_SIGNING_SECRET"),
             bot_token: System.fetch_env!("SLACK_BOT_TOKEN")},
      port: 4000
    )

Options:

  * `:module` (required) — a module implementing the `Slink` behaviour.
  * `:signing_secret` (required) — the app's Signing Secret, for request
    verification. A string, or a 0-arity function returning one (resolved per
    request — see *Mounting in Phoenix*).
  * `:bot_token` — bot token (`xoxb-…`) passed to handlers for Web API calls.
    A string, a 0-arity function, or — for a multi-workspace app — a 1-arity
    function called with the event's team id (see *Multiple workspaces*).

## Multiple workspaces

One app can serve many workspaces from a single endpoint. The signing secret
is per-*app*, so it stays one value; only the bot token differs per install.
Pass a 1-arity `:bot_token` — it's called with the event's team id
(`Slink.Event.team_id/1`), so you look the right token up from your own store:

    forward "/slack/events", to: Slink.EventsApi.Plug,
      init_opts: [
        module: MyBot,
        signing_secret: fn -> System.fetch_env!("SLACK_SIGNING_SECRET") end,
        bot_token: fn team_id -> MyApp.Installs.bot_token(team_id) end
      ]

Acquiring those per-team tokens is handled by `Slink.OAuth` (the "Add to
Slack" consent URL, code exchange, and callback plug); storing them stays
yours — Slink routes to whatever your `:bot_token` resolver returns.

## Mounting in Phoenix

Two pitfalls when mounting inside a Phoenix app:

  * **`forward` options are evaluated at compile time** in production, so
    `System.fetch_env!/1` in the router would read the env var on the *build*
    machine (or fail there). Pass functions instead — they're resolved per
    request:

        forward "/slack/events", to: Slink.EventsApi.Plug,
          init_opts: [
            module: MyBot,
            signing_secret: fn -> System.fetch_env!("SLACK_SIGNING_SECRET") end,
            bot_token: fn -> System.fetch_env!("SLACK_BOT_TOKEN") end
          ]

  * **The raw body must still be readable.** Signature verification hashes the
    raw request body, but `Plug.Parsers` (in every generated endpoint) consumes
    it before the router runs — mounted after it, every request 401s. Mount
    this plug in `endpoint.ex` *before* `plug Plug.Parsers`, or run it as a
    standalone listener (see above).

## Slash commands & interactivity

Slack delivers those as `application/x-www-form-urlencoded`, not JSON; this
plug decodes both. Point the app's **Request URL** for *Interactivity* and
*Slash Commands* at this same endpoint. Slash commands and most interactions
are handled off-process like events, and your handler replies via the
`response_url` (see `Slink.reply/3`).

The one exception is `view_submission` (a modal submit): Slack expects a
`response_action` in the immediate reply. For that type the handler runs
synchronously and its `{:ack, map}` return is sent back as the response — so
keep it fast (Slack's window is ~3s). Returning anything else closes the modal.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
