Skip to content

Integrations

Slack SDK: get started

The Slack Web API through typed application or agent clients

Use an ordinary App, or an Agent when the workload needs agent identity. Alter builds provider requests from a reviewed contract and applies the same connection authorization, policy, approvals, and audit records as raw requests.

Terminal window
npm install @alter-ai/sdk @alter-ai/slack
# Python (installs core automatically)
pip install alter-sdk-slack

The provider package includes only Slack types and response schemas and pins its contract version.

Configure Slack, request the scopes the application needs, and retain each customer’s grant ID after Connect consent. A workspace that installs its own Slack app can instead store the app’s bot token as a Slack managed secret and use that secret’s grant ID: the same methods, policy and approvals apply to both. Alter supplies the credential; it never belongs in operation input.

import { App } from "@alter-ai/sdk";
import { SlackIntegration } from "@alter-ai/slack";
const app = new App({ apiKey: process.env.ALTER_API_KEY! });
try {
const client = new SlackIntegration(app, { grantId: process.env.SLACK_GRANT_ID! });
const result = await (await client.users.list({ query: { limit: 100 } })).wait();
console.log(result.bodyJson());
} finally {
await app.close();
}
from alter_sdk import App
from alter_sdk_slack import SlackIntegration
async def first_call(api_key: str, grant_id: str):
async with App(api_key=api_key) as app:
client = SlackIntegration(app, {"grant_id": grant_id})
call = await client.users.list({"query": {"limit": 100}})
result = await call.wait()
return result.body_json()

Contract 2026-10-28 contains the 80 Web API methods in Slack’s published OpenAPI description that a bot token can call, each corrected to Slack’s current method reference: its arguments and their types, which arguments are required, its HTTP method and its bot-token scopes. Four reads the description lists as GET (api.test, auth.test, calls.info, chat.scheduledMessages.list) stay GET, which Slack accepts on every method, although their pages show POST. The API reference links every operation to its complete input fields, response models, TypeScript and Python examples, and policy identity. Browse by resource under this provider’s API reference in the Integrations sidebar, or download the pinned OpenAPI specification. The Developer Portal provider view and alter integrations operations slack --version 2026-10-28 also expose the schemas.

SDK methods follow Slack’s method names: chat.postMessage is chat.postMessage in TypeScript and chat.post_message in Python, and files.remote.add is filesRemote.add / files_remote.add. Slack’s description was last updated in 2020 and lists fewer methods than Slack documents today. A newer method (for example files.getUploadURLExternal, bookmarks.add or the canvases methods) is not in this contract and remains callable as a raw request through the same connection.

Generated calls pin the Alter contract version. Slack’s own product, role, scope and plan requirements still apply. There is no silent version fallback and no automatic write retry or pagination.

Terminal window
alter integrations execute slack slack.chat_postmessage \
--version 2026-10-28 --grant "$SLACK_GRANT_ID" \
--input '{"body": {"channel": "C0123456789", "text": "Build finished"}}' --wait

Arguments use native query and body containers. A GET method (the reads, and files.remote.share, which Slack documents as GET) takes its arguments as query parameters. Every other method except files.remote.add and files.remote.update (see below) sends a form body (application/x-www-form-urlencoded), the one content type Slack accepts on every Web API method. Structured arguments (blocks, attachments, metadata, view, unfurls) are JSON text in a string field, exactly as Slack documents them (“presented as a URL-encoded string”), and booleans are sent as true / false. Message timestamps (ts, thread_ts, latest, oldest) are strings, so a timestamp keeps every digit that identifies its message. The connection’s token is sent in the Authorization header; token is never an argument. SDK methods preserve provider field names and large integers as strings when necessary.

A policy rule can require approval for any operation. The call returns a pending approval; wait() resolves once the approver decides, and resume(approvalId) picks a pending approval up after a restart. Approval cards show the operation, its contract version and the request input the sensitivity review marked safe to display.

files.remote.add and files.remote.update take two file inputs, preview_image and indexable_file_contents. Each is a JSON object with filename, content_type, and content_base64. Those two methods are sent as multipart parts: a file object is a file part, and an optional field left null is omitted. The shared proxy accepts up to 5 MiB of serialized input and encoded upload data; base64 adds overhead, so accepted file bytes are smaller than that ceiling.

Both file inputs are classified sensitive. Under a require_approval rule a remote-file call that carries one is held with the pending approval, and the approver sees each file masked as its file name and size. Approval pages show every other field exactly, and frozen execution uses the stored input and the original contract, rechecks current authority, and never replays an uncertain write.

Every write has a reviewed method/path family entry in the provider input; an unreviewed new write fails generation.

Every method that changes workspace state is write, and adds:

  • send: chat.postMessage, chat.postEphemeral, chat.scheduleMessage, chat.meMessage, chat.update, chat.unfurl and files.remote.share, which put a message, an edited message, a link preview or a file in front of people.
  • admin: conversations.invite, conversations.kick and usergroups.users.update, which change who is in a conversation or a user group.
  • delete: chat.delete, chat.deleteScheduledMessage, files.delete, files.comments.delete, files.remote.remove and pins.remove.
  • write alone: every other method that changes state, including the views.* methods and dialog.open.

The 29 published operations Alter cataloged before this publication keep their operation IDs and the names of the parameters they projected, so rules naming them, including rules on a message’s channel, still match. Their families and types follow this publication’s review: the message posts, deletes and membership changes are also write, chat.update is also send, and the comma-separated users of conversations.invite and conversations.open is judged one ID at a time. Some gained parameters (markdown_text, metadata and the destination arguments), and a rule on text now fails closed when the request also carries markdown_text (see below).

Slack answers most failures with HTTP 200 and {"ok": false, "error": "..."}. Every operation treats ok: false as a provider fault: ok is false and bodyJson() / body_json() raises ProviderAPIError, so a failed call is never read as a success.

Payload capture follows the application’s capture setting, on managed and raw proxied calls alike: credentials are removed, every field Alter’s reviewed inventory for the provider marks sensitive is masked in requests and responses, including fields it locates only by operation, and binary bodies, calls that can return a credential and calls carrying raw card data are never stored. Metadata audit is unchanged. The two remote-file uploads are held with a pending approval and shown masked; Slack-held IDs, timestamps, URLs and flags are shown as sent.

Alter’s Slack connection holds a bot token, so methods Slack documents for user tokens only are not published: the Enterprise admin.* methods, search.messages, the stars.* methods, the Do Not Disturb snooze methods, users.profile.set, users.setPhoto, users.deletePhoto, users.identity, files.sharedPublicURL and the team access, billing and integration logs. Nor are rtm.connect (classic apps only), apps.event.authorizations.list (an app-level token), or the methods Slack has retired: files.upload, users.setActive, the reminders.* methods, the workspace-app apps.permissions.* methods and oauth.token, and the workflow step methods. Slack’s token, revocation and uninstall endpoints are not operations. A raw request to them is refused by policy as credential minting, and so is a raw POST whose path cannot be read well enough to rule those endpoints out.

Each operation lists the bot-token scopes Slack documents for it. Where it lists several, any one suffices at Slack: the conversation methods take the scope of the conversation’s type (channels:history for a public channel, groups:history for a private one, im:history and mpim:history for direct messages). On a grant narrowed to fewer scopes than its connection holds, a method with one documented scope is allowed when the grant keeps that scope, and a read that lists no scope is allowed. A method whose scope depends on the conversation type is refused, because the conversation’s type is not visible in the request path, and so is a write that lists no scope (views.open, views.publish, views.push, views.update and dialog.open).

The pinned official source, its hash, the generated methods and the policy mapping are checked together. Scheduled checks save upstream candidates and report additions, removals and schema changes; they never replace a published contract automatically. Existing contract versions remain available for installed SDKs and pending approvals.

Slack’s published description is a 2020 document in a repository Slack has since archived, so the scheduled check of that source will not report changes Slack makes today. The corrections Alter applies from Slack’s method reference were captured with this contract and are part of it; the method pages themselves are not watched, and a reviewed change to them is published as a new contract version.

Persist call.approvalId (TypeScript) or call.approval_id (Python), then construct a fresh provider client and call resume(savedId), followed by wait(). The stored approval selects the operation; this installed package verifies its provider and contract version before attaching the response validator. Mismatches raise ManagedOperationError, never silently decode using another contract. See persist-and-resume examples.

Policy operation fields also accept the published SDK function IDs listed by alter integrations operations slack. Use when.operation for a json_match or conditional require_approval rule, or match.operations for content_match (including redaction and step-up). Conditional approval can also use when.match.operations with parameter conditions. alter policy simulate --operation <function-id> tests that exact function.

Managed calls carry a server-verified function identity. Alter still evaluates policy against the actual method, URL and body after building the request from the authorized connection. Existing endpoint, method, host, catalog-operation and family rules continue to apply unchanged. A raw call can share an endpoint with several SDK functions and matches all of them; a function name never grants permission or bypasses existing restrictions.

For example, this rule requires approval for every message the application posts:

{
"effect": "require_approval",
"when": { "operation": "slack.chat_postmessage" },
"approval": { "approvers": ["reviewer@example.com"] }
}

Save it with alter policy rules create --app <app-id> --grant <grant-id> --type require_approval --body @rule.json, or select the function in the portal.

A message can carry its content in markdown_text instead of text. When a request carries markdown_text, a rule that names text cannot judge the message and fails closed, and a redaction of text refuses the request. Name markdown_text in its own rule to judge or redact that content.

Slack reads channels and users as comma-separated lists. Rules judge each listed ID on its own, so any_in with one channel ID matches a request that lists it among others. The Calls methods’ users is a list of participant objects instead, so it is not available to rules there, and a rule that names it on those methods fails closed.

Report an issue with this page

Necessary

Required for sign-in, security, authorization, and remembering your choices.

Always active

Analytics

Helps us understand which product and documentation features are useful.

Performance diagnostics

Uses performance tracing and privacy-masked session replay to diagnose problems.

You can change these choices at any time from Cookie settings.