Slack OAuth
JsWorkflows provides a platform-managed Slack OAuth app. You do not need to create your own Slack app or manage client credentials.
Available scopes
Section titled “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
Section titled “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.order_number}: ${data.total_price} ${data.currency}`, blocks: [ { type: 'section', text: { type: 'mrkdwn', text: `*New order* #${data.order_number}: ${data.total_price} ${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 || 'unknown_error'}`); } }}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
Section titled “Example: upload a file”Slack’s current upload flow is:
- call
files.getUploadURLExternal - upload the file bytes to the returned
upload_url - 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 || 'unknown_error'}`);}
const uploadResponse = await fetch(initResult.upload_url, { 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.file_id, title: 'report.csv' }], channel_id: 'C0123456789', initial_comment: '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 || 'unknown_error'}`);}For file sharing, use channel_id or channels with Slack IDs. Do not use #channel-name in the file upload completion request.