How to Build a Chrome Extension to Save Gmail Emails Using the Gmail API
Posted by navaneeth on Jul 15, 2025 in General | No comments yet
In this post, I’ll walk you through how to build a Chrome Extension that connects to your Gmail account, reads emails, and sends the email content (subject, body, and attachments) to an external API—like a CRM, file management tool, or custom backend.
This is a real-world use case from one of my recent projects: a Gmail extension that sends emails and documents to a system called Exchange Center.
Let’s dive in. 🚀
Tools & Prerequisites
Before we begin, here’s what you’ll need:
- A Google Developer Project with Gmail API enabled
- Your OAuth Client ID
- Basic knowledge of JavaScript / React (optional but helpful)
- Familiarity with Chrome Extensions (Manifest V3)
Step 1: Set Up Your Chrome Extension
Create the base extension folder with the following files:
manifest.json
{
"manifest_version": 3,
"name": "Gmail Saver",
"version": "1.0",
"description": "Save Gmail emails to an external system.",
"permissions": ["identity", "scripting"],
"host_permissions": ["https://mail.google.com/", "https://www.googleapis.com/*"],
"action": {
"default_popup": "popup.html",
"default_icon": "icon.png"
},
"oauth2": {
"client_id": "YOUR_CLIENT_ID.apps.googleusercontent.com",
"scopes": ["https://www.googleapis.com/auth/gmail.readonly"]
},
"background": {
"service_worker": "background.js"
},
"content_scripts": [
{
"matches": ["https://mail.google.com/*"],
"js": ["content.js"],
"run_at": "document_idle"
}
]
}
Step 2: Authenticate with Google
In your background.js, use chrome.identity.getAuthToken() to get the access token:
chrome.runtime.onInstalled.addListener(() => {
console.log("Gmail Saver Extension Installed");
});
function authenticate(callback) {
chrome.identity.getAuthToken({ interactive: true }, function (token) {
if (chrome.runtime.lastError) {
console.error(chrome.runtime.lastError);
return;
}
callback(token);
});
}
Step 3: Fetch Emails from Gmail API
Use the Gmail API to get a list of messages and read their contents.
function fetchEmails(token) {
fetch("https://www.googleapis.com/gmail/v1/users/me/messages?maxResults=5", {
headers: { Authorization: `Bearer ${token}` }
})
.then((res) => res.json())
.then((data) => {
data.messages.forEach((msg) => {
fetch(`https://www.googleapis.com/gmail/v1/users/me/messages/${msg.id}`, {
headers: { Authorization: `Bearer ${token}` }
})
.then((res) => res.json())
.then((messageDetail) => {
const subjectHeader = messageDetail.payload.headers.find(
(h) => h.name === "Subject"
);
const subject = subjectHeader ? subjectHeader.value : "(No Subject)";
const body = getEmailBody(messageDetail.payload);
console.log("Subject:", subject);
console.log("Body:", body);
});
});
});
}
function getEmailBody(payload) {
const encodedBody =
payload.parts?.[0]?.body?.data || payload.body?.data || "";
return decodeURIComponent(escape(window.atob(encodedBody.replace(/-/g, "+").replace(/_/g, "/"))));
}
Step 4: Send Email Content to Your API
Once you have the subject and body, send it to your backend system:
function sendToExchangeCenter(subject, body) {
fetch("https://your-api.com/save-email", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ subject, body })
})
.then((res) => res.json())
.then((response) => console.log("Saved to system", response))
.catch((err) => console.error("Save failed", err));
}
Step 5: Inject Button into Gmail UI
Let’s make this more user-friendly by adding a button directly into Gmail threads.
In content.js:
function addSaveButton() {
const container = document.querySelector("div[aria-label='More']");
if (!container || document.getElementById("saveEmailButton")) return;
const btn = document.createElement("button");
btn.innerText = "Save Email";
btn.id = "saveEmailButton";
btn.style = "margin-left: 10px; background: #1a73e8; color: white; padding: 6px 12px; border: none; border-radius: 4px;";
btn.onclick = () => {
chrome.runtime.sendMessage({ type: "SAVE_EMAIL" });
};
container.parentNode.appendChild(btn);
}
setInterval(addSaveButton, 3000);
Handle the message in your background.js:
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg.type === "SAVE_EMAIL") {
authenticate((token) => {
fetchEmails(token);
});
}
});
Step 6: Security & API Considerations
- Use HTTPS for your API
- Never store Gmail access tokens long-term on the client
- Respect Google’s API usage limits and user privacy
Conclusion
You now have a fully working Chrome Extension that:
- Authenticates using Gmail OAuth
- Reads and parses Gmail content
- Sends data to your backend system
- Injects a save button into the Gmail UI
This can be extended to support:
- Attachments
- Filtering by labels
- Batch processing
- CRM integration
📣 Need Help with a Custom Gmail Extension?
I specialize in building Chrome extensions, custom email automation, and full-stack tools for businesses. If you’re looking to streamline your email workflows — contact me here: https://navaneethm.in/get-in-touch/

Leave a Reply