@doist/todoist-api-typescript 7.4.0 → 7.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +2 -0
  2. package/dist/cjs/authentication.js +50 -6
  3. package/dist/cjs/consts/endpoints.js +63 -1
  4. package/dist/cjs/test-utils/test-defaults.js +33 -1
  5. package/dist/cjs/todoist-api.js +1130 -168
  6. package/dist/cjs/{utils → transport}/fetch-with-retry.js +23 -71
  7. package/dist/cjs/{rest-client.js → transport/http-client.js} +5 -5
  8. package/dist/cjs/transport/http-dispatcher.js +72 -0
  9. package/dist/cjs/types/entities.js +124 -1
  10. package/dist/cjs/types/errors.js +9 -1
  11. package/dist/cjs/types/http.js +3 -1
  12. package/dist/cjs/types/requests.js +24 -0
  13. package/dist/cjs/types/sync/commands/shared.js +2 -8
  14. package/dist/cjs/types/sync/resources/reminders.js +2 -0
  15. package/dist/cjs/utils/multipart-upload.js +1 -1
  16. package/dist/cjs/utils/validators.js +19 -2
  17. package/dist/esm/authentication.js +47 -4
  18. package/dist/esm/consts/endpoints.js +52 -0
  19. package/dist/esm/test-utils/test-defaults.js +32 -0
  20. package/dist/esm/todoist-api.js +1061 -99
  21. package/dist/esm/{utils → transport}/fetch-with-retry.js +23 -38
  22. package/dist/esm/{rest-client.js → transport/http-client.js} +5 -5
  23. package/dist/esm/transport/http-dispatcher.js +35 -0
  24. package/dist/esm/types/entities.js +122 -0
  25. package/dist/esm/types/errors.js +7 -0
  26. package/dist/esm/types/http.js +3 -1
  27. package/dist/esm/types/requests.js +23 -1
  28. package/dist/esm/types/sync/commands/shared.js +1 -8
  29. package/dist/esm/types/sync/resources/reminders.js +2 -0
  30. package/dist/esm/utils/multipart-upload.js +1 -1
  31. package/dist/esm/utils/validators.js +19 -2
  32. package/dist/types/authentication.d.ts +51 -6
  33. package/dist/types/consts/endpoints.d.ts +31 -0
  34. package/dist/types/test-utils/test-defaults.d.ts +5 -1
  35. package/dist/types/todoist-api.d.ts +338 -6
  36. package/dist/types/{utils → transport}/fetch-with-retry.d.ts +1 -1
  37. package/dist/types/{rest-client.d.ts → transport/http-client.d.ts} +1 -1
  38. package/dist/types/transport/http-dispatcher.d.ts +3 -0
  39. package/dist/types/types/entities.d.ts +258 -14
  40. package/dist/types/types/errors.d.ts +4 -0
  41. package/dist/types/types/requests.d.ts +537 -43
  42. package/dist/types/types/sync/commands/projects.d.ts +2 -2
  43. package/dist/types/types/sync/commands/shared.d.ts +1 -4
  44. package/dist/types/types/sync/resources/reminders.d.ts +4 -0
  45. package/dist/types/types/sync/resources/user.d.ts +2 -2
  46. package/dist/types/types/sync/resources/view-options.d.ts +5 -5
  47. package/dist/types/types/sync/user-preferences.d.ts +1 -1
  48. package/dist/types/utils/validators.d.ts +202 -6
  49. package/package.json +4 -4
@@ -9,17 +9,21 @@ var __rest = (this && this.__rest) || function (s, e) {
9
9
  }
10
10
  return t;
11
11
  };
12
- import { request, isSuccess } from './rest-client.js';
13
- import { getSyncBaseUri, ENDPOINT_REST_TASKS, ENDPOINT_REST_TASKS_FILTER, ENDPOINT_REST_TASKS_COMPLETED_BY_COMPLETION_DATE, ENDPOINT_REST_TASKS_COMPLETED_BY_DUE_DATE, ENDPOINT_REST_TASKS_COMPLETED_SEARCH, ENDPOINT_REST_PROJECTS, ENDPOINT_REST_PROJECTS_SEARCH, ENDPOINT_SYNC_QUICK_ADD, ENDPOINT_REST_TASK_CLOSE, ENDPOINT_REST_TASK_REOPEN, ENDPOINT_REST_TASK_MOVE, ENDPOINT_REST_LABELS, ENDPOINT_REST_LABELS_SEARCH, ENDPOINT_REST_PROJECT_COLLABORATORS, ENDPOINT_REST_SECTIONS, ENDPOINT_REST_SECTIONS_SEARCH, ENDPOINT_REST_COMMENTS, ENDPOINT_REST_LABELS_SHARED, ENDPOINT_REST_LABELS_SHARED_RENAME, ENDPOINT_REST_LABELS_SHARED_REMOVE, ENDPOINT_SYNC, PROJECT_ARCHIVE, PROJECT_UNARCHIVE, ENDPOINT_REST_PROJECTS_MOVE_TO_WORKSPACE, ENDPOINT_REST_PROJECTS_MOVE_TO_PERSONAL, ENDPOINT_REST_PROJECTS_ARCHIVED, ENDPOINT_REST_USER, ENDPOINT_REST_PRODUCTIVITY, ENDPOINT_REST_ACTIVITIES, ENDPOINT_REST_UPLOADS, ENDPOINT_WORKSPACE_INVITATIONS, ENDPOINT_WORKSPACE_INVITATIONS_ALL, ENDPOINT_WORKSPACE_INVITATIONS_DELETE, getWorkspaceInvitationAcceptEndpoint, getWorkspaceInvitationRejectEndpoint, ENDPOINT_WORKSPACE_JOIN, ENDPOINT_WORKSPACE_LOGO, ENDPOINT_WORKSPACE_PLAN_DETAILS, ENDPOINT_WORKSPACE_USERS, getWorkspaceActiveProjectsEndpoint, getWorkspaceArchivedProjectsEndpoint, } from './consts/endpoints.js';
14
- import { validateAttachment, validateComment, validateCommentArray, validateCurrentUser, validateLabel, validateLabelArray, validateProject, validateProjectArray, validateSection, validateSectionArray, validateTask, validateTaskArray, validateUserArray, validateProductivityStats, validateActivityEventArray, validateWorkspaceUserArray, validateWorkspaceInvitation, validateWorkspaceInvitationArray, validateWorkspacePlanDetails, validateJoinWorkspaceResult, validateWorkspaceArray, } from './utils/validators.js';
12
+ import { DueDateSchema, } from './types/entities.js';
13
+ import { LOCATION_TRIGGERS } from './types/sync/resources/reminders.js';
14
+ import { REMINDER_DELIVERY_SERVICES, } from './types/requests.js';
15
+ import { request, isSuccess } from './transport/http-client.js';
16
+ import { getSyncBaseUri, ENDPOINT_REST_TASKS, ENDPOINT_REST_TASKS_FILTER, ENDPOINT_REST_TASKS_COMPLETED_BY_COMPLETION_DATE, ENDPOINT_REST_TASKS_COMPLETED_BY_DUE_DATE, ENDPOINT_REST_TASKS_COMPLETED_SEARCH, ENDPOINT_REST_TASKS_COMPLETED, ENDPOINT_REST_TEMPLATES_FILE, ENDPOINT_REST_TEMPLATES_URL, ENDPOINT_REST_TEMPLATES_CREATE_FROM_FILE, ENDPOINT_REST_TEMPLATES_IMPORT_FROM_FILE, ENDPOINT_REST_TEMPLATES_IMPORT_FROM_ID, ENDPOINT_REST_PROJECTS, ENDPOINT_REST_PROJECTS_SEARCH, ENDPOINT_SYNC_QUICK_ADD, ENDPOINT_REST_TASK_CLOSE, ENDPOINT_REST_TASK_REOPEN, ENDPOINT_REST_TASK_MOVE, ENDPOINT_REST_LABELS, ENDPOINT_REST_LABELS_SEARCH, ENDPOINT_REST_PROJECT_COLLABORATORS, ENDPOINT_REST_SECTIONS, ENDPOINT_REST_SECTIONS_SEARCH, ENDPOINT_REST_COMMENTS, ENDPOINT_REST_LOCATION_REMINDERS, ENDPOINT_REST_REMINDERS, ENDPOINT_REST_LABELS_SHARED, ENDPOINT_REST_LABELS_SHARED_RENAME, ENDPOINT_REST_LABELS_SHARED_REMOVE, ENDPOINT_SYNC, PROJECT_ARCHIVE, PROJECT_UNARCHIVE, ENDPOINT_REST_PROJECTS_MOVE_TO_WORKSPACE, ENDPOINT_REST_PROJECTS_MOVE_TO_PERSONAL, ENDPOINT_REST_PROJECTS_ARCHIVED, ENDPOINT_REST_PROJECTS_ARCHIVED_COUNT, ENDPOINT_REST_PROJECTS_PERMISSIONS, ENDPOINT_REST_PROJECT_FULL, ENDPOINT_REST_PROJECT_JOIN, SECTION_ARCHIVE, SECTION_UNARCHIVE, ENDPOINT_REST_USER, ENDPOINT_REST_PRODUCTIVITY, ENDPOINT_REST_ACTIVITIES, ENDPOINT_REST_UPLOADS, getProjectInsightsActivityStatsEndpoint, getProjectInsightsHealthEndpoint, getProjectInsightsHealthContextEndpoint, getProjectInsightsProgressEndpoint, getProjectInsightsHealthAnalyzeEndpoint, getWorkspaceInsightsEndpoint, ENDPOINT_REST_BACKUPS, ENDPOINT_REST_BACKUPS_DOWNLOAD, ENDPOINT_REST_EMAILS, ENDPOINT_REST_ID_MAPPINGS, ENDPOINT_REST_MOVED_IDS, ENDPOINT_REST_WORKSPACES, ENDPOINT_WORKSPACE_MEMBERS, getWorkspaceUserTasksEndpoint, getWorkspaceInviteUsersEndpoint, getWorkspaceUserEndpoint, ENDPOINT_WORKSPACE_INVITATIONS, ENDPOINT_WORKSPACE_INVITATIONS_ALL, ENDPOINT_WORKSPACE_INVITATIONS_DELETE, getWorkspaceInvitationAcceptEndpoint, getWorkspaceInvitationRejectEndpoint, ENDPOINT_WORKSPACE_JOIN, ENDPOINT_WORKSPACE_LOGO, ENDPOINT_WORKSPACE_PLAN_DETAILS, ENDPOINT_WORKSPACE_USERS, getWorkspaceActiveProjectsEndpoint, getWorkspaceArchivedProjectsEndpoint, } from './consts/endpoints.js';
17
+ import { validateAttachment, validateComment, validateCommentArray, validateCurrentUser, validateLabel, validateLabelArray, validateProject, validateProjectArray, validateSection, validateSectionArray, validateTask, validateTaskArray, validateUserArray, validateProductivityStats, validateReminder, validateReminderArray, validateLocationReminderArray, validateActivityEventArray, validateWorkspaceUserArray, validateWorkspaceInvitation, validateWorkspaceInvitationArray, validateWorkspacePlanDetails, validateJoinWorkspaceResult, validateWorkspace, validateWorkspaceArray, validateMemberActivityInfoArray, validateWorkspaceUserTaskArray, validateProjectActivityStats, validateProjectHealth, validateProjectHealthContext, validateProjectProgress, validateWorkspaceInsights, validateBackupArray, validateIdMappingArray, validateMovedIdArray, } from './utils/validators.js';
15
18
  import { formatDateToYYYYMMDD } from './utils/url-helpers.js';
