Daily standup bot

Standup is a crucial part of running an effective engineering team, and also oh so tedious: every morning, everyone digs back through what they did and writes it up. It's worse for teams spread across timezones, where there's no shared standup to anchor the day, so it's easy to just forget.

But the work you did is there: the PRs in github GitHub, the docs in notion Notion, the decisions in slack Slack threads. If it's all recorded somewhere, an agent should be able to at least draft it. Composio sessions make this incredibly easy for agents: a session hands the agent everything it needs, search to find the right tool, parallel execute to run many at once, a sandbox and volumes. It uses Composio to extract, parse, and cross-reference data across all those sources and write clean summaries of the real work your team shipped. All you have to do is create a session for your teammate and let it cook.

The daily standup reminder in Slack
The daily reminder, with Draft and Connect more tools buttons
A generated standup draft in Slack
The draft the agent writes, delivered to a teammate in Slack

So we built a Slack bot that does exactly that. Once a day, at a set time in each teammate's own timezone, it reminds them to post in the daily standup thread in a central channel. With one button click, they can run a subagent that uses their Composio connections to generate a clean, consolidated draft to review and post. We'll build it step by step.

Is this the right example for you?

This is a deliberately advanced, opinionated build. It's a strong reference for five things:

  • Background-agent sessions: the draft agent runs on a schedule, not in a conversation. It works from the tools a member already connected and never pauses to ask for auth.
  • Manual execution for deterministic workflows: outside the draft, the bot doesn't let an agent decide. It runs a fixed flow, calling tools directly with manual execution, so a button always triggers the same exact steps.
  • Manual, pre-connected auth: members connect their tools ahead of time using manual connections, and the agent just uses whatever is there.
  • White-labelling (advanced): your own Slack app and bot identity, via white-labelling. This is not the easy path. We'd recommend Composio's managed apps, which require no additional configuration. Only do this if you specifically want your own branding.
  • The proxy (advanced): using proxyExecute to call Slack API endpoints Composio doesn't wrap as tools.

It is not an example of in-chat or dynamic auth (asking a user to connect a tool mid-run), and it's more setup than many bots need. If you'd rather have a Slack bot with zero setup (Composio's managed app) or in-chat auth, start with the general Slack bot instead.

The Slack bot itself follows a deterministic flow: the same menu every day. When a member taps a button, it launches a subagent with a Composio session to produce the draft. Here's the shape of it:

Setup

You need a Composio API key, a Slack workspace you can install an app into, and Node with tsx. The finished bot deploys to Vercel as two serverless functions, a cron and an interactivity handler, so there's no long-running server.

npm install @composio/core @composio/vercel ai dayjs

Make your custom Slack bot

This bot doesn't post as "Composio". It posts as my app, with its own name, icon, and (frankly ridiculous) face:

The Daily Standup Bot avatar

That's white-labelling: you bring your own Slack app's credentials instead of using Composio-managed ones. Composio-managed auth needs no setup at all; white-labelling is the trade-off, you register and configure your own Slack app first. It's more work, but the bot is fully yours.

Slack's app-creation UI changes from time to time, so the exact screens below may differ. First, the Slack app itself.

Create the app from scratch. At api.slack.com/apps, click Create an App, choose From scratch, name it (mine is Daily Standup Bot), and pick your workspace.

Naming the Slack app and choosing a workspace
Create the app from scratch and name it

Add the Bot Token Scopes. Under OAuth & Permissions, add: chat:write, im:write, channels:history, channels:read, users:read, users:read.email, team:read. Then turn on Interactivity and point its Request URL at your deployment's /api/interactivity.

Adding bot token scopes under OAuth & Permissions
Add the bot token scopes

Grab the app credentials. On Basic Information, copy the Client ID and Client Secret. Composio drives the OAuth as your app with these.

The app's Client ID and Secret under Basic Information
Copy the Client ID and Secret

Auth the bot

The Slack app exists; now connect it through Composio so your code can act as it. You create one slackbot auth config from your credentials, then a setup script does the OAuth once with Composio's manual authentication flow.

`slackbot` vs `slack`

Composio has two Slack toolkits, and this bot uses both:

  • slackbot authenticates a Slack app and acts as the bot (a bot token). It posts the reminders and drafts as "Daily Standup Bot," and it's the one you white-label here.
  • slack authenticates an individual user and acts as them (a user token). Each teammate connects this so the bot can post their standup under their own name and read their activity for context.

Rule of thumb: posting as the bot uses slackbot; doing something as a person uses slack.

Create an auth config and pick the Slackbot toolkit. In the Composio dashboard, click Create Auth Config and search slackbot. Choose Slackbot, not Slack: Slackbot posts as the bot identity, while Slack acts as an individual user.

Choosing the Slackbot toolkit, not Slack
Pick Slackbot, not Slack

Use your own credentials. Pick OAuth 2.0, then Your Own Credentials, and paste the Client ID and Secret from before. Add team:read to the user token scopes. This is the white-label step: your app, your name, your face.

Selecting Your Own Credentials and entering the Client ID and Secret
Use your own credentials

Save the auth config id. Once created, copy its ac_... id into COMPOSIO_SLACKBOT_AUTH_CONFIG_ID. This is the one auth config your app uses to take actions on behalf of your bot.

The created slackbot auth config with its ac_ id
The created auth config and its ac_ id

Run the setup script to connect the bot. For this bot, we first need to connect the bot itself to Composio, which only needs to be done once. The script creates an OAuth link for you to connect your Slack bot to Composio, which lets you use Composio to send messages on behalf of your bot.

1Bind a session to your Slack auth configscripts/setup.ts

The bot authenticates as your own Slack app. Create a session against the slackbot auth config and check whether it's already connected.

2Authorize and waitscripts/setup.ts

If not connected, `session.authorize()` returns a Connect Link. Print it, then `waitForConnection()` resolves once the bot finishes OAuth. That connected account is the identity the bot posts as.

The first run prints a link and waits:

╭─────────────────────────────────────────────────────────╮
│  Daily Standup Bot: One-Time Setup                       │
╰─────────────────────────────────────────────────────────╯
  ✅ Auth config has the required user scopes.
  ·  Bot is not connected yet. Generating an authorization link…

  Open this URL in your browser to authorize the bot:

    https://backend.composio.dev/s/AbC123xy

  Waiting for you to complete the OAuth flow (Ctrl+C to abort)…
  ✅ Bot connected to Slack.

──────────────────────────────────────────────────────────────────────
  🎉 Setup complete. Invite the bot to your standup channel and
     point your Slack app's Interactivity Request URL at
     https://<your-deployment>/api/interactivity
──────────────────────────────────────────────────────────────────────

Open that link to approve the bot, and the connection goes live:

Approving the bot's OAuth connection
Approve the bot in Slack
Composio successfully connected to Slackbot
Connected

The script is idempotent and repeatable. Forgot a scope, or hit an issue? No stress, just re-run it with --reconnect.

Talk to Slack

To send and update messages in our deterministic bot workflow, we use Composio's SLACKBOT_SEND_MESSAGE and SLACKBOT_UPDATES_A_MESSAGE tools via manual tool execution. SLACKBOT_SEND_MESSAGE takes Block Kit blocks, so a message with interactive buttons can go through a tool too.

When a Slack action has no tool, like opening a modal (views.open), it drops to proxyExecute: the escape hatch for anything the named tools don't cover, hitting any Slack Web API endpoint as a connected account with no token in your code.

Make the buttons work

Our StandUp bot gives the user two options every morning: Draft or Connect more tools. Each message uses Block Kit to create those buttons. For each button we define an action_id that lets us recognise which button was clicked.

// the reminder's Draft button
const draftButton = {
  type: 'button',
  style: 'primary',
  text: { type: 'plain_text', text: '📝 Draft' },
  action_id: 'draft',
  value: JSON.stringify({ memberEmail, dmChannel, dmTs }),
};
The daily standup reminder in Slack
The daily reminder, with Draft and Connect more tools buttons

When it's clicked, Slack POSTs to your /api/interactivity handler. Verify the request, ack within Slack's 3-second window, then route on the action_id:

1Verify the request and ack fastapi/interactivity.ts

Slack POSTs to this endpoint on every click. Verify the signature, then respond within 3 seconds so Slack doesn't retry, and handle the click after.

2Route on the actionapi/interactivity.ts

The button carried its context in `value`. Switch on the `action_id` and call the right function. Draft launches the subagent; the rest post through the proxy. The flow is deterministic, no model in the loop.

Connect more tools generates a per-member OAuth link for each toolkit the member hasn't connected, so they can add a source without leaving Slack:

The connect-more-tools menu in Slack
Connect more tools, each button a per-member OAuth link

Edit opens a modal (views.open through the proxy), and Confirm posts the draft into the day's thread as the member.

Draft the standup

Now this is the cool and magical part, and the easy part: all the background agent needs is a tool-router session and a prompt. When a member taps Draft, you spin up a session scoped to the toolkit catalogue and let the agent research and write.

A session writes the draft

A tool-router session gives the agent its tools. Pass the member's email and your full list of toolkits, hand the tools to the model, and let it investigate and write. You don't have to check which ones the member set up: the session only exposes tools for the accounts they've actually connected, and ignores the rest.

Use what's connected, nothing more

The router can also manage connections, asking the user to authorize any toolkits they haven't connected yet. During a draft you don't want that: if the agent reaches for a tool the member hasn't connected, it should skip it, not prompt them to log in. manageConnections: false removes those meta-tools, so the agent drafts from exactly what's already connected.

The bot posts the result back as a draft the member can confirm or edit:

A generated standup draft in Slack with Confirm and Edit buttons
The draft the agent writes, delivered to a teammate in Slack

The whole project

Run it

Edit standup.config.ts with your team (each member's Slack email and timezone, plus your channel and GitHub org), set your four environment variables, run npx tsx scripts/setup.ts once to connect your bot, then vercel deploy.