Using Nylas and OpenAI to triage email

Solving email inbox triage with Nylas APIs and OpenAI GPT APIs.

In my previous post about using AI in DevRel, I mentioned a sample code repo I built with Nylas APIs and OpenAI APIs.

In this post, I will walk through that code to demonstrate how simple it can be to get started with both sets of APIs.

You can see the repo on the Nylas Samples GitHub org.

The concept

Despite all the advances in email sorting technologies over the years, I still find it difficult to open Gmail and just know what deserves my attention. Even with rules, filters, and Gmail’s priority inbox, I find that I’m visually hunting through emails to get to the signal within the noise.

This seems like a problem AI could solve.

The flow

The Node.js script I put together is a simple demonstration of how to wade into using AI to triage email.

The script flow:

  1. [Nylas Email API] Pull in the latest emails from a user’s inbox
  2. [OpenAI API] Prompt GPT to triage email based on relevant data
  3. [Node.js app] Receive the triage results and pass to the user

And that’s it.

We’ll talk about ways this could get really useful later. For now, let’s have a look at the code.

The prereqs

To keep focus on the core purpose of the code, I’ll jump past explaining any prereqs and setup for the repo.

But if you do want a hand in getting started, here are some resources to check out:

The code

Let's go in order from our flow list above:

[Nylas Email API] Pull in the latest emails from a user’s inbox

We start by getting a list of messages from an email account (Gmail in my case, but it could be a host of other providers including Microsoft Outlook and Exchange).

Note how the following code is quite simply asking for the 10 latest messages and returning a list (as a JavaScript array). I’m not doing anything with the data at this stage of the script, just returning.

// Get messages from Nylas
const getMessageList = async () => {
  try {
    const messageList = await nylas.messages.list({ limit: 10 });

    console.log(`Found ${messageList.length} messages in your inbox...`);

    return messageList;
  } catch (err) {
    console.error("Error:\n", err);
  }
};

Next up from our flow list:

[OpenAI API] Pass relevant email data to GPT for triaging

I tackled this step in 2 parts:

  1. Loop through the list, and pass each Nylas email message object to a helper function
  2. In the helper function, extract data from each email message and pass it to GPT with a prompt that instructs the AI how to triage

I’ll skip going into depth on the loop, but you can see that code in the classifyMessages() function in the repo.

Within said loop, each single message is passed to the classifyMessage() function shown below. Note that the message argument is the email message object that came back from the Nylas API—I’m simply deconstructing it in the function parameter as from, subject, and snippet, all properties in the Nylas email message object.

// Pass a message to GPT
// Get a string value for whether the user should read and why
const classifyMessage = async ({ from, subject, snippet }) => {
  const response = await openai.createChatCompletion({
    model: "gpt-3.5-turbo",
    messages: [
      {
        role: "system",
        content: `You're an email assistant and you help me figure out which emails are something I should read and which are not worth my time. 
        The following are categories I want to avoid: spam, newsletters, sales messages, junk.
        Answer with "Yes" or "No", then a comma followed by a one-word category to demonstrate your reason.
        Is the following message something I should read?
        From: ${from}
        Subject: ${subject}
        Snippet: ${snippet}`,
      },
    ],
  });

  return response.data.choices[0].message.content;
};

The OpenAI API returns a response with a lot of good data, but for this simple example, we only want the response to the prompt.

For this prompt with GPT 3.5 Turbo, that response will be a string formatted as "Yes, {category}" or "No, {category}". The "Yes" or "No" indicates whether the message is likely to be important to you, the user.

The prompt

You can see the prompt in the code above, but I’ll format it for easier viewing here:

You're an email assistant and you help me figure out which emails are something I should read and which are not worth my time.

The following are categories I want to avoid: spam, newsletters, sales messages, junk.

Answer with "Yes" or "No", then a comma followed by a one-word category to demonstrate your reason.

Is the following message something I should read?

From: ${from} Subject: ${subject} Snippet: ${snippet}`

The responses from GPT will be along the lines of "Yes, personal" or "No, newsletter".

The output

When the cycle is complete, the script will put the following output in your terminal for each email:

# date, subject, category, and message ID
[4/8/YYYY] Here's an important message - Yes, security. (1yz01ivndb)
[4/7/YYYY] Here's an email subject - No, newsletters. (2wwqyz01ivnmzb)
[4/6/YYYY] Another subject - No, spam. (241ahbvvivnmzb)

So in this example, I'd have one email message worth looking at according to GPT.

Where to take it from here

This is obviously a basic demonstration, but you can imagine where this could go:

  • Give the prompt the option to be "unsure"
  • Let the user "train" the prompt by storing an allow/block list to add to the prompt
  • Expose the results in a graphical email client
  • Use the results to categorize emails in Gmail/Outlook/Exchange
  • Expand on the list of categories to triage

The list goes on and on. I challenge you to explode this into something fun and useful.