TodoistSDK

An unofficial Haskell SDK for the Todoist REST API. Manage projects, tasks, comments, sections, and labels with type-safe, ergonomic Haskell functions.
Features
- Complete API Coverage: Projects, Tasks, Comments, Sections, and Labels
- Type-Safe: Leverages Haskell's type system to prevent common errors
- Ergonomic Builder Pattern: Easily construct API requests with optional fields
- Automatic Pagination: Transparently fetches all pages for list operations
- Flexible Error Handling: Operations return
Either TodoistError a for explicit error handling
- Testing Support (In Progress): Includes Trace interpreter for testing without API calls
Installation
Using Stack
Add to your stack.yaml:
extra-deps:
- todoist-sdk-0.1.0.0
Add to your package.yaml or .cabal file:
dependencies:
- todoist-sdk
Using Cabal
Add to your .cabal file's build-depends:
build-depends:
base >=4.7 && <5
, todoist-sdk
Then install:
cabal update
cabal install todoist-sdk
Quick Start
Get your API token from Todoist Settings → Integrations → Developer.
Try it in the REPL
$ stack repl
>>> import Web.Todoist
>>> let config = newTodoistConfig "your-api-token-here"
>>> result <- todoist config getAllProjects
>>> case result of
Left err -> print err
Right projects -> mapM_ print projects
Complete Example
Create a file example.hs:
#!/usr/bin/env stack
-- stack --resolver lts-24.7 script --package todoist-sdk --package text
{-# LANGUAGE OverloadedStrings #-}
import Web.Todoist
main :: IO ()
main = do
-- Configure with your API token
let config = newTodoistConfig "your-api-token-here"
result <- todoist config $ do
-- Create a new project
let newProj = runBuilder (newProject "My Haskell Project")
(setDescription "Learning Haskell SDK" <> setViewStyle Board)
project <- addProject newProj
-- Create a task in the project
let newTask = runBuilder (newTask "Read documentation")
(setProjectId (_id project) <> setPriority 2)
task <- addTask newTask
-- Get all tasks
tasks <- getTasks emptyTaskParam
pure (project, task, tasks)
case result of
Left err -> putStrLn $ "Error: " ++ show err
Right (proj, task, tasks) -> do
putStrLn $ "Created project: " ++ show (_name proj)
putStrLn $ "Created task: " ++ show (_content task)
putStrLn $ "Total tasks: " ++ show (length tasks)
Run it:
chmod +x example.hs
./example.hs
Common Usage Examples
Working with Projects
import Web.Todoist
let config = newTodoistConfig "your-api-token"
-- Get all projects
result <- todoist config getAllProjects
-- Create a project with optional fields
let newProject = runBuilder (newProject "Shopping List")
(setColor "blue" <> setViewStyle List <> setIsFavorite True)
project <- todoist config (addProject newProject)
-- Update a project
let update = runBuilder emptyProjectUpdate (setName "Updated Name")
updated <- todoist config (updateProject update projectId)
-- Delete a project
todoist config (deleteProject projectId)
Working with Tasks
-- Create a task with due date
let task = runBuilder (newTask "Buy milk")
(setProjectId "project-123"
<> setDueString "tomorrow"
<> setPriority 3
<> setLabelIds ["grocery", "urgent"])
result <- todoist config (addTask task)
-- Get tasks with filters
let params = TaskParam
{ project_id = Just "project-123"
, filter = Nothing
, label_id = Nothing
, cursor = Nothing
, limit = Nothing
}
tasks <- todoist config (getTasks params)
-- Complete a task
todoist config (closeTask taskId)
-- Update a task
let update = runBuilder emptyTaskPatch (setContent "Buy 2% milk")
updated <- todoist config (updateTask update taskId)
-- Add a comment to a task
let comment = runBuilder (newComment "Don't forget organic!")
(setTaskId "task-456")
result <- todoist config (addComment comment)
-- Get all comments for a project
let params = CommentParam
{ project_id = Just "project-123"
, task_id = Nothing
, cursor = Nothing
, limit = Nothing
, public_key = Nothing
}
comments <- todoist config (getComments params)
Working with Sections
-- Create a section
let section = runBuilder (newSection "In Progress" "project-123") mempty
result <- todoist config (addSection section)
-- Get sections for a project with builder pattern
let params = runBuilder newSectionParam (setProjectId "project-123" <> setLimit 50)
sections <- todoist config (getSections params)
Working with Labels
-- Create a label
let label = runBuilder (newLabel "urgent") mempty
result <- todoist config (addLabel label)
-- Get all labels
let params = runBuilder newLabelParam (setLimit 50)
labels <- todoist config (getLabels params)
Error Handling
All operations return Either TodoistError a. The TodoistError type includes:
BadRequest - Invalid request parameters
Unauthorized - Invalid or missing API token
Forbidden - Insufficient permissions
NotFound - Resource doesn't exist
HttpError String - Other HTTP errors
Example:
result <- todoist config (getProject projectId)
case result of
Left BadRequest -> putStrLn "Invalid project ID"
Left Unauthorized -> putStrLn "Check your API token"
Left NotFound -> putStrLn "Project not found"
Left (HttpError msg) -> putStrLn $ "HTTP error: " ++ msg
Right project -> print project
Documentation
Full API documentation is available on Hackage.
For details on the Todoist REST API, see the official documentation.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Acknowledgments
This library is a labor of love to the Haskell community and to the Todoist app. It is an unofficial SDK and is not affiliated with or endorsed by Doist.