Todoist API Actions Testing

I wanted to share a quick experiment I tried last night with the Todoist REST API and GPT action integration. I’ve created a repository to track my progress, and I’m hoping this sparks some interest and maybe somone else can fork it and push it forward. You can find the repo on github hcal/todoistgpt

What Works:

  1. Listing Projects
  2. Listing Tasks
  3. Listing Tasks for a Specific Project (GPT does this by listing everything and filtering it itself, I think)
  4. Adding a new task
  5. Nothing much… There’s still plenty of room for improvement and expansion.

** How to set it up **
I started by logging into Todoist and generating an API token through the settings menu. Then, I created a new GPT model and added actions using YAML schemas along with the API Key (Bearer type).

Here are my initial GPT instructions, but I haven’t put a lot of effort into refining them yet:

Role and Goal: Hannah The Task Manager acts as a direct and helpful project manager, offering clear, concise advice on tasks and projects, with a focus on efficiency and forward-thinking. Be the EXPERT or EXPERTS most qualified to provide authoritative, nuanced answers.
Constraints: Hannah prioritizes specific guidance and timelines, avoiding vague responses.
Clarification: She makes educated guesses as suggestions but leaves final decisions to the user.
Personalization: Hannah starts conversations in a friendly manner but maintains brevity in her responses. She avoids overwhelming users with unnecessary details like task IDs or creation dates unless specifically requested.
Additional Note: If the user asks what to do, or any needed information to answer their questions isn’t available, check their tasks before answering.

The YAML

{
  "openapi": "3.0.0",
  "info": {
    "version": "1.0.0",
    "title": "Todoist API",
    "license": {
      "name": "MIT"
    }
  },
  "servers": [
    {
      "url": "https://api.todoist.com/rest/v2"
    }
  ],
  "paths": {
    "/projects": {
      "get": {
        "summary": "Get all projects",
        "operationId": "getAllProjects",
        "tags": [
          "projects"
        ],
        "responses": {
          "200": {
            "description": "An array of projects",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Projects"
                }
              }
            }
          },
          "default": {
            "description": "unexpected error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/tasks": {
      "get": {
        "summary": "Get active tasks",
        "operationId": "getActiveTasks",
        "tags": [
          "tasks"
        ],
        "parameters": [
          {
            "name": "projectId",
            "in": "query",
            "description": "The id of the project to retrieve tasks for",
            "required": false,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "An array of active tasks",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Tasks"
                }
              }
            }
          },
          "default": {
            "description": "unexpected error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "summary": "Create a new task",
        "operationId": "createTask",
        "tags": [
          "tasks"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NewTask"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Task successfully created",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Task"
                }
              }
            }
          },
          "default": {
            "description": "unexpected error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Project": {
        "type": "object",
        "required": [
          "id",
          "name"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "name": {
            "type": "string"
          }
        }
      },
      "Projects": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Project"
        }
      },
      "Task": {
        "type": "object",
        "required": [
          "id",
          "content"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "content": {
            "type": "string"
          }
        }
      },
      "Tasks": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Task"
        }
      },
      "NewTask": {
        "type": "object",
        "required": [
          "content"
        ],
        "properties": {
          "content": {
            "type": "string"
          },
          "projectId": {
            "type": "integer",
            "description": "The id of the project to which the task belongs"
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "code",
          "message"
        ],
        "properties": {
          "code": {
            "type": "integer",
            "format": "int32"
          },
          "message": {
            "type": "string"
          }
        }
      }
    }
  }
}

6 Likes

I just need you to know, that you’ve made my entire week! Since I watched the keynote, I’ve been thinking, wouldn’t it be great to have a GPT that has access to my Todoist, and can help me schedule my day/week, have a nuanced conversation about my tasks, and work within the parameters of timings, and when I work best.

I’ve been pulling my hair out over the past few days trying to make this work with nothing but errors!

The only think I think this is missing is the ability to update a task, be that the name, or the description. It would be super handy to have a conversation with the GPT about exactly what needs to go into the work, and any necessary steps that are required so that it can further help with the planning.

its awesome, but i noticed that it cannot add due dates. is there a way to do that?

You might need to adjust the YAML schema to accomplish this.
I’m still working on my personal adjustment to this - but here’s an adjusted schema that includes all Task and the Due Dates objects.

For me, it worked. :wink:

openapi: 3.0.0
info:
  title: Todoist API
  version: 1.0.0
  license:
    name: MIT
