# `Slink.API`
[🔗](https://github.com/wkirschbaum/slink/blob/v0.6.0/lib/slink/api.ex#L1)

A thin Slack Web API client built on `Req`.

Slack Web API methods are `POST`s 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.open` needs an **app-level** token (`xapp-…`).
  * Everything else (e.g. `chat.postMessage`) needs a **bot** token (`xoxb-…`).

# `add_reaction`

Add an emoji reaction (`name`, no colons) to the message at `channel`/`timestamp`
via `reactions.add`. Needs the `reactions:write` scope.

# `append_stream`

Append markdown to a streaming message with `chat.appendStream`.

`ts` is the streamed message's timestamp from `start_stream/4`.
`markdown_text` is capped at 12,000 characters per call by Slack.

# `auth_test`

Who this token authenticates as, via `auth.test`.

The body's `"user_id"` is the bot's own user id (`Slink.Identity` caches this
to power `context.bot_user_id` and `Slink.mentions_me?/1`).

# `call`

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_message`

Delete a message with `chat.delete`. Needs the `chat:write` scope.

# `get_permalink`

Get a permalink to the message at `channel`/`timestamp` via `chat.getPermalink`.

Returns `{:ok, url}`.

# `history`

Fetch one page of a channel's message history with `conversations.history`.

`opts` is merged into the body — most usefully `%{limit: ..., cursor: ...}`
for paging (the next cursor is in `body["response_metadata"]["next_cursor"]`).
Needs the relevant `*:history` scope for the conversation type.

# `join_channel`

Join a public channel with `conversations.join`. Needs the `channels:join` scope.

# `oauth_access`

Exchange an OAuth `code` for a workspace's tokens, via `oauth.v2.access`.

This is the round-trip behind an "Add to Slack" install (see `Slink.OAuth`).
Unlike other Web API methods it authenticates with the app's client
credentials and a form-encoded body — no bearer token. `redirect_uri` must
match the one used in the authorize URL, when one was used there.

# `open_connection`

Open a Socket Mode WebSocket URL via `apps.connections.open`.

Pass an app-level token (`xapp-…`) with the `connections:write` scope.

# `open_dm`

Open (or resume) a direct message with `user` via `conversations.open`.

Returns `{:ok, channel_id}` — the DM channel to post into (see
`Slink.send_dm/4` for the one-call version). Needs the `im:write` scope.

# `open_view`

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_ephemeral`

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_message`

Post a message with `chat.postMessage`.

`opts` is merged into the request body (e.g. `%{thread_ts: ..., blocks: ...}`).

# `publish_view`

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_view`

Push a new modal onto the stack of an open modal with `views.push`.

# `remove_reaction`

Remove a reaction added with `add_reaction/4`, via `reactions.remove`.

# `respond`

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`.

# `schedule_message`

Schedule a message for later with `chat.scheduleMessage`.

`post_at` is a Unix timestamp (seconds). `opts` is merged into the body
(e.g. `%{thread_ts: ...}`). Needs the `chat:write` scope.

# `set_suggested_prompts`

Offer tappable prompts in an assistant thread, via
`assistant.threads.setSuggestedPrompts`.

`prompts` is a list of `%{title: ..., message: ...}` maps (the message is
what gets sent when tapped). `opts` merges into the body (e.g. `%{title:
"Try one of these"}` for the section header).

# `set_thread_status`

Show a status line ("is thinking…") in an assistant thread, via
`assistant.threads.setStatus`. Pass `""` to clear it.

# `set_thread_title`

Name an assistant thread, via `assistant.threads.setTitle`.

# `start_stream`

Start a streaming message with `chat.startStream`.

Streamed messages are always thread replies, so `thread_ts` is required.
Returns the started message in the body — its `"ts"` is what
`append_stream/4` and `stop_stream/4` take. When streaming into a *channel*
(not the app's DM), Slack requires `recipient_user_id` and
`recipient_team_id` in `opts`. See `Slink.stream_reply/3` for the high-level
helper.

# `stop_stream`

Finish a streaming message with `chat.stopStream`.

`opts` merges into the body — a final `markdown_text:` chunk, or `blocks:`
(allowed only here, not on start/append).

# `stream`

Lazily stream every page of a cursor-paginated Web API method.

Emits each page's whole body, fetching the next page on demand by following
`response_metadata.next_cursor` until Slack returns none. Extract the
method's list field to stream items:

    Slink.API.stream(token, "conversations.history", %{channel: "C123"})
    |> Stream.flat_map(& &1["messages"])
    |> Enum.take(500)

`params` gets `limit: 200` (Slack's recommended page size) unless you pass
your own. Rate limiting is handled the same as `call/3` — a `429` waits out
`Retry-After` and retries. A page fetch that still fails **raises**
`Slink.API.Error` (a lazy stream can't return an error tuple), so wrap
enumeration in a rescue if you need to handle it.

# `update_message`

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.

# `update_view`

Replace the contents of an open modal with `views.update`.

`view_id` is the id of the view returned when it was opened.

# `upload_file`

Upload a file in one call, hiding Slack's three-step external upload flow
(`files.getUploadURLExternal` → upload the bytes →
`files.completeUploadExternal`).

`content` is the file's binary content. Options (`:filename` required):

  * `:filename` — e.g. `"report.csv"`.
  * `:channel` — share the file into this channel; without it the file is
    uploaded private to the bot.
  * `:title` — display title (defaults to the filename on Slack's side).
  * `:initial_comment` — message text alongside the shared file.
  * `:thread_ts` — share into a thread.
  * `:alt_text` — image description for screen readers.
  * `:snippet_type` — syntax type for text snippets (e.g. `"elixir"`).

Returns `{:ok, body}` from the completing call (`body["files"]` holds the
file objects, ids included), or `{:error, reason}` from whichever step
failed. Needs the `files:write` scope.

# `user_info`

Look up a user's profile with `users.info`. Needs the `users:read` scope.

---

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