Skip to content

Instantly share code, notes, and snippets.

@pookdeveloper
Last active November 8, 2025 10:00
Show Gist options
  • Select an option

  • Save pookdeveloper/8b1f655e75eb700031b4f6181b3e6fb8 to your computer and use it in GitHub Desktop.

Select an option

Save pookdeveloper/8b1f655e75eb700031b4f6181b3e6fb8 to your computer and use it in GitHub Desktop.
add today to todoist using google script
/**
* Google Apps Script to automate tasks in Todoist.
*
* Goal:
* 1. List all active tasks **ONLY from the Inbox**.
* 2. Assign today’s date to Inbox tasks that have no due date.
* 3. Note: The delete function is included, but the REST API V2 does NOT
* allow automatic listing of completed (archived) tasks for deletion.
* The script can only handle active tasks.
*/
// =====================================================================
// 1. CONFIGURATION (REPLACE WITH YOUR TOKEN)
// =====================================================================
// Get your token from Settings > Integrations > Developer in the Todoist web app. [3]
const TODOIST_TOKEN = "x-x-x-x-x-x-x";
const API_BASE_URL = "https://api.todoist.com/rest/v2/";
const API_TASKS_ENDPOINT = API_BASE_URL + "tasks";
const API_PROJECTS_ENDPOINT = API_BASE_URL + "projects";
// Authentication headers (Bearer Token)
const API_HEADERS = {
Authorization: "Bearer " + TODOIST_TOKEN,
// Required for requests that send data (POST/DELETE)
"Content-Type": "application/json",
};
// Global variable to store the Inbox ID once retrieved
let INBOX_PROJECT_ID = null;
// =====================================================================
// 2. DATE UTILITIES
// =====================================================================
/**
* Generates today’s date in YYYY-MM-DD format, as required by the Todoist API. [4]
* Uses the script’s timezone to ensure local accuracy. [5]
* @returns {string} Today’s date in 'YYYY-MM-DD' format.
*/
function getTodayDateString() {
const date = new Date();
const timeZone = Session.getScriptTimeZone(); // Get the script’s timezone
return Utilities.formatDate(date, timeZone, "yyyy-MM-dd"); // Required format [4]
}
// =====================================================================
// 3. API WRAPPER
// =====================================================================
/**
* Core function to interact with the Todoist API using UrlFetchApp.
* Handles authentication, JSON parsing, and special response codes (204). [6]
*
* @param {string} url - Full endpoint URL.
* @param {string} method - HTTP method (GET, POST, DELETE).
* @param {Object} [payload] - JSON body for POST requests.
* @returns {Object|null} JSON response object or null in case of failure or 204.
*/
function fetchTodoistApi(url, method, payload) {
const options = {
method: method,
headers: API_HEADERS,
// Allows the script to manually handle HTTP 4xx and 5xx error codes. [6]
muteHttpExceptions: true,
};
if (payload && method !== "GET") {
options.payload = JSON.stringify(payload);
}
try {
const response = UrlFetchApp.fetch(url, options);
const responseCode = response.getResponseCode();
if (responseCode === 200) {
// Success with content (typically for GET) [4]
return JSON.parse(response.getContentText());
} else if (responseCode === 204) {
// Success with no content (typically for POST/DELETE) [7]
return {
success: true,
code: 204
};
} else if (responseCode === 401) {
// Authentication error
Logger.log("ERROR 401: Invalid or expired Todoist token. Stopping execution.");
throw new Error("ERROR 401: Authorization failed.");
} else {
// Other 4xx or 5xx errors
Logger.log(`ERROR ${responseCode} calling ${url}. Message: ${response.getContentText()}`);
return null;
}
} catch (e) {
Logger.log("Exception in fetchTodoistApi: " + e.message);
return null;
}
}
// =====================================================================
// 4. BUSINESS LOGIC FUNCTIONS
// =====================================================================
/**
* Retrieves the ID of the Inbox project by querying
* the /projects endpoint and finding the one with "is_inbox_project": true.
* @returns {string|null} The Inbox project ID or null if not found.
*/
function getInboxProjectId() {
if (INBOX_PROJECT_ID) {
return INBOX_PROJECT_ID; // Use cached value if available
}
Logger.log("Searching for Inbox project ID...");
const projects = fetchTodoistApi(API_PROJECTS_ENDPOINT, "GET");
if (projects && Array.isArray(projects)) {
const inboxProject = projects.find(project => project.is_inbox_project === true); [2]
if (inboxProject) {
INBOX_PROJECT_ID = inboxProject.id;
Logger.log(`Inbox ID found: ${INBOX_PROJECT_ID}`);
return INBOX_PROJECT_ID;
}
}
Logger.log("ERROR: Could not retrieve Inbox project ID.");
return null;
}
/**
* Updates the due date of a specific task to today.
* @param {string} taskId - ID of the task to update.
* @param {string} todayDateString - Date in 'YYYY-MM-DD' format.
* @returns {boolean} True if the update was successful.
*/
function updateTaskDueDate(taskId, todayDateString) {
const url = `${API_TASKS_ENDPOINT}/${taskId}`;
const payload = {
due_date: todayDateString, // Use due_date in YYYY-MM-DD format [4]
};
const result = fetchTodoistApi(url, "POST", payload);
if (result && result.code === 204) {
Logger.log(` Task ${taskId} updated with today’s date: ${todayDateString}`);
return true;
} else {
Logger.log(`[FAILED] Could not update task ${taskId}.`);
return false;
}
}
/**
* Deletes a specific active task.
* NOTE: This function only applies to ACTIVE tasks. [7]
* @param {string} taskId - ID of the task to delete.
* @returns {boolean} True if deletion was successful.
*/
function deleteActiveTask(taskId) {
const url = `${API_TASKS_ENDPOINT}/${taskId}`;
const result = fetchTodoistApi(url, "DELETE"); [7]
if (result && result.code === 204) {
Logger.log(` Active task ${taskId} deleted successfully.`);
return true;
} else {
Logger.log(`[DELETE FAILURE] Could not delete active task ${taskId}.`);
return false;
}
}
// =====================================================================
// 5. MAIN AUTOMATION FUNCTION
// =====================================================================
/**
* Main function to execute (can be scheduled via a trigger).
* Iterates over ACTIVE INBOX tasks and assigns today’s date to those without one.
*/
function runTodoistAutomation() {
Logger.log("--- STARTING TODOIST AUTOMATION (INBOX ONLY) ---");
// 1. Get the Inbox ID to filter tasks
const inboxId = getInboxProjectId();
if (!inboxId) {
Logger.log("CRITICAL ERROR: Cannot proceed without Inbox ID.");
return;
}
// 2. Get today’s date in the proper format
const todayDate = getTodayDateString();
Logger.log(`Today’s date to assign: ${todayDate}`);
// 3. Build the URL with the 'project_id' parameter to filter by Inbox [4]
// This parameter replaces the text filter 'filter=inbox' that caused ERROR 400.
const urlWithFilter = `${API_TASKS_ENDPOINT}?project_id=${inboxId}`;
// 4. Retrieve all active (pending) Inbox tasks [4]
const tasks = fetchTodoistApi(urlWithFilter, "GET");
if (!tasks || tasks.length === 0) {
Logger.log("No active Inbox tasks found or API call failed.");
return;
}
Logger.log(`Active tasks found in Inbox: ${tasks.length}`);
let updatedCount = 0;
let deletedCount = 0;
// 5. Iterate and process tasks
tasks.forEach(task => {
// A. Logic: Assign today’s date to tasks with no due date
// The 'due' property is null when the task has no assigned date.
if (task.due === null) {
Logger.log(` Task: ${task.content} (ID: ${task.id}) has no date. Assigning today’s date.`);
if (updateTaskDueDate(task.id, todayDate)) {
updatedCount++;
}
}
// B. Logic: Delete completed tasks (applies only to active ones)
/*
* Limitation: The REST API V2 does not expose the completed task history
* for bulk deletion.
*/
});
Logger.log(`--- PROCESS COMPLETED ---`);
Logger.log(`Inbox tasks updated with today’s date: ${updatedCount}`);
Logger.log(`Inbox tasks deleted (requires additional filter logic for active ones): ${deletedCount}`);
}
// =====================================================================
// 6. USAGE INSTRUCTIONS FOR GOOGLE APPS SCRIPT
// =====================================================================
// 1. Paste this code into the 'Code.gs' file.
// 2. Replace `TODOIST_TOKEN` with your personal key. [3]
// 3. **Set up the Trigger:** Schedule the `runTodoistAutomation` function to run daily.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment