A thin Slack Web API client built on Req.
Slack Web API methods are POSTs to https://slack.com/api/<method> with a
bearer token. Note that Slack returns HTTP 200 even on logical failures — the
real status is in the "ok" field of the JSON body, which the helpers here
surface as {:error, reason}.
Genuine rate limiting is different: Slack answers with HTTP 429 and a
Retry-After header. call/3 retries those automatically, honouring the
header, so a burst backs off instead of dropping. Only 429 is retried —
messages aren't idempotent, so we never re-POST on a transport error or a
5xx.
Tokens:
apps.connections.openneeds an app-level token (xapp-…).- Everything else (e.g.
chat.postMessage) needs a bot token (xoxb-…).
Summary
Functions
Add an emoji reaction (name, no colons) to the message at channel/timestamp
via reactions.add. Needs the reactions:write scope.
Call any Web API method. Returns {:ok, body} when Slack replies ok: true,
otherwise {:error, reason} (the Slack error string, or a transport error).
Delete a message with chat.delete. Needs the chat:write scope.
Get a permalink to the message at channel/timestamp via chat.getPermalink.
Open a Socket Mode WebSocket URL via apps.connections.open.
Open a modal with views.open.
Post a message only user can see, with chat.postEphemeral.
Post a message with chat.postMessage.
Publish a Home tab view for user with views.publish.
Push a new modal onto the stack of an open modal with views.push.
Remove a reaction added with add_reaction/4, via reactions.remove.
Reply to a slash command or interaction via its response_url.
Update an existing message with chat.update.
Replace the contents of an open modal with views.update.
Look up a user's profile with users.info. Needs the users:read scope.
Functions
Add an emoji reaction (name, no colons) to the message at channel/timestamp
via reactions.add. Needs the reactions:write scope.
Call any Web API method. Returns {:ok, body} when Slack replies ok: true,
otherwise {:error, reason} (the Slack error string, or a transport error).
Delete a message with chat.delete. Needs the chat:write scope.
Get a permalink to the message at channel/timestamp via chat.getPermalink.
Returns {:ok, url}.
Open a Socket Mode WebSocket URL via apps.connections.open.
Pass an app-level token (xapp-…) with the connections:write scope.
Open a modal with views.open.
trigger_id comes from the interaction that opened it (see
Slink.Event.trigger_id/1) and is only valid for ~3 seconds, so open the
modal promptly. view is a Block Kit view payload (a map).
Post a message only user can see, with chat.postEphemeral.
Shows up for that one user in channel and vanishes on reload — good for
private acknowledgements. opts is merged into the body. Needs chat:write.
Post a message with chat.postMessage.
opts is merged into the request body (e.g. %{thread_ts: ..., blocks: ...}).
Publish a Home tab view for user with views.publish.
This is what populates a bot's App Home tab; call it from an
:app_home_opened handler. Needs the App Home tab enabled for the app.
Push a new modal onto the stack of an open modal with views.push.
Remove a reaction added with add_reaction/4, via reactions.remove.
Reply to a slash command or interaction via its response_url.
Slash commands and interactive payloads carry a short-lived response_url
(valid ~30 minutes, up to 5 uses) — see Slink.Event.response_url/1. Unlike
the Web API methods this is a plain POST to that URL with no bearer token.
params is the message body, e.g. %{response_type: "ephemeral", text: "…"}
("ephemeral" — only the invoker sees it — or "in_channel"), optionally with
blocks, replace_original, or delete_original.
Update an existing message with chat.update.
timestamp is the ts of the message to edit. opts is merged into the body
(e.g. %{blocks: ...}). Needs the chat:write scope.
Replace the contents of an open modal with views.update.
view_id is the id of the view returned when it was opened.
Look up a user's profile with users.info. Needs the users:read scope.