Build a daily digest bot that gathers GitHub activity, summarizes it with a model, and posts to Slack on a schedule without running it inline in a cron handler, where a single slow source or a failing channel can take down the whole run. A cron route starts a single durable workflow that fans out a step per channel, so each fetches, summarizes, and posts on its own, and retries independently.
This guide builds the bot with Chat SDK and Workflow SDK. Every credential is brokered at runtime: Slack and GitHub authenticate through Vercel Connect, and the model authenticates with your project's OpenID Connect (OIDC) token. There are no API keys or client secrets to set.
To get started, clone this template’s GitHub repository. You can also deploy it to Vercel with one click:
GitHub daily digest Slack bot
A Slack bot that reads your GitHub activties and compiles an AI generated daily disgest for you every day.
If you are working with an AI coding agent, hand it the project and this prompt:
Turn your agent into a Vercel expert with this plugin. It gives your coding agent current knowledge of the Vercel products this template uses, including Vercel Connect, Vercel Workflows, Vercel Cron, AI Gateway, and Chat SDK. The plugin is optional; it is not required to use this template or for this guide.
Terminal
Before you begin, make sure you have:
- Node.js 20+
- pnpm (or npm/yarn)
- A Vercel team and project with Vercel Connect enabled, plus permission to create connectors and link them to projects
- The Vercel CLI is installed
- A Slack workspace where you can install apps
- A GitHub account where you can install apps
Create a new Next.js app with create-next-app :
Then install the Chat SDK, Vercel Connect, and Workflow SDK packages:
Wrap your Next.js config in withWorkflow:
Your agent uses Redis for thread subscriptions and distributed locking. Provision Upstash Redis and connect it to your project with the Vercel CLI:
vercel integration add installs the Upstash integration if it isn’t already, provisions a database, connects it to your project, and pulls its connection environment variables into .env.local. Follow the prompts to pick the Redis product and a plan.
Use the Vercel CLI to link the project and pull environment variables:
AI SDK uses VERCEL_OIDC_TOKEN to authenticate with the Vercel AI Gateway with OIDC authentication.
Create the Slack connector in Vercel Connect before you wire the bot locally. Vercel Connect creates and manages the Slack app, so you do not need to create a Slack app at api.slack.com or copy a long-lived Slack bot token.
- Open the Connect page in your Vercel team dashboard.
- Choose Create Connector.
- Select Slack as the provider.
- Select the Slack workspace and name the connector, for example
digest-bot. - Keep triggers enabled if this project should receive Slack events.
- Keep the default scopes selected.
- Create the connector and install it in the Slack workspace.
- In the connector settings, link it to the Vercel project and select the environments where it should be available.
Copy the Slack connector id and store it in .env.local file as CONNECTOR_SLACK, for example:
Also, add the following environment variables:
Create the GitHub connector in Vercel Connect and install it on the repositories you want included in the digest.
- Open the Connect page in your Vercel team dashboard.
- Choose Create Connector.
- Select GitHub as the provider.
- Select the GitHub account or organization to connect.
- Install the connector on all repositories the digest should read, or select a smaller repository allowlist.
- Create the connector.
- In the connector settings, link it to the Vercel project and select the environments where it should be available.
Copy the GitHub connector id and store it in .env.local file as CONNECTOR_GITHUB, for example:
The digest requests an app-scoped token from Vercel Connect:
The connector installation determines which repositories the token can access. If a repository is missing from the digest, check the connector installation and project/environment link before changing code.
lib/bot.ts centralizes the Chat SDK instance. It requests short-lived Slack tokens from Vercel Connect and configures Redis state so proactive posts and webhook handling share the same bot.
lib/digest/types.ts keeps the workflow input, source contract, and model output schema in one place. The same schema validates the cron input and constrains the AI SDK response.
lib/digest/enrollment.ts turns the single DIGEST_CHANNEL_ID environment variable into the workflow input. Keep schedule-independent choices, like lookback window and tone, in code so the environment stays small.
Keep the cron route thin. It should verify the secret CRON_SECRET), load the single configured channel, start the workflow, and return the run id.
vercel.json tells Vercel Cron to call the digest route every day. The route still checks CRON_SECRET, so only authorized cron requests can start a workflow.
Add the CRON_SECRET environment variable to your Vercel project before deploying the application.
Use one workflow to orchestrate the work, but keep expensive or failure-prone operations in separate steps. This makes the workflow retries and run inspection more useful.
lib/digest/sources.ts owns the GitHub data contract. It gets a token from Vercel Connect, discovers connector-visible repositories, counts open issues, and returns a precomputed activity payload for the model.
Use the REST repository endpoints to discover connector-visible repositories. Then use batched GitHub GraphQL queries for issue counts and recent issue nodes.
Avoid the GitHub Search API for per-repository issue counts. Its quota is much lower, and a scheduled digest can exhaust it quickly when it searches once per repository.
Here’s the API request flow:
- Fetch up to a bounded number of active repositories.
- Count public and private repositories in code.
- Query GraphQL in batches for
issues(states: OPEN) { totalCount }. - Fetch recent issue nodes only for the repositories shown in the digest. Keep recent issue nodes capped, for example 10 per repository.
The digest payload should give the model precomputed totals, not ask it to infer them. Build the payload in a complete helper function:
This keeps the prompt focused on writing the digest rather than doing arithmetic over raw issue lists. It also makes the posted summary easier to verify when you inspect a workflow run.
The source file should also throw RetryableError for transient GitHub failures and FatalError for bad configuration, so workflow retries only the failures that can recover.
lib/digest/prompt.ts turns the structured GitHub activity into model instructions. Keep totals and source data in the activity object, then use the prompt only to control tone and output priorities.
Create the Slack message card in the lib/digest/card.tsx :
To trigger the cron route, start the app:
In another terminal, run the following curl command to trigger the cron job:

To inspect workflow runs include the steps timeline and retries during development, run the following command:
You now have a daily digest bot with Chat SDK, Workflow SDK, and Vercel Connect. These primitives are extensible for more comprehensible use cases like a PR review bot or an incident watchlist to help you navigate public reports on GitHub.
Each item below lists a symptom, its cause, and the fix.
Make sure CRON_SECRET is set in the environment where the cron job runs, such as production. Vercel Cron sends Authorization: Bearer $CRON_SECRET, and the route fails closed when the variable is missing or the header does not match.
Symptom: A Vercel Cron run or manual request gets 401 Unauthorized.
Cause: CRON_SECRET is missing from the environment where the route runs, or the request does not include Authorization: Bearer $CRON_SECRET.
Fix: Set CRON_SECRET in production and any preview environment where the cron route should run. For local tests, pull the environment with vercel env pull or set the variable in the shell before calling the route.
Symptom: The workflow runs, but no message appears in Slack.
Cause: The Slack app is not in the configured channel, DIGEST_CHANNEL_ID is not in Chat SDK channel id format, or CONNECTOR_SLACK is missing from the environment.
Fix: Invite the Slack app to the channel, set DIGEST_CHANNEL_ID to a value such as slack:C123ABC, and confirm the Slack connector is linked to the Vercel project environment.
Symptom: The digest includes fewer repositories than expected.
Cause: The GitHub connector is installed on a limited repository allowlist, or the connector is not linked to the environment running the workflow.
Fix: Update the GitHub connector installation to include the repositories you want, then confirm CONNECTOR_GITHUB is available in the same Vercel environment as the workflow.
Check that issue counts use GraphQL batching, not GitHub Search API calls. Search API limits are easier to hit and should not be used once per repository.
Symptom: The fetchGitHubActivity step fails with a rate limit error.
Cause: GitHub rate limits can still apply to connector tokens, especially if the workflow queries too many repositories or uses the Search API for counts.
Fix: Keep repository fetching bounded, use GraphQL batching for issue counts, and avoid per-repository GitHub Search API calls. The template marks rate limits as RetryableError so workflow can retry after a delay.
When Slack events are routed through Vercel Connect, Connect verifies the event before forwarding it. The Slack adapter still expects a webhookVerifier, so provide one that delegates trust to Connect for that route. If you receive events directly from Slack, use Slack's signing secret instead.
Symptom: The Slack webhook route fails with a signing secret error.
Cause: The Slack adapter requires a webhook verifier, but Vercel Connect has already verified Connect-forwarded Slack events before they reach the app.
Fix: For Connect-forwarded Slack events, use a verifier that delegates trust to Connect for that route. If events come directly from Slack, configure verification with Slack's signing secret instead.
Use the Chat SDK function-call Card API in workflow steps instead of JSX if the generated workflow route does not recognize the JSX Card shape at runtime.
Symptom: The Slack post step fails with Invalid JSX element: must be a Card element.
Cause: The workflow route may not recognize a JSX Card shape at runtime.
Fix: Use the Chat SDK function-call Card API in lib/digest/card.tsx, as shown above.
Symptom: GitHub or Slack token requests work in production but fail locally.
Cause: The local VERCEL_OIDC_TOKEN written by vercel env pull has expired, or the local project is linked to the wrong Vercel project.
Fix: Run vercel link to confirm the project, then run vercel env pull again.
- Workflow SDK documentation
- Chat SDK cards
- Vercel Connect and Vercel Connect CLI
- Vercel AI Gateway
- Vercel OIDC
No. Slack and GitHub authenticate through Vercel Connect. The app stores connector IDs such as CONNECTOR_SLACK and CONNECTOR_GITHUB, then requests short-lived app-scoped tokens at runtime.
You can test the scheduled outbound flow locally by running the app and calling the cron route with CRON_SECRET. Inbound Slack events, such as mentions or slash commands, should be tested against a preview or production deployment because Slack events are forwarded through Vercel Connect.
Workflow makes the digest durable and easier to debug. GitHub fetching, AI generation, and Slack posting run as separate steps, so transient failures can retry without mixing all work into one request.
Update the GitHub connector installation in Vercel Connect or GitHub so it has access to the repositories you want. The app reads repositories visible to that connector.