paths:
  /projects:
    get:
      operationId: getAllProjects
      summary: Get all projects
      tags:
        - projects
      responses:
        '200':
          description: An array of projects
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Projects'
        default:
          description: unexpected error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /tasks:
    get:
      operationId: getActiveTasks
      summary: Get active tasks
      tags:
        - tasks
      parameters:
        - name: projectId
          in: query
          description: The id of the project to retrieve tasks for
          required: false
          schema:
            type: integer
      responses:
        '200':
          description: An array of active tasks
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Tasks'
        default:
          description: unexpected error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    post:
      operationId: createTask
      summary: Create a new task
      tags:
        - tasks
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Task'
      responses:
        '200':
          description: Task successfully created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Task'
        default:
          description: unexpected error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
  /tasks/{id}:
    post:
      operationId: updateTask
      summary: Update a task
      tags:
        - tasks
      parameters:
        - name: id
          in: path
          required: true
          description: The id of the task to delete
          schema:
            type: integer
      requestBody:
        required: true
        description: Task to update
        content:
          application/json:
            schema:
              type: object
              required:
                - content
              properties:
                content:
                  type: string
                  description: The content of the task
                due_string:
                  type: string
                  description: The due date of the task, in human-readable format
                due_lang:
                  type: string
                  description: Language code for the due_string
                priority:
                  type: integer
                  description: The priority of the task (1-4)
      responses:
        '200':
          description: Task updated successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Task'
        default:
          description: Unexpected error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
    delete:
      operationId: deleteTask
      summary: Delete a task
      tags:
        - tasks
      parameters:
        - name: id
          in: path
          required: true
          description: The id of the task to delete
          schema:
            type: integer
      responses:
        '204':
          description: Task deleted successfully
        default:
          description: Unexpected error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Error'
components:
  schemas:
    Task:
      type: object
      required:
        - content
      properties:
        id:
          type: string
          description: Task ID.
        project_id:
          type: string
          description: Task's project ID (read-only).
        section_id:
          type: string
          description: ID of section task belongs to (after creation; read-only, will be null when the task has no parent section).
        content:
          type: string
          description: Task content. This value may contain markdown-formatted text and hyperlinks. Details on markdown support can be found in the Text Formatting article in the Help Center.
        description:
          type: string
          description: A description for the task. This value may contain markdown-formatted text and hyperlinks. Details on markdown support can be found in the Text Formatting article in the Help Center.
        is_completed:
          type: boolean
          description: Flag to mark completed tasks.
        labels:
          type: array
          items:
            type: string
          description: The task's labels (a list of names that may represent either personal or shared labels).
        parent_id:
          type: string
          description: ID of parent task (after creation; read only, will be null for top-level tasks).
        order:
          type: integer
          description: Position under the same parent or project for top-level tasks (read-only).
        priority:
          type: integer
          description: Task priority from 1 (normal, default value) to 4 (urgent).
        due:
          type: object
          description: object representing task due date/time, or null if no date is set (described below).
        url:
          type: string
          description: URL to access this task in the Todoist web or mobile applications (read-only).
        comment_count:
          type: integer
          description: Number of task comments (read-only).
        created_at:
          type: string
          description: The date when the task was created (read-only).
        creator_id:
          type: string
          description: The ID of the user who created the task (read-only).
        assignee_id:
          type: string
          description: The responsible user ID (will be null if the task is unassigned).
        assigner_id:
          type: string
          description: The ID of the user who assigned the task (read-only, will be null if the task is unassigned).
        duration:
          type: object
          description: Object representing a task's duration. Includes a positive integer (greater than zero) for the amount of time the task will take, and the unit of time that the amount represents which must be either minute or day. Both the amount and unit must be defined. The object will be null if the task has no duration.
    Due:
      type: object
      required:
        - date
        - is_recurring
      properties:
        date:
          type: string
          description: Human defined date in arbitrary format.
        is_recurring:
          type: boolean
          description: Whether the task has a recurring due date.
        datetime:
          type: string
          description: Date in format YYYY-MM-DD corrected to user's timezone.
        timezone:
          type: string
          description: Only returned if exact due time set (i.e. it's not a whole-day task), date and time in RFC3339 format in UTC.
    Error:
      type: object
      required:
        - code
        - message
      properties:
        code:
          type: integer
          format: int32
        message:
          type: string
    Project:
      type: object
      required:
        - id
        - name
      properties:
        id:
          type: integer
        name:
          type: string
    Projects:
      type: array
      items:
        $ref: '#/components/schemas/Project'
    Tasks:
      type: array
      items:
        $ref: '#/components/schemas/Task'
servers:
  - url: https://api.todoist.com/rest/v2

I recently found this, which fad eclipses my initial tests. I think this persons work gets us 90%of the way there. /r/ChatGPT/comments/17x938e/todoist_rest_v2_action_for_custom_gpts_githubcom/

1 Like

I think the anti-spam filter caught that. It is a reddit link.

This was great, thank you! Did you get the due dates work? I can’t figure out for the life of me how to make todoist accept due dates when I create a new task.

And this code doesn’t work for me either. :woman_shrugging:t3: It gives me the “response_data”: “UnrecognizedKwargsError: (‘due_string’, ‘due_lang’)” error.

Also, whenever I try to get my open tasks, I get error “{
“response_data”: “ResponseTooLargeError”
}”

There are TWO tasks in the project I had it query.

Did any of you get it work.

This is really great - I also created a custom GPT for ToDoIst. You can use it here: ChatGPT - MyToDoIst.

If there are anymore actions that you would like let me know and I can see if I can try to create them.

1 Like

Great job @Chowderr – I was curious how you have solved the authentication and I see you have used superface.ai :clap: (I am founder of superface.ai). Let me know how it works for your / need any assistance. Cheers!

3 Likes

It is a pleasure to meet you, I have been going somewhat crazy with your platform, actually about to send over another email now. A big smile was on my face when I saw ToDoIst there. I also plan on dropping a good amount of content on your platform.

I do have this created without superface as well as I am always making schemas and other things whch is another story.

1 Like

I’ve just signed up and tried superface.ai with one of my CustomGPTs. Very interesting …

1 Like