# Schedule Slack messages and daily digests

Post to any Slack channel on a schedule: a standup digest, a reminder, a daily number, or an announcement. cronservices sends the message through an incoming webhook, so there's no bot to host, and it can fill the message in from any API first.

## Example: a daily Slack reminder

The simplest job: a message every weekday at 09:30 in your time zone.

```json
{
  "name": "Standup reminder",
  "cron": { "expression": "30 9 * * 1-5", "time_zone": "America/New_York" },
  "nodes": [
    { "id": "post", "slack_send_message": {
        "webhook_url": "https://hooks.slack.com/services/T000/B000/XXXX",
        "text": ":wave: Standup in 15 minutes. Post your update in the thread." } }
  ]
}
```

## Example: open pull requests before standup

Fetch data, turn it into lines, and post it. The `join` function writes a list as text, one item per line:

```json
{
  "name": "Open PRs to #eng",
  "cron": { "expression": "0 9 * * 1-5", "time_zone": "Europe/Berlin" },
  "nodes": [
    { "id": "prs", "github_list_pull_requests": { "credential_id": "YOUR_GITHUB_CREDENTIAL_ID", "owner": "acme", "repo": "web", "state": "open" } },
    { "id": "lines", "transform": { "template": "{ \"count\": \"{{ $.length }}\", \"lines\": { \"{{#each $}}\": \"• <{{ $.html_url }}|#{{ $.number }}> {{ $.title }}\" } }" } },
    { "id": "post", "slack_send_message": {
        "webhook_url": "https://hooks.slack.com/services/T000/B000/XXXX",
        "text": "*{{ $.count }} open pull requests*\n{{ join($.lines, \"\\n\") }}" } }
  ]
}
```

## Set up

1. Create a Slack app at [api.slack.com/apps](https://api.slack.com/apps), turn on **Incoming Webhooks**, and add a webhook to the channel.
2. Copy the `https://hooks.slack.com/services/…` URL into `webhook_url`. Treat it as a secret: anyone with it can post to the channel.
3. To read messages with `slack_channel_history`, give the app a bot token with `channels:history` and save it as a `bearer_token` credential.

To send Slack's richer Block Kit layouts, use a [request step](/integrations/webhooks) that posts your own `blocks` JSON to the same webhook.

## Reference

### Send a Slack message: `slack_send_message`

**Sends** POST to the incoming webhook URL with `{"text": …}`. **Output** for the next step: The text `ok`.

| Field | Type | Notes |
| --- | --- | --- |
| `webhook_url` | string, required | An incoming webhook URL, `https://hooks.slack.com/services/…`, from a Slack app with Incoming Webhooks turned on. |
| `text` | string, required | Slack mrkdwn: `*bold*`, `_italic_`, `<https://example.com\|a link>`. Up to 40,000 characters. Placeholders allowed. |

### Read a Slack channel: `slack_channel_history`

**Sends** GET `https://slack.com/api/conversations.history`. **Output** for the next step: Slack's response: `messages` (newest first), each with `text`, `user` and `ts`.

| Field | Type | Notes |
| --- | --- | --- |
| `credential_id` | string, required | The id of a `bearer_token` credential holding a bot token (`xoxb-…`) with `channels:history`, `groups:history` or `im:history`. |
| `channel` | string, required | A channel, private group or DM id, such as `C0123456789`. |
| `limit` | integer | 1 to 100. Omit for Slack’s default of 100. |

## More ideas

- Post yesterday's signups from your analytics API every morning: `0 8 * * *`.
- Announce who's on call from [PagerDuty](/integrations/pagerduty): `0 9 * * MON`.
- Post [past-due Stripe subscriptions](/integrations/stripe) each weekday.
- Remind the team about timesheets on the last weekday of the month: `0 15 LW * *`.
- Post a [weather brief](/integrations/open-meteo) for a field crew at 05:30 local time.
