The official TypeScript SDK for the Todoist REST API.
Requires Node 20.18.1+.
npm install @doist/todoist-sdk
An example of initializing the API client and fetching a user's tasks:
import { TodoistApi } from '@doist/todoist-sdk'
const api = new TodoistApi('YOURTOKEN')
api.getTasks()
.then((tasks) => console.log(tasks))
.catch((error) => console.log(error))
For more detailed reference documentation, have a look at the Todoist API v1 Documentation.
If you're migrating from an older version of the Todoist API (v9), please refer to the official migration guide for detailed information about the changes and breaking updates.
Key changes in v1 include:
The Todoist API client supports custom HTTP implementations to enable usage in environments with specific networking requirements, such as:
import { TodoistApi } from '@doist/todoist-sdk'
// Using the new options-based constructor
const api = new TodoistApi('YOURTOKEN', {
baseUrl: 'https://custom-api.example.com', // optional
customFetch: myCustomFetch, // your custom fetch implementation
})
// Legacy constructor (deprecated but supported)
const apiLegacy = new TodoistApi('YOURTOKEN', 'https://custom-api.example.com')
Your custom fetch function must implement this interface:
type CustomFetch = (
url: string,
options?: RequestInit & { timeout?: number },
) => Promise<CustomFetchResponse>
type CustomFetchResponse = {
ok: boolean
status: number
statusText: string
headers: Record<string, string>
text(): Promise<string>
json(): Promise<unknown>
}
OAuth authentication functions (getAuthToken, revokeAuthToken, revokeToken) support custom fetch through an options object:
// New options-based usage
const { accessToken } = await getAuthToken(args, {
baseUrl: 'https://custom-auth.example.com',
customFetch: myCustomFetch,
})
await revokeToken(args, {
customFetch: myCustomFetch,
})
// Legacy usage (deprecated)
const { accessToken } = await getAuthToken(args, baseUrl)
When the authorization server issues a refreshToken, use refreshAuthToken
to obtain a new access token once the current one expires, without sending the
user back through the authorization flow:
const refreshed = await refreshAuthToken({
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
refreshToken: storedRefreshToken,
})
// The server may rotate the refresh token — persist refreshed.refreshToken
// when present, otherwise keep using the previous one.
Note: Refresh tokens are only issued to OAuth apps that have them enabled (the default for new apps). Apps without them receive a long-lived access token, so
getAuthTokenreturns norefreshTokenand there is nothing to refresh. For public clients (tokenEndpointAuthMethod: 'none'), omitclientSecretwhen callingrefreshAuthToken.
Instead of having an example app in the repository to assist development and testing, we have included ts-node as a dev dependency. This allows us to have a scratch file locally that can import and utilize the API while developing or reviewing pull requests without having to manage a separate app project.
npm installscratch.ts in the src folder.ts-node (instructions for VSCode, WebStorm), or you can optionally run ts-node in a terminal using instructions here (npx ts-node ./src/scratch.ts should be enough).Example scratch.ts file:
/* eslint-disable no-console */
import { TodoistApi } from './todoist-api'
const token = 'YOURTOKEN'
const api = new TodoistApi(token)
api.getProjects()
.then((projects) => {
console.log(projects)
})
.catch((error) => console.error(error))
For live API verification, you can run raw requests with a local token:
.env.example to .env.TODOIST_API_TOKEN in .env.npm run api:request -- ....TODOIST_API_BASE_URL in .env (defaults to https://api.todoist.com).Examples:
npm run api:request -- --path /api/v1/tasks
npm run api:request -- --method POST --path /api/v1/tasks --body '{"content":"API smoke test"}'
npm run api:request -- --method POST --path /api/v1/tasks/123 --body '{"due_string":"no date"}'
npm run api:request -- --path /api/v1/tasks --query '{"project_id":"123","limit":10}'
To see all options:
npm run api:request -- --help
This project uses Release Please to automate releases. Releases are created automatically based on Conventional Commits.
When making changes, use conventional commit messages:
feat: - New features (triggers a minor version bump)fix: - Bug fixes (triggers a patch version bump)feat!: or BREAKING CHANGE: - Breaking changes (triggers a major version bump)chore:, docs:, refactor:, perf: - Other changes (included in changelog)Example:
feat: add support for recurring tasks
fix: resolve issue with date parsing
feat!: remove deprecated getTask method
The release process is fully automated:
Automatic PR Creation: When commits are merged to main, Release Please automatically creates or updates a release PR with:
package.jsonCHANGELOG.mdReview and Merge: Review the release PR to ensure the version bump and changelog are correct, then merge it.
Automatic Release: Upon merging the release PR:
publish.yml workflow is triggered by the tagUsers of the API client can then update to the new version in their package.json.
Any feedback, such as bugs, questions, comments, etc. can be reported as Issues in this repository, and will be handled by us in Todoist.
We would also love contributions in the form of Pull requests in this repository.
$ claude mcp add todoist-sdk-typescript \
-- python -m otcore.mcp_server <graph>