# 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

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

Slack's current upload flow is:

1. call `files.getUploadURLExternal`
2. upload the file bytes to the returned `upload_url`
3. call `files.completeUploadExternal`

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