Skip to main content

Quick Start

Install the plugin:
pnpm install @corsair-dev/todoist
Add the Todoist plugin to your Corsair instance:
corsair.ts
import { createCorsair } from "corsair";
import { todoist } from "@corsair-dev/todoist";

export const corsair = createCorsair({
    plugins: [
        todoist(),
    ],
});
Once configured, you can manage tasks:
// Create a task
await corsair.todoist.api.tasks.create({
    content: "Buy groceries",
    due_string: "tomorrow",
});

Authentication

Supported Auth Types

The Todoist plugin supports:
  • api_key (default) - Use a Todoist API token

Default Auth Type

If no authType is specified, the plugin defaults to api_key.

Configuring API Key Authentication

Store credentials with the CLI:
pnpm corsair setup --todoist api_key=your-api-token
For webhook signature verification:
pnpm corsair setup --todoist webhook_signature=your-webhook-secret
Alternatively, provide credentials directly in the config:
corsair.ts
todoist({
    key: process.env.TODOIST_API_TOKEN,
    webhookSecret: process.env.TODOIST_WEBHOOK_SECRET,
})
See Authentication for details on managing credentials.

Options

OptionTypeDescription
authType'api_key'Authentication method (defaults to 'api_key')
keystringAPI token (optional, uses database if not provided)
webhookSecretstringWebhook secret for verification
hooksobjectEndpoint hooks for custom logic
webhookHooksobjectWebhook hooks for event handling
errorHandlersobjectCustom error handlers
permissionsobjectPermission configuration for AI agent access

Hooks

todoist({
    hooks: {
        tasks: {
            create: {
                before: async (ctx, input) => {
                    console.log("Creating task:", input.content);
                    return { ctx, input };
                },
            },
        },
    },
})
See Hooks for complete documentation.

Error Handling

The plugin includes built-in error handlers for common scenarios. For complete documentation, see the Error Handlers reference.

Usage

Accessing the API

// Get all active tasks
const tasks = await corsair.todoist.api.tasks.getMany({
    filter: "today | overdue",
});

// Complete a task
await corsair.todoist.api.tasks.close({
    id: "task-id",
});

// Move a task to a project
await corsair.todoist.api.tasks.move({
    id: "task-id",
    project_id: "project-id",
});

// Create a project
await corsair.todoist.api.projects.create({
    name: "New Project",
    color: "blue",
});
See API Endpoints for the complete reference.

Webhooks

Todoist sends task and project events to your webhook endpoint (identified by x-todoist-delivery-id header):
app/api/webhook/route.ts
import { processWebhook } from "corsair";
import { corsair } from "@/server/corsair";

export async function POST(request: Request) {
    const headers = Object.fromEntries(request.headers);
    const body = await request.json();

    const result = await processWebhook(corsair, headers, body);
    return result.response;
}
See Webhooks for all available events.

Database Access

const tasks = await corsair.todoist.db.tasks.search({
    data: { project_id: "project-id" },
});
See Database for the complete schema.

Multi-Tenancy

const tenant = corsair.withTenant("user-123");

await tenant.todoist.api.tasks.create({
    content: "Review pull request",
    project_id: "work-project-id",
});

Examples

Example 1: Create Tasks from Incoming Emails

inngest/functions.ts
export const createTaskFromEmail = inngest.createFunction(
    { id: "email-to-todoist-task" },
    { event: "app/email-received" },
    async ({ event }) => {
        const tenant = corsair.withTenant(event.data.tenantId);

        await tenant.todoist.api.tasks.quickAdd({
            text: event.data.subject, 
        });
    }
);

Example 2: Notify on Task Completion

corsair.ts
import { inngest } from "./inngest";

export const corsair = createCorsair({
    plugins: [
        todoist({
            webhookHooks: {
                items: {
                    completed: {
                        after: async (ctx, result) => {
                            await inngest.send({ 
                                name: "todoist/task-completed", 
                                data: { 
                                    tenantId: ctx.tenantId, 
                                    taskId: result.data.id, 
                                    taskContent: result.data.content, 
                                    projectId: result.data.project_id, 
                                }, 
                            }); 
                        },
                    },
                },
            },
        }),
    ],
});
inngest/functions.ts
export const onTaskCompleted = inngest.createFunction(
    { id: "todoist-task-completed" },
    { event: "todoist/task-completed" },
    async ({ event }) => {
        const tenant = corsair.withTenant(event.data.tenantId);

        await tenant.todoist.api.tasks.create({
            content: `Follow up: ${event.data.taskContent}`, 
            project_id: event.data.projectId,
            due_string: "in 3 days",
        });
    }
);

Example 3: Sync GitHub Issues to Todoist

inngest/functions.ts
export const syncIssueToTodoist = inngest.createFunction(
    { id: "sync-github-issue-to-todoist" },
    { event: "github/issue-assigned" },
    async ({ event }) => {
        const tenant = corsair.withTenant(event.data.tenantId);

        await tenant.todoist.api.tasks.create({
            content: `[GitHub] ${event.data.issueTitle}`, 
            description: event.data.issueUrl,
            labels: ["github"],
            priority: event.data.isPriority ? 4 : 2, 
        });
    }
);