From 81ed8e353e7188b16216dc4aa8db047241ee5897 Mon Sep 17 00:00:00 2001 From: Pranav Date: Wed, 7 Oct 2026 17:04:42 -0700 Subject: [PATCH] feat: add Todoist integration --- todoist/README.md | 27 +++++++++ todoist/toolset.yml | 130 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+) create mode 100644 todoist/README.md create mode 100644 todoist/toolset.yml diff --git a/todoist/README.md b/todoist/README.md new file mode 100644 index 0000000..865c554 --- /dev/null +++ b/todoist/README.md @@ -0,0 +1,27 @@ +Connect Todoist to help Captain find the task for a bug or request, check its details and comments, and add a comment when another customer reports it. + +## Setup + +1. In Todoist, go to **Settings → Integrations → Developer** and copy your API token. +2. Enter the token when installing. + +A personal API token has full access to the Todoist account it belongs to and cannot be scoped. Use an account that only holds the projects you want Captain to see. + +## Tools + +- **Search Tasks:** Search active tasks by keyword. Returns up to 10 tasks with priority, due date, labels, and task ID. +- **Get Task:** Check one active task's description, priority, due date, labels, and comment count. +- **List Task Comments:** Read up to 10 comments on a task, newest first. +- **List Projects:** List up to 50 active projects with their project IDs. +- **List Project Tasks:** List up to 25 active tasks in a project. +- **Add Task Comment:** Add a comment to a task, such as a note that another customer reported the same issue. + +Start with Search Tasks, then use the task ID with the other tools. + +## Notes + +- These tools use the unified Todoist API v1 (`https://api.todoist.com/api/v1`). +- Completed tasks are not returned by Search Tasks, Get Task, or List Project Tasks. +- Tasks show their project as an ID; use List Projects to match it to a name. +- Priority runs from 1 (normal) to 4 (urgent). Todoist's apps show priority 4 as "P1". +- Add Task Comment posts as the account the token belongs to. diff --git a/todoist/toolset.yml b/todoist/toolset.yml new file mode 100644 index 0000000..afa74af --- /dev/null +++ b/todoist/toolset.yml @@ -0,0 +1,130 @@ +version: 0.0.1 +kind: captain_toolset +name: Todoist +author: chatwoot +category: Others +description: Search Todoist tasks, check their details and comments, and add comments to tasks. + +secrets: + api_token: + label: Todoist API token + type: password + required: true + +tools: + - id: search_tasks + title: Search Tasks + description: Search active Todoist tasks by keyword, such as a bug or request a customer mentions. Use this first to find the task ID the other tools need. Returns up to 10 tasks; completed tasks are not included. + http_method: GET + endpoint_url: 'https://api.todoist.com/api/v1/tasks/filter?query=search%3A%20{{ keyword | replace: "&", " " | replace: "|", " " | replace: ",", " " | replace: "(", " " | replace: ")", " " | replace: "!", " " | strip | url_encode }}&limit=10' + auth_type: bearer + auth_config: + token: ${{ secrets.api_token }} + param_schema: + - name: keyword + type: string + description: One or two distinctive words to look for in task names. + required: true + response_template: |- + {% if response.results %}{% for task in response.results limit: 10 %}- {{ task.content | truncate: 150 }} (task ID {{ task.id }}) · Priority {{ task.priority }} of 4{% if task.due.date %} · Due {{ task.due.date | slice: 0, 10 }}{% endif %}{% if task.labels.size > 0 %} · Labels: {{ task.labels | join: ", " }}{% endif %} + Project ID: {{ task.project_id }} · Comments: {{ task.note_count | default: 0 }}{% if task.updated_at %} · Updated {{ task.updated_at | slice: 0, 10 }}{% endif %} + {% else %}No active tasks found matching this search. + {% endfor %}{% if response.next_cursor %}More tasks match; use a more specific keyword to narrow the results.{% endif %}{% else %}Todoist returned an unexpected response.{% endif %} + + - id: get_task + title: Get Task + description: Get one active Todoist task's details, including its description, priority, due date, labels, and comment count. Use a task ID from Search Tasks or List Project Tasks. + http_method: GET + endpoint_url: https://api.todoist.com/api/v1/tasks/{{ task_id | strip | url_encode }} + auth_type: bearer + auth_config: + token: ${{ secrets.api_token }} + param_schema: + - name: task_id + type: string + description: The Todoist task ID from Search Tasks or List Project Tasks. + required: true + response_template: |- + {% if response.id %}{{ response.content }} (task ID {{ response.id }}) · {% if response.checked %}completed{% else %}active{% endif %} + Priority: {{ response.priority }} of 4 (4 is most urgent){% if response.due.date %} + Due: {{ response.due.date | slice: 0, 10 }}{% if response.due.is_recurring %} (recurring: {{ response.due.string }}){% endif %}{% endif %}{% if response.deadline.date %} + Deadline: {{ response.deadline.date }}{% endif %}{% if response.labels.size > 0 %} + Labels: {{ response.labels | join: ", " }}{% endif %} + Project ID: {{ response.project_id }}{% if response.parent_id %} + Parent task ID: {{ response.parent_id }}{% endif %} + Created: {{ response.added_at | slice: 0, 10 }}{% if response.updated_at %} · Updated: {{ response.updated_at | slice: 0, 10 }}{% endif %} + Comments: {{ response.note_count | default: 0 }}{% if response.description and response.description != "" %} + Description: {{ response.description | truncate: 1500 }}{% endif %}{% else %}Todoist returned an unexpected response.{% endif %} + + - id: list_task_comments + title: List Task Comments + description: Read up to 10 comments on a Todoist task, such as progress notes. Use a task ID from Search Tasks or List Project Tasks. + http_method: GET + endpoint_url: https://api.todoist.com/api/v1/comments?task_id={{ task_id | strip | url_encode }}&limit=50 + auth_type: bearer + auth_config: + token: ${{ secrets.api_token }} + param_schema: + - name: task_id + type: string + description: The Todoist task ID from Search Tasks or List Project Tasks. + required: true + response_template: |- + {% if response.results %}{% assign comments = response.results | sort: "posted_at" | reverse %}{% for comment in comments limit: 10 %}- {{ comment.posted_at | slice: 0, 10 }}: {{ comment.content | newline_to_br | replace: "
", " " | strip_newlines | truncate: 500 }}{% if comment.file_attachment.file_name %} (attachment: {{ comment.file_attachment.file_name }}){% endif %} + {% else %}No comments on this task. + {% endfor %}{% else %}Todoist returned an unexpected response.{% endif %} + + - id: list_projects + title: List Projects + description: List up to 50 active Todoist projects with their project IDs. Use this to find the project ID that List Project Tasks needs, or to name the project a task belongs to. + http_method: GET + endpoint_url: https://api.todoist.com/api/v1/projects?limit=50 + auth_type: bearer + auth_config: + token: ${{ secrets.api_token }} + response_template: |- + {% if response.results %}{% for project in response.results limit: 50 %}- {{ project.name }} (project ID {{ project.id }}){% if project.inbox_project %} · Inbox{% endif %}{% if project.is_shared %} · shared{% endif %}{% if project.description and project.description != "" %} + {{ project.description | newline_to_br | replace: "
", " " | strip_newlines | truncate: 150 }}{% endif %} + {% else %}No active projects found. + {% endfor %}{% if response.next_cursor %}More projects exist than the 50 shown.{% endif %}{% else %}Todoist returned an unexpected response.{% endif %} + + - id: list_project_tasks + title: List Project Tasks + description: List up to 25 active tasks in a Todoist project, to see what is open there. Use a project ID from List Projects. + http_method: GET + endpoint_url: https://api.todoist.com/api/v1/tasks?project_id={{ project_id | strip | url_encode }}&limit=25 + auth_type: bearer + auth_config: + token: ${{ secrets.api_token }} + param_schema: + - name: project_id + type: string + description: The Todoist project ID from List Projects. + required: true + response_template: |- + {% if response.results %}{% for task in response.results limit: 25 %}- {{ task.content | truncate: 150 }} (task ID {{ task.id }}) · Priority {{ task.priority }} of 4{% if task.due.date %} · Due {{ task.due.date | slice: 0, 10 }}{% endif %}{% if task.labels.size > 0 %} · Labels: {{ task.labels | join: ", " }}{% endif %} + Project ID: {{ task.project_id }} · Comments: {{ task.note_count | default: 0 }}{% if task.updated_at %} · Updated {{ task.updated_at | slice: 0, 10 }}{% endif %} + {% else %}No active tasks in this project. + {% endfor %}{% if response.next_cursor %}More tasks exist than the 25 shown.{% endif %}{% else %}Todoist returned an unexpected response.{% endif %} + + - id: add_task_comment + title: Add Task Comment + description: Add a comment to a Todoist task, for example to note that another customer reported the same issue, with a short summary. Use a task ID from Search Tasks. Only add a comment when the conversation needs one. + http_method: POST + endpoint_url: https://api.todoist.com/api/v1/comments + auth_type: bearer + auth_config: + token: ${{ secrets.api_token }} + param_schema: + - name: task_id + type: string + description: The Todoist task ID from Search Tasks or List Project Tasks. + required: true + - name: content + type: string + description: The comment text. Todoist renders Markdown. + required: true + request_template: |- + {% capture bs %}\{% endcapture %}{% capture escaped_bs %}\\\\{% endcapture %}{% capture escaped_quote %}\"{% endcapture %}{% capture escaped_newline %}\\n{% endcapture %}{"task_id":"{{ task_id | strip_newlines | strip | replace: bs, escaped_bs | replace: '"', escaped_quote }}","content":"{{ content | strip | replace: bs, escaped_bs | replace: '"', escaped_quote | newline_to_br | strip_newlines | replace: "
", escaped_newline }}"} + response_template: |- + {% if response.id %}Added the comment to the task.{% else %}Todoist returned an unexpected response.{% endif %}