Archive Gmail Messages
Archive messages by removing them from inbox (not deleting)
Source Code
import fs from "fs";
const [messageIdsArg = "", outputPath = "session/archive-results.json"] =
process.argv.slice(2);
const messageIds = messageIdsArg
.split(",")
.map((id) => id.trim())
.filter(Boolean);
if (messageIds.length === 0) {
console.error("No message IDs provided");
console.log(JSON.stringify({ success: false, error: "no_message_ids" }));
process.exit(1);
}
console.log(`Archiving ${messageIds.length} message(s)...`);
/**
* Archive a batch with retry for transient failures
*/
async function archiveBatchWithRetry(batch, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const res = await fetch(
"https://gmail.googleapis.com/gmail/v1/users/me/messages/batchModify",
{
method: "POST",
headers: {
Authorization: "Bearer PLACEHOLDER_TOKEN",
"Content-Type": "application/json",
},
body: JSON.stringify({
ids: batch,
removeLabelIds: ["INBOX"],
}),
}
);
if (res.ok) {
return { success: true };
}
// Retry on 5xx or 429 (rate limit)
if (res.status >= 500 || res.status === 429) {
const delay = Math.pow(2, attempt) * 1000 + Math.random() * 1000;
console.log(
` Batch failed (${res.status}), retrying in ${Math.round(delay / 1000)}s...`
);
await new Promise((r) => setTimeout(r, delay));
continue;
}
// Non-retryable error
const errorText = await res.text();
return { success: false, status: res.status, error: errorText };
}
return { success: false, status: "max_retries", error: "Exceeded retry limit" };
}
/**
* Archive messages using Gmail batch modify API
* Archives by removing INBOX label (messages stay in All Mail)
*/
async function archiveMessages(ids) {
const BATCH_SIZE = 50;
const results = { archived: [], failed: [] };
const totalBatches = Math.ceil(ids.length / BATCH_SIZE);
for (let i = 0; i < ids.length; i += BATCH_SIZE) {
const batch = ids.slice(i, i + BATCH_SIZE);
const batchNum = Math.floor(i / BATCH_SIZE) + 1;
console.log(
` Archiving batch ${batchNum}/${totalBatches} (${batch.length} messages)...`
);
const result = await archiveBatchWithRetry(batch);
if (result.success) {
results.archived.push(...batch);
console.log(` ✓ Batch ${batchNum} complete (${results.archived.length}/${ids.length} total)`);
} else {
console.error(` ✗ Batch ${batchNum} failed: ${result.status} - ${result.error}`);
results.failed.push(...batch);
}
}
return results;
}
try {
const results = await archiveMessages(messageIds);
// Write results
const dir = outputPath.split("/").slice(0, -1).join("/");
if (dir) fs.mkdirSync(dir, { recursive: true });
const output = {
success: results.failed.length === 0,
archivedAt: new Date().toISOString(),
archivedCount: results.archived.length,
failedCount: results.failed.length,
archived: results.archived,
failed: results.failed,
};
fs.writeFileSync(outputPath, JSON.stringify(output, null, 2));
console.log(`\n✓ Archived ${results.archived.length} message(s)`);
if (results.failed.length > 0) {
console.log(`✗ Failed to archive ${results.failed.length} message(s)`);
}
console.log(JSON.stringify(output));
} catch (error) {
console.error("Archive failed:", error.message);
throw error;
}