This version (previous one here) uses two applets instead of four, with lots of ways to tweak it to fit your style. Once IFTTT lets us use the task ID directly, we'll only need one applet.
This automation requires an IFTTT Pro+ plan.
Last update: 02 FEB 2025
You'll set up a web request to send the task ID to the next applet.
Trigger: New completed task in Todoist.
Filter Code:
// Extract task ID from URL
let taskIdMatch = Todoist.newCompletedTask.LinkToTask.match(/task\/(\d+)/);
let taskId = taskIdMatch ? taskIdMatch[1] : '';
// Send task ID to the second applet
MakerWebhooks.makeWebRequest.setBody(
JSON.stringify({ value1: taskId })
);
Action: Make a web request to send the task ID to the next applet. We'll configure it later after creating the second applet.
This applet should now look like this:
This applet receives the task ID, fetches detailed information using Todoist's Sync API, formats the data, and logs it into Reflect.
Trigger: Receive a web request. Event name can be anyone like: todoist_completed
Action: Make a web request with JSON response to Todoist's Sync API.
URL: https://api.todoist.com/sync/v9/items/get?item_id= {{Value1}}
Notice the space in = {
? Yeah, IFTTT does that. Just go with it.
Additional headers: Authorization: Bearer API_TOKEN
Grab your API token from Todoist under Settings > Integrations > Developer tab.
Method: GET
Content type: application/json
Filter Code:
// ----- CONFIGURATION OPTIONS -----
// Uncomment the line that matches your preferred date format.
const DATE_FORMAT = 'DD/MM/YYYY';
// const DATE_FORMAT = 'MM/DD/YYYY';
// Display settings
const GROUP_UNDER_SINGLE_LIST = false; // This flag is not compatible with INCLUDE_PARENT_TASK
const SINGLE_LIST_NAME = '[[✅ Completed tasks]]'; // Name for the single task list, if enabled
const USE_ICONS = true; // Toggle icons for tasks
// Define the keyword for cancelled tasks. It has no effect if USE_ICONS is false. Read the guide for more info.
const CANCELLED_KEYWORD = 'cancel';
// Configure flags to include or exclude parts of the task
const INCLUDE_PROJECT = true;
const INCLUDE_SECTION = true;
const INCLUDE_LABELS = true;
const INCLUDE_DESCRIPTION = true;
const INCLUDE_COMMENTS = true;
const INCLUDE_PARENT_TASK = true;
const INCLUDE_CREATION_DATE = true;
// ----- END CONFIGURATION -----
// Format a date in the specified format
function formatDate(date: Date): string {
const year = date.getFullYear();
const month = ('0' + (date.getMonth() + 1)).slice(-2);
const day = ('0' + date.getDate()).slice(-2);
return DATE_FORMAT
.replace('YYYY', year.toString())
.replace('MM', month)
.replace('DD', day);
}
// Get values from the Todoist Sync API response
const response = JSON.parse(MakerWebhooks.makeWebRequestQueryJson[0].ResponseBody);
const item = response.item;
const parentTask = response.ancestors.length > 0 ? response.ancestors[0] : null;
const notes = response.notes;
const section = response.section;
// Remove "* " from the name of non-completable parent tasks
function cleanTaskName(name: string): string {
return name.indexOf("* ") === 0 ? name.substring(2) : name;
}
// Check if task is cancelled
function isTaskCancelled(taskName: string): boolean {
const words = taskName.toLowerCase().split(' ');
return words[words.length - 1].indexOf(CANCELLED_KEYWORD) !== -1;
}
// Format task's text
const icon = USE_ICONS ? (isTaskCancelled(item.content) ? "◎" : "◉") : "";
const taskNameLink = `${icon ? `[[${icon}]] ` : ""}${cleanTaskName(item.content)} [↗](https://todoist.com/showTask?id=${item.id})`;
const descriptionText = INCLUDE_DESCRIPTION && item.description ? ` ${item.description}` : '';
const labelsText = INCLUDE_LABELS && item.labels.length > 0 ? item.labels.map((label: string) => ` [[${label}]]`).join(' ') : '';
const sectionText = INCLUDE_SECTION && section ? ` [[${section.name}]]` : '';
const projectText = INCLUDE_PROJECT ? ` [[${response.project.name}]]` : ''; // Access project name from response
// Get today's date
const today = new Date();
const todayFormatted = formatDate(today);
// Format task's creation date
const createdDate = new Date(item.added_at);
const formattedCreatedDate = formatDate(createdDate);
const createdDateText = INCLUDE_CREATION_DATE && formattedCreatedDate !== todayFormatted ? ` [[${formattedCreatedDate}` : '';
// Format comments with a pseudo-backlink to their date (Reflect doesn't support backlinks to dates from the API yet) if it's different from today
const notesText = INCLUDE_COMMENTS ? notes.map((note: { posted_at: string; content: string }) => {
const date = new Date(note.posted_at);
const formattedDate = formatDate(date);
const datePrefix = formattedDate !== todayFormatted ? `[[${formattedDate} ` : '';
const contentLines = note.content.split('\n');
const indentedContent = contentLines.map((line: string, index: number) => index === 0 ? line : `\t${line}`).join('\n');
return `- ${datePrefix}${indentedContent}`;
}).join('\n') : '';
// Compose final text of the task
let listName = GROUP_UNDER_SINGLE_LIST ? SINGLE_LIST_NAME : '';
let taskContent = '';
if (INCLUDE_PARENT_TASK && parentTask && !GROUP_UNDER_SINGLE_LIST) {
const parentLabelsText = parentTask.labels.length > 0 ? parentTask.labels.map((label: string) => ` [[${label}]]`).join(' ') : '';
const parentLink = `https://todoist.com/showTask?id=${parentTask.id}`;
// Format parent task's creation date
const parentCreatedDate = new Date(parentTask.added_at);
const formattedParentCreatedDate = formatDate(parentCreatedDate);
const parentCreatedDateText = INCLUDE_CREATION_DATE && formattedParentCreatedDate !== todayFormatted ? ` [[${formattedParentCreatedDate}` : '';
const parentIcon = USE_ICONS ? "◐" : "";
const parentTaskContent = `${parentIcon ? `[[${parentIcon}]] ` : ""}${cleanTaskName(parentTask.content)} [↗](https://todoist.com/showTask?id=${parentTask.id})${projectText}${sectionText}${parentLabelsText}${parentCreatedDateText}`;
taskContent = `${taskNameLink}${descriptionText}${labelsText}${createdDateText}\n${notesText}`;
listName = parentTaskContent;
} else {
taskContent = `${taskNameLink}${descriptionText}${projectText}${sectionText}${labelsText}${createdDateText}\n${notesText}`;
}
// Compose request body of the call to the Reflect API
const body_request = JSON.stringify({
text: taskContent,
transform_type: 'list-append',
list_name: listName
});
MakerWebhooks.makeWebRequest.setBody(body_request);
Action: Make a web request to log the formatted task details into Reflect.
URL: https://reflect.app/api/graphs/GRAPH_ID/daily-notes
Replace GRAPH_ID
with your Reflect's graph identifier found in Preferences > Graphs.
Method: PUT
Content type: application/json
Additional headers: Authorization: Bearer ACCESS_TOKEN
Replace ACCESS_TOKEN
with a Reflect's access token. They can be created here.
This applet should now look like this:
Go back to the first applet and configure the action (THEN):
URL: paste the webhook URL of the second applet.
Method: POST
Content type: application/json
You can easily tweak the filter code in the second applet to make it your own.
Date Format: Adjust the DATE_FORMAT
constant to match your regional preference.
Cancelled Keyword: I like to track what I chose not to do and why. Todoist doesn't have a cancelled status, so I add '- cancelada' to the task name before checking it off. If the last word contains that keyword, it's marked as cancelled.
Flags: Use the configuration flags to include or exclude specific parts of the task, such as project, section, labels, description, comments, parent task, and creation date.
Here's a peek at how tasks and comments will show up.
Example 1. Default settings (group under a single list disabled. Icons enabled. All flags enabled).
Example 2. Group under a single list enabled. Icons disabled. Sections and labels disabled. Creation date disabled.
Icons:
Icons will help you spot tasks in your daily note if you, like me, prefer to see them flowing with the rest of your daily note and not groped under the same list.
The icons are backlinks, so you can see all the tasks together. Initially, you'll get a note for each one, but you can merge them and add aliases like this: ◉ // ◎ // ◐ // Todoist tasks
. if you prefer to keep them separate, consider adding aliases like ◉ // Completed Todoist tasks
, ◎ // Cancelled Todoist tasks
and ◐ // Parent Todoist tasks
to find them easily.
The ↗ icon links straight to your task in Todoist.
Parent tasks:
In a subtask, the project and section are only added to the parent to avoid repetition, since they share the same ones.
If you finish a parent task the same day as its subtasks, it will show up twice. I change the icon from ◐ to ◉ and remove the duplicate to keep things tidy.
Date backlinks to daily notes:
Reflect doesn't support creating backlinks to daily notes from the API yet. Having the opening brackets lets you quickly create them by placing the cursor after each date.
The date at the end of a task is its creation date. It's nice to see when something first came to mind.
The date at the beginning of a comment is when it was posted.
Dates are added only if they're not today's date.
That's it. Feel free to share this guide and drop your questions or issues in the Reflect Discord!