Slack setup
SkillCommunicationSet up or verify Slack in Backoffice for bot-token API calls, sending messages, Events API webhooks, app mentions, URL verification, signing-secret validation, or checking whether a Slack event arrived.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Slack setup skill
What this skill tells your AI
The instructions your AI receives, as published by rejot-dev/fragno in apps/backoffice/content/marketplace/slack-setup/versions/1.0.0/skills/slack-setup/SKILL.md and read by ahel’s review.
Set Slack up as one handshake: connect → verify → receive → prove.
1. Inspect existing API connections
The API capability is available automatically for the current scope. Inspect the existing outbound API connections before creating or updating the Slack connection.
Complete when the existing outbound connections are known.
2. Collect missing secrets
Collect secrets through a durable Backoffice UI rather than chat. Define a workflow and return this
exact shape from a completed step.do, including only the controls for secrets still missing:
await step.do("request Slack credentials", async () => ({
$ui: {
version: 1,
state: { response: { botUserOAuthToken: "", signingSecret: "" } },
spec: {
root: "form",
elements: {
form: {
type: "Stack",
props: { gap: "md" },
children: ["bot-token", "signing-secret", "submit"],
},
"bot-token": {
type: "TextInput",
props: {
label: "Bot User OAuth Token",
description: "Slack token beginning with xoxb-.",
value: { $bindState: "/response/botUserOAuthToken" },
required: true,
secret: true,
},
children: [],
},
"signing-secret": {
type: "TextInput",
props: {
label: "Signing Secret",
description: "Slack App → Basic Information → App Credentials.",
value: { $bindState: "/response/signingSecret" },
required: true,
secret: true,
},
children: [],
},
submit: {
type: "WorkflowEventButton",
props: {
label: "Connect Slack",
eventType: "slack.credentials-submitted",
payload: { $state: "/response" },
},
children: [],
},
},
},
},
}));
const credentials = await step.waitForEvent("Slack credentials", {
type: "slack.credentials-submitted",
});
Use credentials.payload.botUserOAuthToken and credentials.payload.signingSecret only inside
later step.do calls. Keep them out of workflow output, tool summaries, and prose.
Complete when the workflow has received every missing secret through
slack.credentials-submitted.
3. Connect the Slack bot
Use an existing active API connection whose slug is slack. Otherwise use the submitted Bot User
OAuth Token to create this connection:
await api.createConnection({
slug: "slack",
name: "Slack",
baseUrl: "https://slack.com/api",
auth: { type: "bearer", token: botUserOAuthToken },
});
Treat tokens as secrets: use the exact user-supplied value only in the tool call and omit it from
results and prose. Verify the connection with api.getAuthStatus, then call Slack's
POST /auth.test with an empty JSON body. Slack API calls can return HTTP 200 for failures, so
require both HTTP 200 and body.ok === true.
If verification fails, report Slack's error value and stop. Never replace a missing token with a
placeholder or test credential.
Complete when authentication is active and auth.test returns ok: true.
4. Send messages
Send with POST /chat.postMessage and a JSON body containing channel and text:
await api.request({
slug: "slack",
method: "POST",
path: "/chat.postMessage",
headers: { Accept: "application/json", "Content-Type": "application/json" },
json: { channel, text },
timeoutMs: 30000,
});
Use the channel identifier exactly as the user supplied it. Make discovery calls such as
conversations.list only when the user asks to resolve or browse channels. Require
body.ok === true and report Slack's error otherwise.
Complete when Slack confirms the message with ok: true.
5. Configure inbound Slack events
Inspect before writing:
const existing = await api.getWebhookEndpoint({ endpointId: "slack" });
Use this Slack challenge configuration:
const verification = {
type: "challenge",
method: "POST",
when: {
type: "equals",
source: { type: "jsonBodyPath", path: ["type"] },
value: "url_verification",
},
response: {
type: "echoText",
source: { type: "jsonBodyPath", path: ["challenge"] },
},
};
const deliveryIdentity = { type: "jsonBodyPath", path: ["event_id"] };
When the endpoint is absent, create it as a draft. createWebhookEndpoint replaces the complete
resource, so use it only in this branch:
await api.createWebhookEndpoint({
endpointId: "slack",
name: "Slack",
status: "draft",
verification,
deliveryIdentity,
auth: { type: "none" },
});
When the endpoint exists, preserve its stored secrets and unrelated configuration. Use
updateWebhookEndpoint and include only fields that must change. In particular, omit auth when
the existing authConfig is already Slack-compatible HMAC; omission preserves the stored signing
secret:
await api.updateWebhookEndpoint({
endpointId: "slack",
verification,
deliveryIdentity,
});
A Slack-compatible existing HMAC configuration has all of these values:
type: "hmac"andalgorithm: "sha256"- signature header
x-slack-signature, hex encoding, and prefixv0= - timestamped body prefix
v0:, headerx-slack-request-timestamp, delimiter:, and a 300-second tolerance - at least one stored
secretRef
Collect a Signing Secret only when creating the endpoint or replacing absent/incompatible HMAC.
Activate or repair it with one patch containing the necessary changed fields and this auth value:
await api.updateWebhookEndpoint({
endpointId: "slack",
status: "active",
verification,
deliveryIdentity,
auth: {
type: "hmac",
secret: signingSecret,
algorithm: "sha256",
signature: {
location: "header",
name: "x-slack-signature",
encoding: "hex",
prefix: "v0=",
},
signedPayload: {
type: "timestampedBody",
prefix: "v0:",
timestampHeader: "x-slack-request-timestamp",
delimiter: ":",
toleranceSeconds: 300,
},
},
});
Re-read the endpoint after every write. Give its publicUrl to the user for Slack App → Event
Subscriptions → Request URL. Tell the user to subscribe to the app_mention bot event, grant
app_mentions:read and chat:write, reinstall the Slack app when scopes change, and retry Request
URL verification.
Complete when the re-read endpoint is active with Slack challenge handling, event_id delivery
identity, and timestamped HMAC verification. Never report an existing setup as verified from status
alone.
6. Prove event delivery
When the user asks whether a Slack message arrived, call:
await hooks.list({ fragment: "api", pageSize: 10 });
Find the newest completed onWebhookReceived entry whose payload has endpointId: "slack". Confirm
receipt from its parsed body.event, including event type, channel, and message text when present.
Keep authorization tokens, signatures, raw headers, and unrelated payload fields out of the
response.
A received event proves ingestion only. State clearly when no automation exists yet to reply.
Sending a reply requires chat.postMessage, normally using body.event.channel; use
body.event.ts as thread_ts only when the requested reply should be threaded.
Complete when the newest matching delivery is identified, or the absence of one is reported.
Signals
- GitHub stars
- 61
- Forks
- 6
- Last commit
- Sep 2026
ahel review
S4info
community integration — published by rejot-dev, not slack
Automated review, not a security audit. Ruleset v1.
Advanced
- Catalog kind
- skill
- Gateway key
slack-setup- Source
- github.com/rejot-dev/fragno