Zero
Zero
Back

Build an Email Summary Bot using Claude AI, Gmail, and Slack (Part 2)

Use the Claude AI assistant to summarize emails in just a few lines of code.

Sam Magura

Sam Magura

A cube made of smaller cubes

In this series of posts, we're building a "bot" that watches your Gmail inbox and sends you a succinct summary of each email as a DM on Slack.

In Part 1, we created an API that exposes a webhook that gets called when you receive a new email. The API method then calls the Gmail API to get the contents of the email.

This post, Part 2, covers the integration with the Claude AI assistant  and the Slack API. As with the first post, we're leveraging Zero to conveniently manage all the API keys our app needs, all in the same place.

🔗 The full code for this example is available in the zerosecrets/examples  GitHub repository.

Secure your secrets conveniently

Zero is a modern secrets manager built with usability at its core. Reliable and secure, it saves time and effort.

Zero dashboard

Integrating with Claude

In Part 1, we left off with writing the Gmail message contents to the console. Now, let's call the Claude API with a prompt that requests a summary of the email.

You can sign up for the Claude API here . Once you're in, you'll need to obtain credits, either by paying or verifying your phone number to see if you qualify for free credits.

Now, create a Claude API key using the link on the dashboard. Let's copy the API key and store it in Zero as a new secret, within the same Zero project that contains our Gmail API credentials. You should select "Other secret" for the secret type and set the secret's name to "Claude". For the field name, enter API_KEY.

The code for our Claude integration will live in a new claude.ts file. This file should export a summarize function which takes in a GmailMessage and returns the summary.

claude.ts
export async function summarize(gmailMessage: GmailMessage, apiKey: string): Promise<string> { // TODO }

Claude provides a TypeScript client SDK  which makes interacting with the API a breeze. You can install it with

Terminal
npm install @anthropic-ai/sdk

Next, we initialize the client SDK within the summarize function.

claude.ts
const anthropic = new Anthropic({ apiKey, })

To learn more about the Claude API, check out the Messages API docs  and review the available AI models . Claude 3 has three models, Haiku, Sonnet, and Opus. Haiku is fast and cheap, while Opus is the most intelligent but more expensive. Sonnet falls somewhere in the middle. We'll use Haiku for this demo to conserve our credits.

Claude is a conversational AI assistant, so a prompt consists of one or more "messages", which can either be from the user or the assistant. The ability to include assistant messages in the prompt is neat, because it gives you more control over how the AI responds.

The prompt for our use case can be incredibly simple — no fancy prompt engineering required:

claude.ts
const claudeMessage = await anthropic.messages.create({ model: 'claude-3-haiku-20240307', max_tokens: 256, messages: [ { role: 'user', content: `Please generate a 3 sentence summary of the following email: Subject: ${gmailMessage.subject} ${gmailMessage.body}`, }, ], }) return claudeMessage.content[0].text

If you would like a longer or shorter summary, feel free to tweak the prompt.

Testing it Out

Testing our app's entire flow end-to-end is tedious, because you need update the webhook URL in the Google Pub/Sub subscription each time you begin development, and you need to send yourself an email to exercise the application. To make it more convenient to test the Claude and Slack integrations, I recommend adding a hardcoded testMessage to your codebase so that you can call summarize directly, without going through Gmail. Here's the test message  I used.

Note that to call summarize from index.ts, you'll also need to modify the fetchSecrets function to request the Claude secret, and pass that secret in when you call summarize.

Creating a Slack App

Now, let's send the AI-generated summary to ourselves as a Slack direct message. To integrate with the Slack API, you create a Slack App that can then be installed into the workspace of your choosing. For step-by-step instructions on creating a Slack App, see my earlier post on Sending Slack Alerts .

For our app to call the API, we need to grant it the appropriate scopes. To do that, we'll need to plan out which API methods our application will utilize. Here's the basic flow we need to implement:

  1. Look up our Slack user ID via users.lookupByEmail  — requires the users:read and users:read.email scopes.
  2. Open a direct message conversation between the app and our user via conversations.open  — requires the im:write scope.
  3. Send a chat message with chat.postMessage  — requires the chat:write scope.

To add these scopes to the Slack App, select "OAuth & Permissions" from the left-hand side menu and scroll down to Scopes. Then grant the app the scopes described above.

While you're on the OAuth & Permissions page, you also need to click the "Install to workspace" button that's at the top. Installing the app will reveal the bot user access token, which should be copied and pasted into Zero within a new Slack secret. Set the secret field name to ACCESS_TOKEN.

The Slack API client we'll be using in the next section also requires a signing secret, which you can find on the Basic Information page under the App Credentials heading. Copy the signing secret into your existing Slack secret in Zero, with the field name SIGNING_SECRET.

Calling the Slack API with Bolt

Slack provides a framework called Bolt  for building applications on the Slack platform. Bolt supports both calling the Slack API and receiving events from Slack. We only need to call the API, so we can simply treat Bolt as an API client.

To get started with Bolt, install it into your project:

Terminal
npm install @slack/bolt

Then, in a new slack.ts file, initialize the Bolt client:

slack.ts
import Bolt from '@slack/bolt' export async function sendSlackMessage( from: string, summary: string, accessToken: string, signingSecret: string, ): Promise<void> { const app = new Bolt.App({token: accessToken, signingSecret}) // TODO }

Here, accessToken and signingSecret should be passed in from the fetchSecrets function that calls Zero.

The code to interface with Slack is pretty straightforward — for me, the tricky part was figuring out which API methods needed to be called. Here's the code, with error handling omitted for brevity:

slack.ts
const userResponse = await app.client.users.lookupByEmail({ email: 'YOUR_EMAIL@example.com', }) const userId = userResponse.user?.id const openResponse = await app.client.conversations.open({ users: userId, }) const channelId = openResponse.channel?.id const messageResponse = await app.client.chat.postMessage({ channel: channelId, text: `New email from ${from}:\n\n${summary}`, })

If you run your app again, the bot will ping you on Slack!

Receiving a Slack DM from the bot
Receiving a Slack DM from the bot

Testing it End-to-End

The final step is to test the Gmail, Claude, and Slack integrations all together. To do so, you'll place the calls to summarize and sendSlackMessage inside the /api/summarize handler:

index.ts
app.post('/api/summarize', async (req, res) => { const result = await queryNewMessages(googleClient, historyId) historyId = result.historyId console.log(`Received ${result.messages.length} email(s).`) for (const message of result.messages) { const summary = await summarize(message, secrets.claude.api_key) console.log('Summarized email with Claude.') await sendSlackMessage(message.from, summary, secrets.slack.access_token, secrets.slack.signing_secret) console.log('Sent summary on Slack.') } res.send(undefined) })

You should now be able to trigger the Slack DM by sending yourself an email in Gmail.

Wrapping Up

This project demonstrated the power of chaining multiple APIs together, using Zero to seamlessly manage the API credentials in a single place. With the right APIs and webhooks, you can exchange information between many different applications. Writing your own code for this instead of relying on an off-the-shelf solution like Zapier makes it easier to integrate with AI models like Claude, to transform the data as it flows from application to application. The possibilities are really endless once you master this kind of workflow.


Other articles

A tall office building

Build an Email Summary Bot using Claude AI, Gmail, and Slack (Part 1)

Integrate with the Gmail Push Notifications API to enable your app to intelligently respond to new emails.

Old-school mailboxes at a post office

Using Notion as a Human-Readable Database

Capture form submissions from your web app and store them where your team works.

Secure your secrets

Zero is a modern secrets manager built with usability at its core. Reliable and secure, it saves time and effort.