16
19
  import { uploadMultipartFile } from './utils/multipart-upload.js';
20
+ import { camelCaseKeys } from './utils/case-conversion.js';
17
21
  import { normalizeObjectEventTypeForApi, denormalizeObjectTypeFromApi, } from './utils/activity-helpers.js';
18
22
  import { processTaskContent } from './utils/uncompletable-helpers.js';
19
23
  import { z } from 'zod';
20
24
  import { v4 as uuidv4 } from 'uuid';
21
25
  import { DATE_FORMAT_TO_API, TIME_FORMAT_TO_API, DAY_OF_WEEK_TO_API, } from './types/sync/index.js';
22
- import { TodoistRequestError } from './types/index.js';
26
+ import { TodoistArgumentError, TodoistRequestError } from './types/index.js';
23
27
  const MAX_COMMAND_COUNT = 100;
24
28
  /**
25
29
  * Joins path segments using `/` separator.
@@ -80,6 +84,56 @@ function headersToRecord(headers) {
80
84
  });
81
85
  return result;
82
86
  }
87
+ const ReminderDeliveryServiceSchema = z.enum(REMINDER_DELIVERY_SERVICES);
88
+ const ReminderIdSchema = z.string();
89
+ const ReminderDueDateSchema = DueDateSchema.pick({
90
+ date: true,
91
+ string: true,
92
+ timezone: true,
93
+ lang: true,
94
+ isRecurring: true,
95
+ })
96
+ .partial()
97
+ .strict();
98
+ const UpdateRelativeReminderArgsSchema = z
99
+ .object({
100
+ reminderType: z.literal('relative'),
101
+ minuteOffset: z.number().int().optional(),
102
+ notifyUid: z.string().optional(),
103
+ service: ReminderDeliveryServiceSchema.optional(),
104
+ isUrgent: z.boolean().optional(),
105
+ })
106
+ .strict();
107
+ const UpdateAbsoluteReminderArgsSchema = z
108
+ .object({
109
+ reminderType: z.literal('absolute'),
110
+ due: ReminderDueDateSchema.optional(),
111
+ notifyUid: z.string().optional(),
112
+ service: ReminderDeliveryServiceSchema.optional(),
113
+ isUrgent: z.boolean().optional(),
114
+ })
115
+ .strict();
116
+ const UpdateLocationReminderArgsSchema = z
117
+ .object({
118
+ notifyUid: z.string().optional(),
119
+ name: z.string().optional(),
120
+ locLat: z.string().optional(),
121
+ locLong: z.string().optional(),
122
+ locTrigger: z.enum(LOCATION_TRIGGERS).optional(),
123
+ radius: z.number().int().optional(),
124
+ })
125
+ .strict()
126
+ .refine((args) => Object.values(args).some((value) => value !== undefined), {
127
+ message: 'At least one reminder field must be provided to updateLocationReminder',
128
+ });
129
+ const UpdateReminderArgsSchema = z
130
+ .discriminatedUnion('reminderType', [
131
+ UpdateRelativeReminderArgsSchema,
132
+ UpdateAbsoluteReminderArgsSchema,
133
+ ])
134
+ .refine((args) => Object.entries(args).some(([key, value]) => key !== 'reminderType' && value !== undefined), {
135
+ message: 'At least one reminder field must be provided to updateReminder',
136
+ });
83
137
  export class TodoistApi {
84
138
  constructor(
85
139
  /**
@@ -291,6 +345,30 @@ export class TodoistApi {
291
345
  nextCursor,
292
346
  };
293
347
  }
348
+ /**
349
+ * Retrieves all completed tasks with optional filters.
350
+ *
351
+ * Uses offset-based pagination rather than cursor-based.
352
+ *
353
+ * @param args - Optional parameters including project ID, label, date range, and pagination.
354
+ * @returns A promise that resolves to completed tasks with associated project and section data.
355
+ */
356
+ async getAllCompletedTasks(args = {}) {
357
+ const { since, until } = args, rest = __rest(args, ["since", "until"]);
358
+ const { data } = await request({
359
+ httpMethod: 'GET',
360
+ baseUri: this.syncApiBase,
361
+ relativePath: ENDPOINT_REST_TASKS_COMPLETED,
362
+ apiToken: this.authToken,
363
+ customFetch: this.customFetch,
364
+ payload: Object.assign(Object.assign(Object.assign({}, rest), (since ? { since: since.toISOString() } : {})), (until ? { until: until.toISOString() } : {})),
365
+ });
366
+ return {
367
+ projects: data.projects,
368
+ sections: data.sections,
369
+ items: validateTaskArray(data.items),
370
+ };
371
+ }
294
372
  /**
295
373
  * Creates a new task with the provided parameters.
296
374
  *
@@ -683,6 +761,83 @@ export class TodoistApi {
683
761
  });
684
762
  return validateProject(response.data.project);
685
763
  }
764
+ /**
765
+ * Counts the number of archived projects.
766
+ *
767
+ * @param args - Optional parameters to filter the count.
768
+ * @returns A promise that resolves to the count of archived projects.
769
+ */
770
+ async getArchivedProjectsCount(args = {}) {
771
+ const { data } = await request({
772
+ httpMethod: 'GET',
773
+ baseUri: this.syncApiBase,
774
+ relativePath: ENDPOINT_REST_PROJECTS_ARCHIVED_COUNT,
775
+ apiToken: this.authToken,
776
+ customFetch: this.customFetch,
777
+ payload: args,
778
+ });
779
+ return data;
780
+ }
781
+ /**
782
+ * Retrieves the role-to-action permission mappings for projects.
783
+ *
784
+ * @returns A promise that resolves to the permission mappings.
785
+ */
786
+ async getProjectPermissions() {
787
+ const { data } = await request({
788
+ httpMethod: 'GET',
789
+ baseUri: this.syncApiBase,
790
+ relativePath: ENDPOINT_REST_PROJECTS_PERMISSIONS,
791
+ apiToken: this.authToken,
792
+ customFetch: this.customFetch,
793
+ });
794
+ return data;
795
+ }
796
+ /**
797
+ * Retrieves full project data including tasks, sections, collaborators, and notes.
798
+ *
799
+ * @param id - The unique identifier of the project.
800
+ * @param args - Optional parameters.
801
+ * @returns A promise that resolves to the full project data.
802
+ */
803
+ async getFullProject(id, args = {}) {
804
+ z.string().parse(id);
805
+ const { data } = await request({
806
+ httpMethod: 'GET',
807
+ baseUri: this.syncApiBase,
808
+ relativePath: generatePath(ENDPOINT_REST_PROJECTS, id, ENDPOINT_REST_PROJECT_FULL),
809
+ apiToken: this.authToken,
810
+ customFetch: this.customFetch,
811
+ payload: args,
812
+ });
813
+ return {
814
+ project: data.project ? validateProject(data.project) : null,
815
+ commentsCount: data.commentsCount,
816
+ tasks: validateTaskArray(data.tasks),
817
+ sections: validateSectionArray(data.sections),
818
+ collaborators: validateUserArray(data.collaborators),
819
+ notes: validateCommentArray(data.notes),
820
+ };
821
+ }
822
+ /**
823
+ * Joins a shared project by its ID.
824
+ *
825
+ * @param id - The unique identifier of the project to join.
826
+ * @param requestId - Optional custom identifier for the request.
827
+ * @returns A promise that resolves to the joined project.
828
+ */
829
+ async joinProject(id, requestId) {
830
+ z.string().parse(id);
831
+ const response = await request({
832
+ httpMethod: 'POST',
833
+ baseUri: this.syncApiBase,
834
+ relativePath: generatePath(ENDPOINT_REST_PROJECTS, id, ENDPOINT_REST_PROJECT_JOIN),
835
+ apiToken: this.authToken,
836
+ customFetch: this.customFetch,
837
+ requestId: requestId,
838
+ });
839
+ return validateProject(response.data);
840
+ }
686
841
  /**
687
842
  * Retrieves a list of collaborators for a specific project.
688
843
  *
@@ -705,6 +860,116 @@ export class TodoistApi {
705
860
  nextCursor,
706
861
  };
707
862
  }
863
+ // ── Insights ──
864
+ /**
865
+ * Retrieves activity statistics for a project.
866
+ *
867
+ * @param projectId - The unique identifier of the project.
868
+ * @param args - Optional parameters including weeks and weekly counts flag.
869
+ * @returns A promise that resolves to the project activity stats.
870
+ */
871
+ async getProjectActivityStats(projectId, args = {}) {
872
+ z.string().parse(projectId);
873
+ const response = await request({
874
+ httpMethod: 'GET',
875
+ baseUri: this.syncApiBase,
876
+ relativePath: getProjectInsightsActivityStatsEndpoint(projectId),
877
+ apiToken: this.authToken,
878
+ customFetch: this.customFetch,
879
+ payload: Object.assign({ objectType: 'ITEM', eventType: 'COMPLETED' }, args),
880
+ });
881
+ return validateProjectActivityStats(response.data);
882
+ }
883
+ /**
884
+ * Retrieves the health status of a project.
885
+ *
886
+ * @param projectId - The unique identifier of the project.
887
+ * @returns A promise that resolves to the project health data.
888
+ */
889
+ async getProjectHealth(projectId) {
890
+ z.string().parse(projectId);
891
+ const response = await request({
892
+ httpMethod: 'GET',
893
+ baseUri: this.syncApiBase,
894
+ relativePath: getProjectInsightsHealthEndpoint(projectId),
895
+ apiToken: this.authToken,
896
+ customFetch: this.customFetch,
897
+ });
898
+ return validateProjectHealth(response.data);
899
+ }
900
+ /**
901
+ * Retrieves the health context for a project, including metrics and task details.
902
+ *
903
+ * @param projectId - The unique identifier of the project.
904
+ * @returns A promise that resolves to the project health context.
905
+ */
906
+ async getProjectHealthContext(projectId) {
907
+ z.string().parse(projectId);
908
+ const response = await request({
909
+ httpMethod: 'GET',
910
+ baseUri: this.syncApiBase,
911
+ relativePath: getProjectInsightsHealthContextEndpoint(projectId),
912
+ apiToken: this.authToken,
913
+ customFetch: this.customFetch,
914
+ });
915
+ return validateProjectHealthContext(response.data);
916
+ }
917
+ /**
918
+ * Retrieves progress information for a project.
919
+ *
920
+ * @param projectId - The unique identifier of the project.
921
+ * @returns A promise that resolves to the project progress data.
922
+ */
923
+ async getProjectProgress(projectId) {
924
+ z.string().parse(projectId);
925
+ const response = await request({
926
+ httpMethod: 'GET',
927
+ baseUri: this.syncApiBase,
928
+ relativePath: getProjectInsightsProgressEndpoint(projectId),
929
+ apiToken: this.authToken,
930
+ customFetch: this.customFetch,
931
+ });
932
+ return validateProjectProgress(response.data);
933
+ }
934
+ /**
935
+ * Retrieves insights for all projects in a workspace.
936
+ *
937
+ * @param workspaceId - The unique identifier of the workspace.
938
+ * @param args - Optional parameters including project IDs filter.
939
+ * @returns A promise that resolves to workspace insights data.
940
+ */
941
+ async getWorkspaceInsights(workspaceId, args = {}) {
942
+ z.string().parse(workspaceId);
943
+ const response = await request({
944
+ httpMethod: 'GET',
945
+ baseUri: this.syncApiBase,
946
+ relativePath: getWorkspaceInsightsEndpoint(workspaceId),
947
+ apiToken: this.authToken,
948
+ customFetch: this.customFetch,
949
+ payload: Object.assign(Object.assign({}, args), (args.projectIds ? { projectIds: args.projectIds.join(',') } : {})),
950
+ });
951
+ return validateWorkspaceInsights(response.data);
952
+ }
953
+ /**
954
+ * Triggers a health analysis for a project.
955
+ *
956
+ * @param projectId - The unique identifier of the project.
957
+ * @param requestId - Optional custom identifier for the request.
958
+ * @returns A promise that resolves to the updated project health data.
959
+ */
960
+ async analyzeProjectHealth(projectId, requestId) {
961
+ z.string().parse(projectId);
962
+ const response = await request({
963
+ httpMethod: 'POST',
964
+ baseUri: this.syncApiBase,
965
+ relativePath: getProjectInsightsHealthAnalyzeEndpoint(projectId),
966
+ apiToken: this.authToken,
967
+ customFetch: this.customFetch,
968
+ requestId: requestId,
969
+ });
970
+ return validateProjectHealth(response.data);
971
+ }
972
+ // ── Sections ──
708
973
  /**
709
974
  * Retrieves all sections within a specific project or matching criteria.
710
975
  *
@@ -822,6 +1087,44 @@ export class TodoistApi {
822
1087
  });
823
1088
  return isSuccess(response);
824
1089
  }
1090
+ /**
1091
+ * Archives a section by its ID.
1092
+ *
1093
+ * @param id - The unique identifier of the section to archive.
1094
+ * @param requestId - Optional custom identifier for the request.
1095
+ * @returns A promise that resolves to the updated section.
1096
+ */
1097
+ async archiveSection(id, requestId) {
1098
+ z.string().parse(id);
1099
+ const response = await request({
1100
+ httpMethod: 'POST',
1101
+ baseUri: this.syncApiBase,
1102
+ relativePath: generatePath(ENDPOINT_REST_SECTIONS, id, SECTION_ARCHIVE),
1103
+ apiToken: this.authToken,
1104
+ customFetch: this.customFetch,
1105
+ requestId: requestId,
1106
+ });
1107
+ return validateSection(response.data);
1108
+ }
1109
+ /**
1110
+ * Unarchives a section by its ID.
1111
+ *
1112
+ * @param id - The unique identifier of the section to unarchive.
1113
+ * @param requestId - Optional custom identifier for the request.
1114
+ * @returns A promise that resolves to the updated section.
1115
+ */
1116
+ async unarchiveSection(id, requestId) {
1117
+ z.string().parse(id);
1118
+ const response = await request({
1119
+ httpMethod: 'POST',
1120
+ baseUri: this.syncApiBase,
1121
+ relativePath: generatePath(ENDPOINT_REST_SECTIONS, id, SECTION_UNARCHIVE),
1122
+ apiToken: this.authToken,
1123
+ customFetch: this.customFetch,
1124
+ requestId: requestId,
1125
+ });
1126
+ return validateSection(response.data);
1127
+ }
825
1128
  /**
826
1129
  * Retrieves a label by its ID.
827
1130
  *
@@ -1086,117 +1389,359 @@ export class TodoistApi {
1086
1389
  return isSuccess(response);
1087
1390
  }
1088
1391
  /**
1089
- * Retrieves productivity stats for the authenticated user.
1392
+ * Retrieves a paginated list of time-based reminders.
1090
1393
  *
1091
- * @returns A promise that resolves to the productivity stats.
1394
+ * @param args - Optional parameters including task ID filter and pagination.
1395
+ * @returns A promise that resolves to a paginated list of reminders.
1092
1396
  */
