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
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.
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.
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
npm install @anthropic-ai/sdk
Next, we initialize the client SDK within the summarize
function.
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:
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:
- Look up our Slack user ID via users.lookupByEmail — requires the
users:read
andusers:read.email
scopes. - Open a direct message conversation between the app and our user via conversations.open — requires the
im:write
scope. - 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:
npm install @slack/bolt
Then, in a new slack.ts
file, initialize the Bolt client:
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:
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!
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:
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
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.
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.