This script automatically sets all events created by Todoist on Google Calendar to public visibility using sync tokens for efficient incremental updates. It automatically finds your Todoist calendar and processes only new or modified events after the first run. Perfect for when the Todoist–Google Calendar integration creates events as private by default.
- ✅ Automatically finds the Todoist calendar by name
- ✅ Uses Google Calendar API sync tokens to process only new or modified events
- ✅ Sets the visibility of private events to
PUBLIC - ✅ Efficient: after the first run, only changed events are processed (no date ranges needed)
- ✅ Works with events scheduled at any date in the future or past
- ✅ Can run periodically via triggers (every 10–15 minutes)
- Google account with access to the calendar connected to Todoist
- A calendar named "Todoist" (or update the script with your calendar name)
- Google Calendar API advanced service enabled in Apps Script
- Permission to modify events on the target calendar
- Go to script.google.com and create a new project
- Click on Services (+ icon) and add Google Calendar API
- Paste the code into the main script file
- If your Todoist calendar has a different name, update
cal.summary === 'Todoist'with the correct name - Save the project (
Todoist to Google Calendar Public Events) - Run
updateTodoistEvents()once to authorize the script
function updateTodoistEvents() {
try {
// List all calendars with error handling
const calendars = Calendar.CalendarList.list();
// Check if we got a valid response
if (!calendars || !calendars.items) {
Logger.log('No calendars found or empty response');
return;
}
// Find the Todoist calendar ID
let todoistId = null;
calendars.items.forEach(cal => {
Logger.log('Found calendar: ' + cal.summary); // Debug log
if (cal.summary === 'Todoist') {
todoistId = cal.id;
}
});
if (!todoistId) {
Logger.log('Todoist calendar not found in available calendars');
return;
}
Logger.log('Processing Todoist calendar: ' + todoistId);
const props = PropertiesService.getUserProperties();
let options = { maxResults: 250 };
// First run: get last 30 days, then only changes
if (!props.getProperty('syncToken')) {
const past = new Date();
past.setDate(past.getDate() - 30);
options.timeMin = past.toISOString();
} else {
options.syncToken = props.getProperty('syncToken');
}
const events = Calendar.Events.list(todoistId, options);
if (events.items && events.items.length > 0) {
Logger.log('Processing ' + events.items.length + ' events');
events.items.forEach(event => {
if (event.status !== 'cancelled' && event.visibility === 'private') {
try {
Calendar.Events.patch({visibility: 'public'}, todoistId, event.id);
Logger.log('Updated event: ' + event.summary);
} catch (e) {
Logger.log('Error updating event ' + event.id + ': ' + e.message);
}
}
});
} else {
Logger.log('No events found to update');
}
if (events.nextSyncToken) {
props.setProperty('syncToken', events.nextSyncToken);
}
} catch (e) {
Logger.log('Error: ' + e.message);
Logger.log('Stack: ' + e.stack);
// If sync token expired, clear it for next run
if (e.message.includes('Sync token') || e.message.includes('sync token')) {
PropertiesService.getUserProperties().deleteProperty('syncToken');
Logger.log('Sync token cleared, will do full sync on next run');
}
}
}- Automatically finds your Todoist calendar by name
- Processes events from the last 30 days
- Sets all private events to public
- Saves a sync token for future runs
- Uses the sync token to retrieve only events created or modified since the last run
- No date ranges needed - processes only changes
- Updates private events to public visibility
- Saves the new sync token
Any event created by Todoist (regardless of when it's scheduled) will be detected and updated within minutes.
- In Apps Script project, open Triggers section
- Add new trigger:
- Function:
updateTodoistEvents - Event source:
Time-driven - Frequency:
Every 10–15 minutes
- Function:
- Save and authorize
| Error | Solution |
|---|---|
"Todoist calendar not found" |
Verify calendar name in Google Calendar, update cal.summary === 'Todoist' |
"API call failed" |
Enable Google Calendar API in Services |
| Sync token errors | Script auto-resets invalid tokens |