Slack OAuth Connect Slack to send messages, read channels, and manage files. JsWorkflows provides a platform-managed Slack OAuth app. You do not need to create your own Slack app or manage client credentials. Available scopes | Resource | Operation | Scope | | Messaging | Send message | chat:write, users:read | | Messaging | Read messages | channels:history, groups:history, im:history, mpim:history | | Users | Get user info / list users | users:read | | Channels | List channels | channels:read | | Channels | Join channel | channels:join | | Files | Upload file | files:write | | Files | Read files | files:read | | Reactions | Add/read reactions | reactions:write, reactions:read | Example: post a message export class Workflow { async start(data, headers, api) { const { token, error } = await api.getOAuthToken('my-slack'); if (error | | !token) throw new Error(error | | 'Missing Slack token'); const response = await fetch('https://slack.com/api/chat.postMessage', { method: 'POST', headers: { 'Content-Type': 'application/json; charset=utf-8', Authorization: Bearer ${token}, }, body: JSON.stringify({ channel: 'C0123456789', text: New order #${data.ordernumber}: ${data.totalprice} ${data.currency}, blocks: [ { type: 'section', text: { type: 'mrkdwn', text: New order #${data.ordernumber}: ${data.totalprice} ${data.currency}, }, }, ], }), }); if (!response.ok) { throw new Error(Slack HTTP ${response.status}: ${await response.text()}); } const result = await response.json(); if (!result.ok) { throw new Error(Slack API error: ${result.error | | 'unknownerror'}); } } } Use a channel ID when possible. It is the safest value to store in workflow config and pass to Slack APIs. Example: upload a file Slack's current upload flow is: 1. call files.getUploadURLExternal 2. upload the file bytes to the returned uploadurl 3. call files.completeUploadExternal const { token, error } = await api.getOAuthToken('my-slack'); if (error | | !token) throw new Error(error | | 'Missing Slack token'); const csvString = 'sku,available\\nABC-123,42\\n'; const fileBytes = new TextEncoder().encode(csvString); const initResponse = await fetch('https://slack.com/api/files.getUploadURLExternal', { method: 'POST', headers: { 'Content-Type': 'application/json; charset=utf-8', Authorization: Bearer ${token}, }, body: JSON.stringify({ filename: 'report.csv', length: fileBytes.byteLength, }), }); if (!initResponse.ok) { throw new Error(Slack HTTP ${initResponse.status}: ${await initResponse.text()}); } const initResult = await initResponse.json(); if (!initResult.ok) { throw new Error(Slack API error: ${initResult.error | | 'unknownerror'}); } const uploadResponse = await fetch(initResult.uploadurl, { method: 'POST', headers: { 'Content-Type': 'text/csv; charset=utf-8' }, body: fileBytes, }); if (!uploadResponse.ok) { throw new Error(Slack upload failed: ${uploadResponse.status} ${await uploadResponse.text()}); } const completeResponse = await fetch('https://slack.com/api/files.completeUploadExternal', { method: 'POST', headers: { 'Content-Type': 'application/json; charset=utf-8', Authorization: Bearer ${token}, }, body: JSON.stringify({ files: [{ id: initResult.fileid, title: 'report.csv' }], channelid: 'C0123456789', initialcomment: 'Daily inventory report', }), }); if (!completeResponse.ok) { throw new Error(Slack HTTP ${completeResponse.status}: ${await completeResponse.text()}); } const completeResult = await completeResponse.json(); if (!completeResult.ok) { throw new Error(Slack API error: ${completeResult.error | | 'unknownerror'}); } For file sharing, use channelid or channels with Slack IDs. Do not use #channel-name in the file upload completion request.