1093
- async getProductivityStats() {
1094
- const response = await request({
1397
+ async getReminders(args = {}) {
1398
+ const { data: { results, nextCursor }, } = await request({
1095
1399
  httpMethod: 'GET',
1096
1400
  baseUri: this.syncApiBase,
1097
- relativePath: ENDPOINT_REST_PRODUCTIVITY,
1401
+ relativePath: ENDPOINT_REST_REMINDERS,
1098
1402
  apiToken: this.authToken,
1099
1403
  customFetch: this.customFetch,
1404
+ payload: args,
1100
1405
  });
1101
- return validateProductivityStats(response.data);
1406
+ return {
1407
+ results: validateReminderArray(results),
1408
+ nextCursor,
1409
+ };
1102
1410
  }
1103
1411
  /**
1104
- * Retrieves activity logs with optional filters.
1412
+ * Retrieves a paginated list of location-based reminders.
1105
1413
  *
1106
- * @param args - Optional filter parameters for activity logs.
1107
- * @returns A promise that resolves to a paginated response of activity events.
1414
+ * @param args - Optional parameters including task ID filter and pagination.
1415
+ * @returns A promise that resolves to a paginated list of location reminders.
1108
1416
  */
1109
- async getActivityLogs(args = {}) {
1110
- // Convert Date objects to YYYY-MM-DD strings
1111
- const dateFrom = args.dateFrom instanceof Date ? formatDateToYYYYMMDD(args.dateFrom) : args.dateFrom;
1112
- const dateTo = args.dateTo instanceof Date ? formatDateToYYYYMMDD(args.dateTo) : args.dateTo;
1113
- // Destructure out raw date, filter-type, and removed legacy fields so they don't leak into payload
1114
- const _a = args, { dateFrom: _dateFrom, dateTo: _dateTo, objectEventTypes, objectType: _objectType, eventType: _eventType, since: _since, until: _until } = _a, rest = __rest(_a, ["dateFrom", "dateTo", "objectEventTypes", "objectType", "eventType", "since", "until"]);
1115
- // Build normalized objectEventTypes for the API
1116
- let normalizedObjectEventTypes;
1117
- if (objectEventTypes !== undefined) {
1118
- const arr = Array.isArray(objectEventTypes) ? objectEventTypes : [objectEventTypes];
1119
- normalizedObjectEventTypes = arr.map(normalizeObjectEventTypeForApi);
1120
- }
1121
- const processedArgs = Object.assign(Object.assign(Object.assign(Object.assign({}, rest), (dateFrom !== undefined ? { dateFrom } : {})), (dateTo !== undefined ? { dateTo } : {})), (normalizedObjectEventTypes !== undefined
1122
- ? { objectEventTypes: normalizedObjectEventTypes }
1123
- : {}));
1417
+ async getLocationReminders(args = {}) {
1124
1418
  const { data: { results, nextCursor }, } = await request({
1125
1419
  httpMethod: 'GET',
1126
1420
  baseUri: this.syncApiBase,
1127
- relativePath: ENDPOINT_REST_ACTIVITIES,
1421
+ relativePath: ENDPOINT_REST_LOCATION_REMINDERS,
1128
1422
  apiToken: this.authToken,
1129
1423
  customFetch: this.customFetch,
1130
- payload: processedArgs,
1131
- });
1132
- // Convert legacy API object types back to modern SDK types
1133
- const normalizedResults = results.map((event) => {
1134
- const normalizedType = denormalizeObjectTypeFromApi(event.objectType);
1135
- return Object.assign(Object.assign({}, event), { objectType: normalizedType || event.objectType });
1424
+ payload: args,
1136
1425
  });
1137
1426
  return {
1138
- results: validateActivityEventArray(normalizedResults),
1427
+ results: validateLocationReminderArray(results),
1139
1428
  nextCursor,
1140
1429
  };
1141
1430
  }
1142
1431
  /**
1143
- * Uploads a file and returns attachment metadata.
1144
- * This creates an upload record that can be referenced in tasks or comments.
1145
- *
1146
- * @param args - Upload parameters including file content, filename, and optional project ID.
1147
- * @param requestId - Optional custom identifier for the request.
1148
- * @returns A promise that resolves to the uploaded file's attachment metadata.
1149
- *
1150
- * @example
1151
- * ```typescript
1152
- * // Upload from a file path
1153
- * const upload = await api.uploadFile({
1154
- * file: '/path/to/document.pdf',
1155
- * projectId: '12345'
1156
- * })
1157
- *
1158
- * // Upload from a Buffer
1159
- * const buffer = fs.readFileSync('/path/to/document.pdf')
1160
- * const upload = await api.uploadFile({
1161
- * file: buffer,
1162
- * fileName: 'document.pdf', // Required for Buffer/Stream
1163
- * projectId: '12345'
1164
- * })
1432
+ * Retrieves a time-based reminder by its ID.
1165
1433
  *
1166
- * // Use the returned fileUrl in a comment
1167
- * await api.addComment({
1168
- * content: 'See attached document',
1169
- * taskId: '67890',
1170
- * attachment: {
1171
- * fileUrl: upload.fileUrl,
1172
- * fileName: upload.fileName,
1173
- * fileType: upload.fileType,
1174
- * resourceType: upload.resourceType
1175
- * }
1176
- * })
1177
- * ```
1434
+ * @param id - The unique identifier of the reminder to retrieve.
1435
+ * @returns A promise that resolves to the requested reminder.
1178
1436
  */
1179
- async uploadFile(args, requestId) {
1180
- const additionalFields = {};
1181
- if (args.projectId) {
1182
- additionalFields.project_id = args.projectId;
1437
+ async getReminder(id) {
1438
+ ReminderIdSchema.parse(id);
1439
+ try {
1440
+ const response = await request({
1441
+ httpMethod: 'GET',
1442
+ baseUri: this.syncApiBase,
1443
+ relativePath: generatePath(ENDPOINT_REST_REMINDERS, id),
1444
+ apiToken: this.authToken,
1445
+ customFetch: this.customFetch,
1446
+ });
1447
+ return validateReminder(response.data);
1448
+ }
1449
+ catch (error) {
1450
+ if (!(error instanceof TodoistRequestError) || error.httpStatusCode !== 404) {
1451
+ throw error;
1452
+ }
1453
+ throw new TodoistArgumentError(`Reminder ${id} was not found on the time-based reminder endpoint. If this is a location reminder, use getLocationReminder instead.`);
1183
1454
  }
1184
- const data = await uploadMultipartFile({
1185
- baseUrl: this.syncApiBase,
1186
- authToken: this.authToken,
1187
- endpoint: ENDPOINT_REST_UPLOADS,
1188
- file: args.file,
1189
- fileName: args.fileName,
1190
- additionalFields: additionalFields,
1191
- requestId: requestId,
1192
- customFetch: this.customFetch,
1193
- });
1194
- return validateAttachment(data);
1195
1455
  }
1196
1456
  /**
1197
- * Deletes an uploaded file by its URL.
1457
+ * Retrieves a location reminder by its ID.
1198
1458
  *
1199
- * @param args - The file URL to delete.
1459
+ * @param id - The unique identifier of the location reminder to retrieve.
1460
+ * @returns A promise that resolves to the requested reminder.
1461
+ */
1462
+ async getLocationReminder(id) {
1463
+ ReminderIdSchema.parse(id);
1464
+ try {
1465
+ const response = await request({
1466
+ httpMethod: 'GET',
1467
+ baseUri: this.syncApiBase,
1468
+ relativePath: generatePath(ENDPOINT_REST_LOCATION_REMINDERS, id),
1469
+ apiToken: this.authToken,
1470
+ customFetch: this.customFetch,
1471
+ });
1472
+ return validateReminder(response.data);
1473
+ }
1474
+ catch (error) {
1475
+ if (!(error instanceof TodoistRequestError) || error.httpStatusCode !== 404) {
1476
+ throw error;
1477
+ }
1478
+ throw new TodoistArgumentError(`Location reminder ${id} was not found on the location reminder endpoint. If this is a time-based reminder, use getReminder instead.`);
1479
+ }
1480
+ }
1481
+ /**
1482
+ * Creates a time-based reminder for a task.
1483
+ *
1484
+ * @param args - Reminder creation parameters for relative or absolute reminders.
1485
+ * @param requestId - Optional custom identifier for the request.
1486
+ * @returns A promise that resolves to the created reminder.
1487
+ */
1488
+ async addReminder(args, requestId) {
1489
+ const response = await request({
1490
+ httpMethod: 'POST',
1491
+ baseUri: this.syncApiBase,
1492
+ relativePath: ENDPOINT_REST_REMINDERS,
1493
+ apiToken: this.authToken,
1494
+ customFetch: this.customFetch,
1495
+ payload: args,
1496
+ requestId: requestId,
1497
+ });
1498
+ return validateReminder(response.data);
1499
+ }
1500
+ /**
1501
+ * Creates a location reminder for a task.
1502
+ *
1503
+ * @param args - Location reminder creation parameters.
1504
+ * @param requestId - Optional custom identifier for the request.
1505
+ * @returns A promise that resolves to the created reminder.
1506
+ */
1507
+ async addLocationReminder(args, requestId) {
1508
+ const response = await request({
1509
+ httpMethod: 'POST',
1510
+ baseUri: this.syncApiBase,
1511
+ relativePath: ENDPOINT_REST_LOCATION_REMINDERS,
1512
+ apiToken: this.authToken,
1513
+ customFetch: this.customFetch,
1514
+ payload: Object.assign(Object.assign({}, args), { reminderType: 'location' }),
1515
+ requestId: requestId,
1516
+ });
1517
+ return validateReminder(response.data);
1518
+ }
1519
+ /**
1520
+ * Updates an existing time-based reminder.
1521
+ *
1522
+ * @param id - The unique identifier of the reminder to update.
1523
+ * @param args - Reminder update parameters.
1524
+ * @param requestId - Optional custom identifier for the request.
1525
+ * @returns A promise that resolves to the updated reminder.
1526
+ */
1527
+ async updateReminder(id, args, requestId) {
1528
+ ReminderIdSchema.parse(id);
1529
+ const payload = UpdateReminderArgsSchema.parse(args);
1530
+ try {
1531
+ const response = await request({
1532
+ httpMethod: 'POST',
1533
+ baseUri: this.syncApiBase,
1534
+ relativePath: generatePath(ENDPOINT_REST_REMINDERS, id),
1535
+ apiToken: this.authToken,
1536
+ customFetch: this.customFetch,
1537
+ payload,
1538
+ requestId: requestId,
1539
+ });
1540
+ return validateReminder(response.data);
1541
+ }
1542
+ catch (error) {
1543
+ if (!(error instanceof TodoistRequestError) || error.httpStatusCode !== 404) {
1544
+ throw error;
1545
+ }
1546
+ throw new TodoistArgumentError(`Reminder ${id} was not found on the time-based reminder endpoint. If this is a location reminder, use updateLocationReminder instead.`);
1547
+ }
1548
+ }
1549
+ /**
1550
+ * Updates an existing location reminder.
1551
+ *
1552
+ * @param id - The unique identifier of the location reminder to update.
1553
+ * @param args - Location reminder update parameters.
1554
+ * @param requestId - Optional custom identifier for the request.
1555
+ * @returns A promise that resolves to the updated reminder.
1556
+ */
1557
+ async updateLocationReminder(id, args, requestId) {
1558
+ ReminderIdSchema.parse(id);
1559
+ const payload = UpdateLocationReminderArgsSchema.parse(args);
1560
+ try {
1561
+ const response = await request({
1562
+ httpMethod: 'POST',
1563
+ baseUri: this.syncApiBase,
1564
+ relativePath: generatePath(ENDPOINT_REST_LOCATION_REMINDERS, id),
1565
+ apiToken: this.authToken,
1566
+ customFetch: this.customFetch,
1567
+ payload,
1568
+ requestId: requestId,
1569
+ });
1570
+ return validateReminder(response.data);
1571
+ }
1572
+ catch (error) {
1573
+ if (!(error instanceof TodoistRequestError) || error.httpStatusCode !== 404) {
1574
+ throw error;
1575
+ }
1576
+ throw new TodoistArgumentError(`Location reminder ${id} was not found on the location reminder endpoint. If this is a time-based reminder, use updateReminder instead.`);
1577
+ }
1578
+ }
1579
+ /**
1580
+ * Deletes a time-based reminder by its ID.
1581
+ *
1582
+ * @param id - The unique identifier of the reminder to delete.
1583
+ * @param requestId - Optional custom identifier for the request.
1584
+ * @returns A promise that resolves to `true` if successful.
1585
+ */
1586
+ async deleteReminder(id, requestId) {
1587
+ ReminderIdSchema.parse(id);
1588
+ try {
1589
+ const response = await request({
1590
+ httpMethod: 'DELETE',
1591
+ baseUri: this.syncApiBase,
1592
+ relativePath: generatePath(ENDPOINT_REST_REMINDERS, id),
1593
+ apiToken: this.authToken,
1594
+ customFetch: this.customFetch,
1595
+ requestId: requestId,
1596
+ });
1597
+ return isSuccess(response);
1598
+ }
1599
+ catch (error) {
1600
+ if (!(error instanceof TodoistRequestError) || error.httpStatusCode !== 404) {
1601
+ throw error;
1602
+ }
1603
+ throw new TodoistArgumentError(`Reminder ${id} was not found on the time-based reminder endpoint. If this is a location reminder, use deleteLocationReminder instead.`);
1604
+ }
1605
+ }
1606
+ /**
1607
+ * Deletes a location reminder by its ID.
1608
+ *
1609
+ * @param id - The unique identifier of the location reminder to delete.
1610
+ * @param requestId - Optional custom identifier for the request.
1611
+ * @returns A promise that resolves to `true` if successful.
1612
+ */
1613
+ async deleteLocationReminder(id, requestId) {
1614
+ ReminderIdSchema.parse(id);
1615
+ try {
1616
+ const response = await request({
1617
+ httpMethod: 'DELETE',
1618
+ baseUri: this.syncApiBase,
1619
+ relativePath: generatePath(ENDPOINT_REST_LOCATION_REMINDERS, id),
1620
+ apiToken: this.authToken,
1621
+ customFetch: this.customFetch,
1622
+ requestId: requestId,
1623
+ });
1624
+ return isSuccess(response);
1625
+ }
1626
+ catch (error) {
1627
+ if (!(error instanceof TodoistRequestError) || error.httpStatusCode !== 404) {
1628
+ throw error;
1629
+ }
1630
+ throw new TodoistArgumentError(`Location reminder ${id} was not found on the location reminder endpoint. If this is a time-based reminder, use deleteReminder instead.`);
1631
+ }
1632
+ }
1633
+ /**
1634
+ * Retrieves productivity stats for the authenticated user.
1635
+ *
1636
+ * @returns A promise that resolves to the productivity stats.
1637
+ */
1638
+ async getProductivityStats() {
1639
+ const response = await request({
1640
+ httpMethod: 'GET',
1641
+ baseUri: this.syncApiBase,
1642
+ relativePath: ENDPOINT_REST_PRODUCTIVITY,
1643
+ apiToken: this.authToken,
1644
+ customFetch: this.customFetch,
1645
+ });
1646
+ return validateProductivityStats(response.data);
1647
+ }
1648
+ /**
1649
+ * Retrieves activity logs with optional filters.
1650
+ *
1651
+ * @param args - Optional filter parameters for activity logs.
1652
+ * @returns A promise that resolves to a paginated response of activity events.
1653
+ */
1654
+ async getActivityLogs(args = {}) {
1655
+ // Convert Date objects to YYYY-MM-DD strings
1656
+ const dateFrom = args.dateFrom instanceof Date ? formatDateToYYYYMMDD(args.dateFrom) : args.dateFrom;
1657
+ const dateTo = args.dateTo instanceof Date ? formatDateToYYYYMMDD(args.dateTo) : args.dateTo;
1658
+ // Destructure out raw date, filter-type, and removed legacy fields so they don't leak into payload
1659
+ const _a = args, { dateFrom: _dateFrom, dateTo: _dateTo, objectEventTypes, objectType: _objectType, eventType: _eventType, since: _since, until: _until } = _a, rest = __rest(_a, ["dateFrom", "dateTo", "objectEventTypes", "objectType", "eventType", "since", "until"]);
1660
+ // Build normalized objectEventTypes for the API
1661
+ let normalizedObjectEventTypes;
1662
+ if (objectEventTypes !== undefined) {
1663
+ const arr = Array.isArray(objectEventTypes) ? objectEventTypes : [objectEventTypes];
1664
+ normalizedObjectEventTypes = arr.map(normalizeObjectEventTypeForApi);
1665
+ }
1666
+ const processedArgs = Object.assign(Object.assign(Object.assign(Object.assign({}, rest), (dateFrom !== undefined ? { dateFrom } : {})), (dateTo !== undefined ? { dateTo } : {})), (normalizedObjectEventTypes !== undefined
1667
+ ? { objectEventTypes: normalizedObjectEventTypes }
1668
+ : {}));
1669
+ const { data: { results, nextCursor }, } = await request({
1670
+ httpMethod: 'GET',
1671
+ baseUri: this.syncApiBase,
1672
+ relativePath: ENDPOINT_REST_ACTIVITIES,
1673
+ apiToken: this.authToken,
1674
+ customFetch: this.customFetch,
1675
+ payload: processedArgs,
1676
+ });
1677
+ // Convert legacy API object types back to modern SDK types
1678
+ const normalizedResults = results.map((event) => {
1679
+ const normalizedType = denormalizeObjectTypeFromApi(event.objectType);
1680
+ return Object.assign(Object.assign({}, event), { objectType: normalizedType || event.objectType });
1681
+ });
1682
+ return {
1683
+ results: validateActivityEventArray(normalizedResults),
1684
+ nextCursor,
1685
+ };
1686
+ }
1687
+ /**
1688
+ * Uploads a file and returns attachment metadata.
1689
+ * This creates an upload record that can be referenced in tasks or comments.
1690
+ *
1691
+ * @param args - Upload parameters including file content, filename, and optional project ID.
1692
+ * @param requestId - Optional custom identifier for the request.
1693
+ * @returns A promise that resolves to the uploaded file's attachment metadata.
1694
+ *
1695
+ * @example
1696
+ * ```typescript
1697
+ * // Upload from a file path
1698
+ * const upload = await api.uploadFile({
1699
+ * file: '/path/to/document.pdf',
1700
+ * projectId: '12345'
1701
+ * })
1702
+ *
1703
+ * // Upload from a Buffer
1704
+ * const buffer = fs.readFileSync('/path/to/document.pdf')
1705
+ * const upload = await api.uploadFile({
1706
+ * file: buffer,
1707
+ * fileName: 'document.pdf', // Required for Buffer/Stream
1708
+ * projectId: '12345'
1709
+ * })
1710
+ *
1711
+ * // Use the returned fileUrl in a comment
1712
+ * await api.addComment({
1713
+ * content: 'See attached document',
1714
+ * taskId: '67890',
1715
+ * attachment: {
1716
+ * fileUrl: upload.fileUrl,
1717
+ * fileName: upload.fileName,
1718
+ * fileType: upload.fileType,
1719
+ * resourceType: upload.resourceType
1720
+ * }
1721
+ * })
1722
+ * ```
1723
+ */
1724
+ async uploadFile(args, requestId) {
1725
+ const additionalFields = {};
1726
+ if (args.projectId) {
1727
+ additionalFields.project_id = args.projectId;
1728
+ }
1729
+ const data = await uploadMultipartFile({
1730
+ baseUrl: this.syncApiBase,
1731
+ authToken: this.authToken,
1732
+ endpoint: ENDPOINT_REST_UPLOADS,
1733
+ file: args.file,
1734
+ fileName: args.fileName,
1735
+ additionalFields: additionalFields,
1736
+ requestId: requestId,
1737
+ customFetch: this.customFetch,
1738
+ });
1739
+ return validateAttachment(data);
1740
+ }
1741
+ /**
1742
+ * Deletes an uploaded file by its URL.
1743
+ *
1744
+ * @param args - The file URL to delete.
1200
1745
  * @param requestId - Optional custom identifier for the request.
1201
1746
  * @returns A promise that resolves to `true` if deletion was successful.
1202
1747
  *
@@ -1296,6 +1841,246 @@ export class TodoistApi {
1296
1841
  arrayBuffer: () => response.arrayBuffer(),
1297
1842
  };
1298
1843
  }
1844
+ // ── Backups ──
1845
+ /**
1846
+ * Retrieves a list of available backups.
1847
+ *
1848
+ * @param args - Optional parameters including MFA token.
1849
+ * @returns A promise that resolves to an array of backups.
1850
+ */
1851
+ async getBackups(args = {}) {
1852
+ const response = await request({
1853
+ httpMethod: 'GET',
1854
+ baseUri: this.syncApiBase,
1855
+ relativePath: ENDPOINT_REST_BACKUPS,
1856
+ apiToken: this.authToken,
1857
+ customFetch: this.customFetch,
1858
+ payload: args,
1859
+ });
1860
+ return validateBackupArray(response.data);
1861
+ }
1862
+ /**
1863
+ * Downloads a backup file as binary data.
1864
+ *
1865
+ * @param args - Arguments including the backup file URL (from getBackups).
1866
+ * @returns A promise that resolves to a response with binary data accessible via arrayBuffer().
1867
+ */
1868
+ async downloadBackup(args) {
1869
+ const url = `${this.syncApiBase}${ENDPOINT_REST_BACKUPS_DOWNLOAD}?file=${encodeURIComponent(args.file)}`;
1870
+ const fetchOptions = {
1871
+ headers: { Authorization: `Bearer ${this.authToken}` },
1872
+ };
1873
+ if (this.customFetch) {
1874
+ const response = await this.customFetch(url, fetchOptions);
1875
+ if (!response.ok) {
1876
+ throw new Error(`Failed to download backup: ${response.status} ${response.statusText}`);
1877
+ }
1878
+ const text = await response.text();
1879
+ const buffer = new TextEncoder().encode(text).buffer;
1880
+ return {
1881
+ ok: response.ok,
1882
+ status: response.status,
1883
+ statusText: response.statusText,
1884
+ headers: response.headers,
1885
+ text: () => Promise.resolve(text),
1886
+ json: () => response.json(),
1887
+ arrayBuffer: () => Promise.resolve(buffer),
1888
+ };
1889
+ }
1890
+ const response = await fetch(url, fetchOptions);
1891
+ if (!response.ok) {
1892
+ throw new Error(`Failed to download backup: ${response.status} ${response.statusText}`);
1893
+ }
1894
+ return {
1895
+ ok: response.ok,
1896
+ status: response.status,
1897
+ statusText: response.statusText,
1898
+ headers: headersToRecord(response.headers),
1899
+ text: () => response.text(),
1900
+ json: () => response.json(),
1901
+ arrayBuffer: () => response.arrayBuffer(),
1902
+ };
1903
+ }
1904
+ // ── Emails ──
1905
+ /**
1906
+ * Gets or creates an email forwarding address for an object.
1907
+ *
1908
+ * @param args - Arguments including object type and ID.
1909
+ * @param requestId - Optional custom identifier for the request.
1910
+ * @returns A promise that resolves to the email address.
1911
+ */
1912
+ async getOrCreateEmailForwarding(args, requestId) {
1913
+ const { data } = await request({
1914
+ httpMethod: 'PUT',
1915
+ baseUri: this.syncApiBase,
1916
+ relativePath: ENDPOINT_REST_EMAILS,
1917
+ apiToken: this.authToken,
1918
+ customFetch: this.customFetch,
1919
+ payload: args,
1920
+ requestId: requestId,
1921
+ });
1922
+ return data;
1923
+ }
1924
+ /**
1925
+ * Disables email forwarding for an object.
1926
+ *
1927
+ * @param args - Arguments including object type and ID.
1928
+ * @param requestId - Optional custom identifier for the request.
1929
+ * @returns A promise that resolves to `true` if successful.
1930
+ */
1931
+ async disableEmailForwarding(args, requestId) {
1932
+ const queryParams = new URLSearchParams({
1933
+ obj_type: args.objType,
1934
+ obj_id: args.objId,
1935
+ });
1936
+ const response = await request({
1937
+ httpMethod: 'DELETE',
1938
+ baseUri: this.syncApiBase,
1939
+ relativePath: `${ENDPOINT_REST_EMAILS}?${queryParams.toString()}`,
1940
+ apiToken: this.authToken,
1941
+ customFetch: this.customFetch,
1942
+ requestId: requestId,
1943
+ });
1944
+ return isSuccess(response);
1945
+ }
1946
+ // ── ID Mappings ──
1947
+ /**
1948
+ * Retrieves ID mappings between old and new IDs.
1949
+ *
1950
+ * @param args - Arguments including object type and IDs to look up.
1951
+ * @returns A promise that resolves to an array of ID mappings.
1952
+ */
1953
+ async getIdMappings(args) {
1954
+ const response = await request({
1955
+ httpMethod: 'GET',
1956
+ baseUri: this.syncApiBase,
1957
+ relativePath: generatePath(ENDPOINT_REST_ID_MAPPINGS, args.objName, args.objIds.join(',')),
1958
+ apiToken: this.authToken,
1959
+ customFetch: this.customFetch,
1960
+ });
1961
+ return validateIdMappingArray(response.data);
1962
+ }
1963
+ /**
1964
+ * Retrieves moved IDs for objects that have been migrated.
1965
+ *
1966
+ * @param args - Arguments including object type and optional old IDs to look up.
1967
+ * @returns A promise that resolves to an array of moved ID pairs.
1968
+ */
1969
+ async getMovedIds(args) {
1970
+ const response = await request({
1971
+ httpMethod: 'GET',
1972
+ baseUri: this.syncApiBase,
1973
+ relativePath: generatePath(ENDPOINT_REST_MOVED_IDS, args.objName),
1974
+ apiToken: this.authToken,
1975
+ customFetch: this.customFetch,
1976
+ payload: args.oldIds ? { oldIds: args.oldIds.join(',') } : undefined,
1977
+ });
1978
+ return validateMovedIdArray(response.data);
1979
+ }
1980
+ // ── Templates ──
1981
+ /**
1982
+ * Exports a project as a template file (CSV format).
1983
+ *
1984
+ * @param args - Arguments including project ID and optional date format preference.
1985
+ * @returns A promise that resolves to the template file content as a string.
1986
+ */
1987
+ async exportTemplateAsFile(args) {
1988
+ const response = await request({
1989
+ httpMethod: 'GET',
1990
+ baseUri: this.syncApiBase,
1991
+ relativePath: ENDPOINT_REST_TEMPLATES_FILE,
1992
+ apiToken: this.authToken,
1993
+ customFetch: this.customFetch,
1994
+ payload: args,
1995
+ });
1996
+ return response.data;
1997
+ }
1998
+ /**
1999
+ * Exports a project as a template URL.
2000
+ *
2001
+ * @param args - Arguments including project ID and optional date format preference.
2002
+ * @returns A promise that resolves to the file name and URL.
2003
+ */
2004
+ async exportTemplateAsUrl(args) {
2005
+ const { data } = await request({
2006
+ httpMethod: 'GET',
2007
+ baseUri: this.syncApiBase,
2008
+ relativePath: ENDPOINT_REST_TEMPLATES_URL,
2009
+ apiToken: this.authToken,
2010
+ customFetch: this.customFetch,
2011
+ payload: args,
2012
+ });
2013
+ return data;
2014
+ }
2015
+ /**
2016
+ * Creates a new project from a template file.
2017
+ *
2018
+ * @param args - Arguments including project name, template file, and optional workspace ID.
2019
+ * @param requestId - Optional custom identifier for the request.
2020
+ * @returns A promise that resolves to the created project data.
2021
+ */
2022
+ async createProjectFromTemplate(args, requestId) {
2023
+ const { file, fileName, name, workspaceId } = args;
2024
+ const additionalFields = { name };
2025
+ if (workspaceId !== undefined && workspaceId !== null) {
2026
+ additionalFields.workspace_id = workspaceId;
2027
+ }
2028
+ const data = await uploadMultipartFile({
2029
+ baseUrl: this.syncApiBase,
2030
+ authToken: this.authToken,
2031
+ endpoint: ENDPOINT_REST_TEMPLATES_CREATE_FROM_FILE,
2032
+ file,
2033
+ fileName,
2034
+ additionalFields,
2035
+ customFetch: this.customFetch,
2036
+ requestId,
2037
+ });
2038
+ return this.validateTemplateResponse(camelCaseKeys(data));
2039
+ }
2040
+ /**
2041
+ * Imports a template file into an existing project.
2042
+ *
2043
+ * @param args - Arguments including project ID and template file.
2044
+ * @param requestId - Optional custom identifier for the request.
2045
+ * @returns A promise that resolves to the import result.
2046
+ */
2047
+ async importTemplateIntoProject(args, requestId) {
2048
+ const { file, fileName, projectId } = args;
2049
+ const data = await uploadMultipartFile({
2050
+ baseUrl: this.syncApiBase,
2051
+ authToken: this.authToken,
2052
+ endpoint: ENDPOINT_REST_TEMPLATES_IMPORT_FROM_FILE,
2053
+ file,
2054
+ fileName,
2055
+ additionalFields: { project_id: projectId },
2056
+ customFetch: this.customFetch,
2057
+ requestId,
2058
+ });
2059
+ return this.validateTemplateResponse(camelCaseKeys(data));
2060
+ }
2061
+ /**
2062
+ * Imports a template by ID into an existing project.
2063
+ *
2064
+ * @param args - Arguments including project ID, template ID, and optional locale.
2065
+ * @param requestId - Optional custom identifier for the request.
2066
+ * @returns A promise that resolves to the import result.
2067
+ */
2068
+ async importTemplateFromId(args, requestId) {
2069
+ const { data } = await request({
2070
+ httpMethod: 'POST',
2071
+ baseUri: this.syncApiBase,
2072
+ relativePath: ENDPOINT_REST_TEMPLATES_IMPORT_FROM_ID,
2073
+ apiToken: this.authToken,
2074
+ customFetch: this.customFetch,
2075
+ payload: args,
2076
+ requestId: requestId,
2077
+ });
2078
+ return this.validateTemplateResponse(data);
2079
+ }
2080
+ validateTemplateResponse(data) {
2081
+ var _a, _b, _c, _d;
2082
+ return Object.assign(Object.assign({}, data), { projects: validateProjectArray((_a = data.projects) !== null && _a !== void 0 ? _a : []), sections: validateSectionArray((_b = data.sections) !== null && _b !== void 0 ? _b : []), tasks: validateTaskArray((_c = data.tasks) !== null && _c !== void 0 ? _c : []), comments: validateCommentArray((_d = data.comments) !== null && _d !== void 0 ? _d : []) });
2083
+ }
1299
2084
  /* Workspace methods */
1300
2085
  /**
1301
2086
  * Gets pending invitations for a workspace.
@@ -1520,8 +2305,6 @@ export class TodoistApi {
1520
2305
  /**
1521
2306
  * Retrieves all workspaces for the authenticated user.
1522
2307
  *
1523
- * Uses the Sync API internally to fetch workspace data.
1524
- *
1525
2308
  * @param requestId - Optional custom identifier for the request.
1526
2309
  * @returns A promise that resolves to an array of workspaces.
1527
2310
  *
@@ -1534,17 +2317,196 @@ export class TodoistApi {
1534
2317
  * ```
1535
2318
  */
1536
2319
  async getWorkspaces(requestId) {
1537
- const syncRequest = {
1538
- syncToken: '*',
1539
- resourceTypes: ['workspaces'],
2320
+ const response = await request({
2321
+ httpMethod: 'GET',
2322
+ baseUri: this.syncApiBase,
2323
+ relativePath: ENDPOINT_REST_WORKSPACES,
2324
+ apiToken: this.authToken,
2325
+ customFetch: this.customFetch,
2326
+ requestId: requestId,
2327
+ });
2328
+ return validateWorkspaceArray(response.data);
2329
+ }
2330
+ /**
2331
+ * Retrieves a workspace by its ID.
2332
+ *
2333
+ * @param id - The unique identifier of the workspace.
2334
+ * @param requestId - Optional custom identifier for the request.
2335
+ * @returns A promise that resolves to the requested workspace.
2336
+ */
2337
+ async getWorkspace(id, requestId) {
2338
+ z.string().parse(id);
2339
+ const response = await request({
2340
+ httpMethod: 'GET',
2341
+ baseUri: this.syncApiBase,
2342
+ relativePath: generatePath(ENDPOINT_REST_WORKSPACES, id),
2343
+ apiToken: this.authToken,
2344
+ customFetch: this.customFetch,
2345
+ requestId: requestId,
2346
+ });
2347
+ return validateWorkspace(response.data);
2348
+ }
2349
+ /**
2350
+ * Creates a new workspace.
2351
+ *
2352
+ * @param args - The arguments for creating the workspace.
2353
+ * @param requestId - Optional custom identifier for the request.
2354
+ * @returns A promise that resolves to the created workspace.
2355
+ */
2356
+ async addWorkspace(args, requestId) {
2357
+ const response = await request({
2358
+ httpMethod: 'POST',
2359
+ baseUri: this.syncApiBase,
2360
+ relativePath: ENDPOINT_REST_WORKSPACES,
2361
+ apiToken: this.authToken,
2362
+ customFetch: this.customFetch,
2363
+ payload: args,
2364
+ requestId: requestId,
2365
+ });
2366
+ return validateWorkspace(response.data);
2367
+ }
2368
+ /**
2369
+ * Updates an existing workspace.
2370
+ *
2371
+ * @param id - The unique identifier of the workspace to update.
2372
+ * @param args - The arguments for updating the workspace.
2373
+ * @param requestId - Optional custom identifier for the request.
2374
+ * @returns A promise that resolves to the updated workspace.
2375
+ */
2376
+ async updateWorkspace(id, args, requestId) {
2377
+ z.string().parse(id);
2378
+ const response = await request({
2379
+ httpMethod: 'POST',
2380
+ baseUri: this.syncApiBase,
2381
+ relativePath: generatePath(ENDPOINT_REST_WORKSPACES, id),
2382
+ apiToken: this.authToken,
2383
+ customFetch: this.customFetch,
2384
+ payload: args,
2385
+ requestId: requestId,
2386
+ });
2387
+ return validateWorkspace(response.data);
2388
+ }
2389
+ /**
2390
+ * Deletes a workspace by its ID.
2391
+ *
2392
+ * @param id - The unique identifier of the workspace to delete.
2393
+ * @param requestId - Optional custom identifier for the request.
2394
+ * @returns A promise that resolves to `true` if successful.
2395
+ */
2396
+ async deleteWorkspace(id, requestId) {
2397
+ z.string().parse(id);
2398
+ const response = await request({
2399
+ httpMethod: 'DELETE',
2400
+ baseUri: this.syncApiBase,
2401
+ relativePath: generatePath(ENDPOINT_REST_WORKSPACES, id),
2402
+ apiToken: this.authToken,
2403
+ customFetch: this.customFetch,
2404
+ requestId: requestId,
2405
+ });
2406
+ return isSuccess(response);
2407
+ }
2408
+ /**
2409
+ * Retrieves activity information for workspace members.
2410
+ *
2411
+ * @param args - Arguments including workspace ID and optional user/project filters.
2412
+ * @param requestId - Optional custom identifier for the request.
2413
+ * @returns A promise that resolves to workspace members activity data.
2414
+ */
2415
+ async getWorkspaceMembersActivity(args, requestId) {
2416
+ const { workspaceId } = args, queryParams = __rest(args, ["workspaceId"]);
2417
+ const { data } = await request({
2418
+ httpMethod: 'GET',
2419
+ baseUri: this.syncApiBase,
2420
+ relativePath: ENDPOINT_WORKSPACE_MEMBERS,
2421
+ apiToken: this.authToken,
2422
+ customFetch: this.customFetch,
2423
+ payload: Object.assign({ workspaceId }, queryParams),
2424
+ requestId: requestId,
2425
+ });
2426
+ return {
2427
+ members: validateMemberActivityInfoArray(data.members),
1540
2428
  };
1541
- const syncResponse = await this.requestSync(syncRequest, requestId, false);
1542
- const workspacesData = syncResponse.workspaces;
1543
- if (!workspacesData || typeof workspacesData !== 'object') {
1544
- return [];
1545
- }
1546
- const workspacesArray = Object.values(workspacesData);
1547
- return validateWorkspaceArray(workspacesArray);
2429
+ }
2430
+ /**
2431
+ * Retrieves tasks assigned to a specific user in a workspace.
2432
+ *
2433
+ * @param args - Arguments including workspace ID, user ID, and optional project filter.
2434
+ * @param requestId - Optional custom identifier for the request.
2435
+ * @returns A promise that resolves to workspace user tasks.
2436
+ */
2437
+ async getWorkspaceUserTasks(args, requestId) {
2438
+ const { workspaceId, userId } = args, queryParams = __rest(args, ["workspaceId", "userId"]);
2439
+ const { data } = await request({
2440
+ httpMethod: 'GET',
2441
+ baseUri: this.syncApiBase,
2442
+ relativePath: getWorkspaceUserTasksEndpoint(workspaceId, userId),
2443
+ apiToken: this.authToken,
2444
+ customFetch: this.customFetch,
2445
+ payload: queryParams,
2446
+ requestId: requestId,
2447
+ });
2448
+ return {
2449
+ tasks: validateWorkspaceUserTaskArray(data.tasks),
2450
+ };
2451
+ }
2452
+ /**
2453
+ * Invites users to a workspace by email.
2454
+ *
2455
+ * @param args - Arguments including workspace ID, email list, and optional role.
2456
+ * @param requestId - Optional custom identifier for the request.
2457
+ * @returns A promise that resolves to the list of invited emails.
2458
+ */
2459
+ async inviteWorkspaceUsers(args, requestId) {
2460
+ const { workspaceId } = args, payload = __rest(args, ["workspaceId"]);
2461
+ const { data } = await request({
2462
+ httpMethod: 'POST',
2463
+ baseUri: this.syncApiBase,
2464
+ relativePath: getWorkspaceInviteUsersEndpoint(workspaceId),
2465
+ apiToken: this.authToken,
2466
+ customFetch: this.customFetch,
2467
+ payload: payload,
2468
+ requestId: requestId,
2469
+ });
2470
+ return data;
2471
+ }
2472
+ /**
2473
+ * Updates a workspace user's role.
2474
+ *
2475
+ * @param args - Arguments including workspace ID, user ID, and new role.
2476
+ * @param requestId - Optional custom identifier for the request.
2477
+ * @returns A promise that resolves to the updated workspace user view.
2478
+ */
2479
+ async updateWorkspaceUser(args, requestId) {
2480
+ const { workspaceId, userId } = args, payload = __rest(args, ["workspaceId", "userId"]);
2481
+ const response = await request({
2482
+ httpMethod: 'POST',
2483
+ baseUri: this.syncApiBase,
2484
+ relativePath: getWorkspaceUserEndpoint(workspaceId, userId),
2485
+ apiToken: this.authToken,
2486
+ customFetch: this.customFetch,
2487
+ payload: payload,
2488
+ requestId: requestId,
2489
+ });
2490
+ return validateJoinWorkspaceResult(response.data);
2491
+ }
2492
+ /**
2493
+ * Removes a user from a workspace.
2494
+ *
2495
+ * @param args - Arguments including workspace ID and user ID.
2496
+ * @param requestId - Optional custom identifier for the request.
2497
+ * @returns A promise that resolves to `true` if successful.
2498
+ */
2499
+ async removeWorkspaceUser(args, requestId) {
2500
+ const { workspaceId, userId } = args;
2501
+ const response = await request({
2502
+ httpMethod: 'DELETE',
2503
+ baseUri: this.syncApiBase,
2504
+ relativePath: getWorkspaceUserEndpoint(workspaceId, userId),
2505
+ apiToken: this.authToken,
2506
+ customFetch: this.customFetch,
2507
+ requestId: requestId,
2508
+ });
2509
+ return isSuccess(response);
1548
2510
  }
1549
2511
  /**
1550
2512
  * Gets active projects in a workspace with pagination.