@panoptic-it-solutions/zoho-projects-client 0.2.4 → 0.2.6

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 (178) hide show
  1. package/README.md +75 -7
  2. package/dist/__tests__/fixtures/attachments.d.ts +59 -39
  3. package/dist/__tests__/fixtures/attachments.d.ts.map +1 -1
  4. package/dist/__tests__/fixtures/attachments.js +2 -4
  5. package/dist/__tests__/fixtures/attachments.js.map +1 -1
  6. package/dist/__tests__/fixtures/comments.d.ts +25 -13
  7. package/dist/__tests__/fixtures/comments.d.ts.map +1 -1
  8. package/dist/__tests__/fixtures/events.d.ts +27 -15
  9. package/dist/__tests__/fixtures/events.d.ts.map +1 -1
  10. package/dist/__tests__/fixtures/forums.d.ts +25 -13
  11. package/dist/__tests__/fixtures/forums.d.ts.map +1 -1
  12. package/dist/__tests__/fixtures/issues.d.ts +50 -26
  13. package/dist/__tests__/fixtures/issues.d.ts.map +1 -1
  14. package/dist/__tests__/fixtures/phases.d.ts +325 -41
  15. package/dist/__tests__/fixtures/phases.d.ts.map +1 -1
  16. package/dist/__tests__/fixtures/projects.d.ts +176 -8
  17. package/dist/__tests__/fixtures/projects.d.ts.map +1 -1
  18. package/dist/__tests__/fixtures/tasklists.d.ts +71 -25
  19. package/dist/__tests__/fixtures/tasklists.d.ts.map +1 -1
  20. package/dist/__tests__/fixtures/tasklists.js +1 -3
  21. package/dist/__tests__/fixtures/tasklists.js.map +1 -1
  22. package/dist/__tests__/fixtures/tasks.d.ts +257 -62
  23. package/dist/__tests__/fixtures/tasks.d.ts.map +1 -1
  24. package/dist/__tests__/fixtures/timelogs.d.ts +2 -2
  25. package/dist/__tests__/integration/api-v3.test.d.ts +2 -0
  26. package/dist/__tests__/integration/api-v3.test.d.ts.map +1 -0
  27. package/dist/__tests__/integration/api-v3.test.js +180 -0
  28. package/dist/__tests__/integration/api-v3.test.js.map +1 -0
  29. package/dist/__tests__/integration/setup.d.ts +0 -3
  30. package/dist/__tests__/integration/setup.d.ts.map +1 -1
  31. package/dist/__tests__/integration/setup.js +7 -1
  32. package/dist/__tests__/integration/setup.js.map +1 -1
  33. package/dist/__tests__/unit/client/attachments.test.js +31 -10
  34. package/dist/__tests__/unit/client/attachments.test.js.map +1 -1
  35. package/dist/__tests__/unit/client/blueprints.test.js +11 -11
  36. package/dist/__tests__/unit/client/blueprints.test.js.map +1 -1
  37. package/dist/__tests__/unit/client/clients.test.js +6 -6
  38. package/dist/__tests__/unit/client/clients.test.js.map +1 -1
  39. package/dist/__tests__/unit/client/comments.test.js +7 -7
  40. package/dist/__tests__/unit/client/comments.test.js.map +1 -1
  41. package/dist/__tests__/unit/client/contacts.test.js +6 -6
  42. package/dist/__tests__/unit/client/contacts.test.js.map +1 -1
  43. package/dist/__tests__/unit/client/customviews.test.js +8 -8
  44. package/dist/__tests__/unit/client/customviews.test.js.map +1 -1
  45. package/dist/__tests__/unit/client/dashboards.test.js +6 -6
  46. package/dist/__tests__/unit/client/dashboards.test.js.map +1 -1
  47. package/dist/__tests__/unit/client/events.test.js +6 -6
  48. package/dist/__tests__/unit/client/events.test.js.map +1 -1
  49. package/dist/__tests__/unit/client/followers.test.js +8 -8
  50. package/dist/__tests__/unit/client/followers.test.js.map +1 -1
  51. package/dist/__tests__/unit/client/forums.test.js +6 -6
  52. package/dist/__tests__/unit/client/forums.test.js.map +1 -1
  53. package/dist/__tests__/unit/client/groups.test.js +6 -6
  54. package/dist/__tests__/unit/client/groups.test.js.map +1 -1
  55. package/dist/__tests__/unit/client/issues.test.js +6 -6
  56. package/dist/__tests__/unit/client/issues.test.js.map +1 -1
  57. package/dist/__tests__/unit/client/leaves.test.js +6 -6
  58. package/dist/__tests__/unit/client/leaves.test.js.map +1 -1
  59. package/dist/__tests__/unit/client/modules.test.js +7 -7
  60. package/dist/__tests__/unit/client/modules.test.js.map +1 -1
  61. package/dist/__tests__/unit/client/phases.test.js +6 -6
  62. package/dist/__tests__/unit/client/phases.test.js.map +1 -1
  63. package/dist/__tests__/unit/client/portals.test.js +3 -3
  64. package/dist/__tests__/unit/client/portals.test.js.map +1 -1
  65. package/dist/__tests__/unit/client/profiles.test.js +6 -6
  66. package/dist/__tests__/unit/client/profiles.test.js.map +1 -1
  67. package/dist/__tests__/unit/client/projects.test.js +18 -18
  68. package/dist/__tests__/unit/client/projects.test.js.map +1 -1
  69. package/dist/__tests__/unit/client/reports.test.js +9 -9
  70. package/dist/__tests__/unit/client/reports.test.js.map +1 -1
  71. package/dist/__tests__/unit/client/roles.test.js +6 -6
  72. package/dist/__tests__/unit/client/roles.test.js.map +1 -1
  73. package/dist/__tests__/unit/client/search.test.js +7 -7
  74. package/dist/__tests__/unit/client/search.test.js.map +1 -1
  75. package/dist/__tests__/unit/client/tags.test.js +9 -9
  76. package/dist/__tests__/unit/client/tags.test.js.map +1 -1
  77. package/dist/__tests__/unit/client/tasklists.test.js +12 -12
  78. package/dist/__tests__/unit/client/tasklists.test.js.map +1 -1
  79. package/dist/__tests__/unit/client/tasks.test.js +17 -17
  80. package/dist/__tests__/unit/client/tasks.test.js.map +1 -1
  81. package/dist/__tests__/unit/client/teams.test.js +8 -8
  82. package/dist/__tests__/unit/client/teams.test.js.map +1 -1
  83. package/dist/__tests__/unit/client/timelogs.test.js +7 -7
  84. package/dist/__tests__/unit/client/timelogs.test.js.map +1 -1
  85. package/dist/__tests__/unit/client/timers.test.js +11 -11
  86. package/dist/__tests__/unit/client/timers.test.js.map +1 -1
  87. package/dist/__tests__/unit/client/trash.test.js +8 -8
  88. package/dist/__tests__/unit/client/trash.test.js.map +1 -1
  89. package/dist/__tests__/unit/client/users.test.js +8 -8
  90. package/dist/__tests__/unit/client/users.test.js.map +1 -1
  91. package/dist/__tests__/unit/client/widgets.test.js +6 -6
  92. package/dist/__tests__/unit/client/widgets.test.js.map +1 -1
  93. package/dist/client.d.ts +63 -11
  94. package/dist/client.d.ts.map +1 -1
  95. package/dist/client.js +470 -274
  96. package/dist/client.js.map +1 -1
  97. package/dist/types/attachments.d.ts +1525 -512
  98. package/dist/types/attachments.d.ts.map +1 -1
  99. package/dist/types/attachments.js +51 -10
  100. package/dist/types/attachments.js.map +1 -1
  101. package/dist/types/clients.js +2 -2
  102. package/dist/types/clients.js.map +1 -1
  103. package/dist/types/comments.d.ts +325 -169
  104. package/dist/types/comments.d.ts.map +1 -1
  105. package/dist/types/common.d.ts +33 -15
  106. package/dist/types/common.d.ts.map +1 -1
  107. package/dist/types/common.js +12 -7
  108. package/dist/types/common.js.map +1 -1
  109. package/dist/types/contacts.d.ts +6 -6
  110. package/dist/types/dashboards.d.ts +979 -511
  111. package/dist/types/dashboards.d.ts.map +1 -1
  112. package/dist/types/dashboards.js +1 -1
  113. package/dist/types/dashboards.js.map +1 -1
  114. package/dist/types/documents.d.ts +1300 -676
  115. package/dist/types/documents.d.ts.map +1 -1
  116. package/dist/types/events.d.ts +351 -195
  117. package/dist/types/events.d.ts.map +1 -1
  118. package/dist/types/forums.d.ts +325 -169
  119. package/dist/types/forums.d.ts.map +1 -1
  120. package/dist/types/forums.js +2 -2
  121. package/dist/types/forums.js.map +1 -1
  122. package/dist/types/groups.js +1 -1
  123. package/dist/types/groups.js.map +1 -1
  124. package/dist/types/index.d.ts +2 -1
  125. package/dist/types/index.d.ts.map +1 -1
  126. package/dist/types/index.js +3 -1
  127. package/dist/types/index.js.map +1 -1
  128. package/dist/types/issues.d.ts +650 -338
  129. package/dist/types/issues.d.ts.map +1 -1
  130. package/dist/types/issues.js +3 -3
  131. package/dist/types/issues.js.map +1 -1
  132. package/dist/types/phases.d.ts +4358 -561
  133. package/dist/types/phases.d.ts.map +1 -1
  134. package/dist/types/phases.js +103 -25
  135. package/dist/types/phases.js.map +1 -1
  136. package/dist/types/portals.js +5 -5
  137. package/dist/types/portals.js.map +1 -1
  138. package/dist/types/profiles.js +1 -1
  139. package/dist/types/profiles.js.map +1 -1
  140. package/dist/types/projects.d.ts +6324 -1368
  141. package/dist/types/projects.d.ts.map +1 -1
  142. package/dist/types/projects.js +123 -39
  143. package/dist/types/projects.js.map +1 -1
  144. package/dist/types/reports.d.ts +650 -338
  145. package/dist/types/reports.d.ts.map +1 -1
  146. package/dist/types/reports.js +1 -1
  147. package/dist/types/reports.js.map +1 -1
  148. package/dist/types/search.js +1 -1
  149. package/dist/types/search.js.map +1 -1
  150. package/dist/types/tags.js +1 -1
  151. package/dist/types/tags.js.map +1 -1
  152. package/dist/types/tasklists.d.ts +938 -326
  153. package/dist/types/tasklists.d.ts.map +1 -1
  154. package/dist/types/tasklists.js +43 -16
  155. package/dist/types/tasklists.js.map +1 -1
  156. package/dist/types/tasks.d.ts +3516 -876
  157. package/dist/types/tasks.d.ts.map +1 -1
  158. package/dist/types/tasks.js +70 -23
  159. package/dist/types/tasks.js.map +1 -1
  160. package/dist/types/teams.js +2 -2
  161. package/dist/types/teams.js.map +1 -1
  162. package/dist/types/timelogs.d.ts +232 -230
  163. package/dist/types/timelogs.d.ts.map +1 -1
  164. package/dist/types/timelogs.js.map +1 -1
  165. package/dist/types/timers.d.ts +750 -390
  166. package/dist/types/timers.d.ts.map +1 -1
  167. package/dist/types/trash.d.ts +650 -338
  168. package/dist/types/trash.d.ts.map +1 -1
  169. package/dist/types/workdrive.d.ts +1002 -0
  170. package/dist/types/workdrive.d.ts.map +1 -0
  171. package/dist/types/workdrive.js +70 -0
  172. package/dist/types/workdrive.js.map +1 -0
  173. package/dist/utils/pagination.d.ts +9 -9
  174. package/dist/utils/pagination.d.ts.map +1 -1
  175. package/dist/utils/pagination.js +11 -11
  176. package/dist/utils/pagination.js.map +1 -1
  177. package/package.json +3 -1
  178. package/templates/CLAUDE.md +185 -2
package/dist/client.js CHANGED
@@ -96,8 +96,8 @@ export function createZohoProjectsClient(config) {
96
96
  }
97
97
  return result.data;
98
98
  }
99
- // Base path for portal-specific endpoints
100
- const basePath = `/restapi/portal/${portalId}`;
99
+ // Base path for portal-specific endpoints (V3 API)
100
+ const basePath = `/api/v3/portal/${portalId}`;
101
101
  return {
102
102
  /**
103
103
  * Projects API
@@ -107,17 +107,20 @@ export function createZohoProjectsClient(config) {
107
107
  * List projects with pagination
108
108
  */
109
109
  async list(params) {
110
- const response = await requestWithValidation(`${basePath}/projects/`, ProjectListResponseSchema, {
110
+ const response = await requestWithValidation(`${basePath}/projects`, ProjectListResponseSchema, {
111
111
  params: {
112
- index: params?.index ?? 0,
113
- range: params?.range ?? DEFAULT_PAGE_SIZE,
112
+ page: params?.page ?? params?.index ?? 1,
113
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
114
114
  sort_column: params?.sort_column,
115
115
  sort_order: params?.sort_order,
116
116
  },
117
117
  });
118
+ // V3 API returns array directly, legacy API wraps in { projects: [...] }
119
+ const projects = Array.isArray(response) ? response : response.projects;
120
+ const pageInfo = Array.isArray(response) ? undefined : response.page_info;
118
121
  return {
119
- data: response.projects,
120
- pageInfo: response.page_info,
122
+ data: projects,
123
+ pageInfo,
121
124
  };
122
125
  },
123
126
  /**
@@ -130,37 +133,65 @@ export function createZohoProjectsClient(config) {
130
133
  * Iterate over all projects with auto-pagination
131
134
  */
132
135
  iterate(options) {
133
- return autoPaginate((index, range) => this.list({ index, range }), options);
136
+ return autoPaginate((page, per_page) => this.list({ page, per_page }), options);
134
137
  },
135
138
  /**
136
139
  * Get a single project by ID
137
140
  */
138
141
  async get(projectId) {
139
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/`, z.object({ projects: z.array(ProjectSchema) }));
140
- if (response.projects.length === 0) {
141
- throw new ZohoProjectsError(`Project not found: ${projectId}`, 404);
142
+ // V3 API returns project directly, legacy wraps in {projects: [...]}
143
+ const schema = z.union([
144
+ ProjectSchema, // V3: returns project directly
145
+ z.object({ projects: z.array(ProjectSchema) }), // Legacy
146
+ ]);
147
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}`, schema);
148
+ // Handle both formats
149
+ if (response && typeof response === "object" && "projects" in response) {
150
+ const legacyResponse = response;
151
+ if (legacyResponse.projects.length === 0) {
152
+ throw new ZohoProjectsError(`Project not found: ${projectId}`, 404);
153
+ }
154
+ return legacyResponse.projects[0];
142
155
  }
143
- return response.projects[0];
156
+ return response;
144
157
  },
145
158
  /**
146
159
  * Create a new project
147
160
  */
148
161
  async create(data) {
149
- const response = await requestWithValidation(`${basePath}/projects/`, z.object({ projects: z.array(ProjectSchema) }), { method: "POST", data });
150
- return response.projects[0];
162
+ // V3 API returns project directly, legacy wraps in {projects: [...]}
163
+ const schema = z.union([
164
+ ProjectSchema, // V3: returns project directly
165
+ z.object({ projects: z.array(ProjectSchema) }), // Legacy
166
+ ]);
167
+ const response = await requestWithValidation(`${basePath}/projects`, schema, { method: "POST", data });
168
+ // Handle both formats
169
+ if (response && typeof response === "object" && "projects" in response) {
170
+ return response.projects[0];
171
+ }
172
+ return response;
151
173
  },
152
174
  /**
153
175
  * Update an existing project
154
176
  */
155
177
  async update(projectId, data) {
156
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/`, z.object({ projects: z.array(ProjectSchema) }), { method: "PUT", data });
157
- return response.projects[0];
178
+ // V3 API returns project directly, legacy wraps in {projects: [...]}
179
+ const schema = z.union([
180
+ ProjectSchema, // V3: returns project directly
181
+ z.object({ projects: z.array(ProjectSchema) }), // Legacy
182
+ ]);
183
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}`, schema, { method: "PUT", data });
184
+ // Handle both formats
185
+ if (response && typeof response === "object" && "projects" in response) {
186
+ return response.projects[0];
187
+ }
188
+ return response;
158
189
  },
159
190
  /**
160
191
  * Delete a project (moves to trash)
161
192
  */
162
193
  async delete(projectId) {
163
- await request(`${basePath}/projects/${projectId}/`, {
194
+ await request(`${basePath}/projects/${projectId}`, {
164
195
  method: "DELETE",
165
196
  });
166
197
  },
@@ -173,10 +204,10 @@ export function createZohoProjectsClient(config) {
173
204
  * List tasks for a project with pagination
174
205
  */
175
206
  async list(projectId, params) {
176
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/tasks/`, TaskListResponseSchema, {
207
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/tasks`, TaskListResponseSchema, {
177
208
  params: {
178
- index: params?.index ?? 0,
179
- range: params?.range ?? DEFAULT_PAGE_SIZE,
209
+ page: params?.page ?? params?.index ?? 1,
210
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
180
211
  sort_column: params?.sort_column,
181
212
  sort_order: params?.sort_order,
182
213
  },
@@ -196,17 +227,27 @@ export function createZohoProjectsClient(config) {
196
227
  * Iterate over all tasks for a project with auto-pagination
197
228
  */
198
229
  iterate(projectId, options) {
199
- return autoPaginate((index, range) => this.list(projectId, { index, range }), options);
230
+ return autoPaginate((page, per_page) => this.list(projectId, { page, per_page }), options);
200
231
  },
201
232
  /**
202
233
  * Get a single task by ID
203
234
  */
204
235
  async get(projectId, taskId) {
205
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/tasks/${taskId}/`, z.object({ tasks: z.array(TaskSchema) }));
206
- if (response.tasks.length === 0) {
207
- throw new ZohoProjectsError(`Task not found: ${taskId}`, 404);
236
+ // V3 API returns task directly, legacy wraps in {tasks: [...]}
237
+ const schema = z.union([
238
+ TaskSchema, // V3: returns task directly
239
+ z.object({ tasks: z.array(TaskSchema) }), // Legacy
240
+ ]);
241
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/tasks/${taskId}`, schema);
242
+ // Handle both formats
243
+ if (response && typeof response === "object" && "tasks" in response) {
244
+ const legacyResponse = response;
245
+ if (legacyResponse.tasks.length === 0) {
246
+ throw new ZohoProjectsError(`Task not found: ${taskId}`, 404);
247
+ }
248
+ return legacyResponse.tasks[0];
208
249
  }
209
- return response.tasks[0];
250
+ return response;
210
251
  },
211
252
  /**
212
253
  * List all tasks across all projects
@@ -216,30 +257,51 @@ export function createZohoProjectsClient(config) {
216
257
  const projects = await this._getProjectsRef().listAll();
217
258
  const allTasks = [];
218
259
  for (const project of projects) {
219
- const tasks = await this.listAll(project.id_string, options);
260
+ // V3 API uses 'id' as string directly, legacy uses 'id_string'
261
+ const tasks = await this.listAll(String(project.id), options);
220
262
  allTasks.push(...tasks);
221
263
  }
222
264
  return allTasks;
223
265
  },
224
266
  /**
225
267
  * Create a new task in a project
268
+ * Note: V3 API requires a tasklist to be specified
226
269
  */
227
270
  async create(projectId, data) {
228
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/tasks/`, z.object({ tasks: z.array(TaskSchema) }), { method: "POST", data });
229
- return response.tasks[0];
271
+ // V3 API returns task directly, legacy wraps in {tasks: [...]}
272
+ const schema = z.union([
273
+ TaskSchema, // V3: returns task directly
274
+ z.object({ tasks: z.array(TaskSchema) }), // Legacy
275
+ ]);
276
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/tasks`, schema, { method: "POST", data });
277
+ // Handle both formats
278
+ if (response && typeof response === "object" && "tasks" in response) {
279
+ return response.tasks[0];
280
+ }
281
+ return response;
230
282
  },
231
283
  /**
232
284
  * Update an existing task
285
+ * Note: V3 API uses PATCH method for updates
233
286
  */
234
287
  async update(projectId, taskId, data) {
235
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/tasks/${taskId}/`, z.object({ tasks: z.array(TaskSchema) }), { method: "PUT", data });
236
- return response.tasks[0];
288
+ // V3 API returns task directly, legacy wraps in {tasks: [...]}
289
+ const schema = z.union([
290
+ TaskSchema, // V3: returns task directly
291
+ z.object({ tasks: z.array(TaskSchema) }), // Legacy
292
+ ]);
293
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/tasks/${taskId}`, schema, { method: "PATCH", data });
294
+ // Handle both formats
295
+ if (response && typeof response === "object" && "tasks" in response) {
296
+ return response.tasks[0];
297
+ }
298
+ return response;
237
299
  },
238
300
  /**
239
301
  * Delete a task (moves to trash)
240
302
  */
241
303
  async delete(projectId, taskId) {
242
- await request(`${basePath}/projects/${projectId}/tasks/${taskId}/`, {
304
+ await request(`${basePath}/projects/${projectId}/tasks/${taskId}`, {
243
305
  method: "DELETE",
244
306
  });
245
307
  },
@@ -255,10 +317,10 @@ export function createZohoProjectsClient(config) {
255
317
  */
256
318
  async list(projectId, params) {
257
319
  // Make the request - may return 204 No Content if no logs
258
- const rawResponse = await request(`${basePath}/projects/${projectId}/logs/`, {
320
+ const rawResponse = await request(`${basePath}/projects/${projectId}/logs`, {
259
321
  params: {
260
- index: params.index ?? 0,
261
- range: params.range ?? DEFAULT_PAGE_SIZE,
322
+ page: params.page ?? 1,
323
+ per_page: params.per_page ?? DEFAULT_PAGE_SIZE,
262
324
  users_list: params.users_list,
263
325
  view_type: params.view_type,
264
326
  date: params.date,
@@ -310,7 +372,7 @@ export function createZohoProjectsClient(config) {
310
372
  * Iterate over all time logs for a project with auto-pagination
311
373
  */
312
374
  iterate(projectId, params, options) {
313
- return autoPaginate((index, range) => this.list(projectId, { ...params, index, range }), options);
375
+ return autoPaginate((page, per_page) => this.list(projectId, { ...params, page, per_page }), options);
314
376
  },
315
377
  /**
316
378
  * List all time logs across all projects
@@ -319,7 +381,8 @@ export function createZohoProjectsClient(config) {
319
381
  const projects = await this._getProjectsRef().listAll();
320
382
  const allLogs = [];
321
383
  for (const project of projects) {
322
- const logs = await this.listAll(project.id_string, params, options);
384
+ // V3 API uses 'id' as string directly, legacy uses 'id_string'
385
+ const logs = await this.listAll(String(project.id), params, options);
323
386
  allLogs.push(...logs);
324
387
  }
325
388
  return allLogs;
@@ -328,28 +391,28 @@ export function createZohoProjectsClient(config) {
328
391
  * Create a time log for a task
329
392
  */
330
393
  async createForTask(projectId, data) {
331
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/logs/`, z.object({ timelogs: z.object({ tasklogs: z.array(TimeLogSchema) }) }), { method: "POST", data });
394
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/logs`, z.object({ timelogs: z.object({ tasklogs: z.array(TimeLogSchema) }) }), { method: "POST", data });
332
395
  return response.timelogs.tasklogs[0];
333
396
  },
334
397
  /**
335
398
  * Create a time log for a bug
336
399
  */
337
400
  async createForBug(projectId, data) {
338
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/logs/`, z.object({ timelogs: z.object({ buglogs: z.array(TimeLogSchema) }) }), { method: "POST", data });
401
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/logs`, z.object({ timelogs: z.object({ buglogs: z.array(TimeLogSchema) }) }), { method: "POST", data });
339
402
  return response.timelogs.buglogs[0];
340
403
  },
341
404
  /**
342
405
  * Create a general time log (not tied to task or bug)
343
406
  */
344
407
  async createGeneral(projectId, data) {
345
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/logs/`, z.object({ timelogs: z.object({ generallogs: z.array(TimeLogSchema) }) }), { method: "POST", data });
408
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/logs`, z.object({ timelogs: z.object({ generallogs: z.array(TimeLogSchema) }) }), { method: "POST", data });
346
409
  return response.timelogs.generallogs[0];
347
410
  },
348
411
  /**
349
412
  * Update a time log
350
413
  */
351
414
  async update(projectId, logId, data) {
352
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/logs/${logId}/`, z.object({ timelogs: z.object({ tasklogs: z.array(TimeLogSchema).optional(), buglogs: z.array(TimeLogSchema).optional(), generallogs: z.array(TimeLogSchema).optional() }) }), { method: "PUT", data });
415
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/logs/${logId}`, z.object({ timelogs: z.object({ tasklogs: z.array(TimeLogSchema).optional(), buglogs: z.array(TimeLogSchema).optional(), generallogs: z.array(TimeLogSchema).optional() }) }), { method: "PUT", data });
353
416
  // Return whichever type was updated
354
417
  const logs = response.timelogs.tasklogs || response.timelogs.buglogs || response.timelogs.generallogs || [];
355
418
  return logs[0];
@@ -358,7 +421,7 @@ export function createZohoProjectsClient(config) {
358
421
  * Delete a time log
359
422
  */
360
423
  async delete(projectId, logId) {
361
- await request(`${basePath}/projects/${projectId}/logs/${logId}/`, {
424
+ await request(`${basePath}/projects/${projectId}/logs/${logId}`, {
362
425
  method: "DELETE",
363
426
  });
364
427
  },
@@ -373,10 +436,10 @@ export function createZohoProjectsClient(config) {
373
436
  * List all users in the portal
374
437
  */
375
438
  async list(params) {
376
- const response = await requestWithValidation(`${basePath}/users/`, UserListResponseSchema, {
439
+ const response = await requestWithValidation(`${basePath}/users`, UserListResponseSchema, {
377
440
  params: {
378
- index: params?.index ?? 0,
379
- range: params?.range ?? DEFAULT_PAGE_SIZE,
441
+ page: params?.page ?? params?.index ?? 1,
442
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
380
443
  },
381
444
  });
382
445
  return {
@@ -394,13 +457,13 @@ export function createZohoProjectsClient(config) {
394
457
  * Iterate over all users with auto-pagination
395
458
  */
396
459
  iterate(options) {
397
- return autoPaginate((index, range) => this.list({ index, range }), options);
460
+ return autoPaginate((page, per_page) => this.list({ page, per_page }), options);
398
461
  },
399
462
  /**
400
463
  * Get a single user by ID
401
464
  */
402
465
  async get(userId) {
403
- const response = await requestWithValidation(`${basePath}/users/${userId}/`, z.object({ users: z.array(UserSchema) }));
466
+ const response = await requestWithValidation(`${basePath}/users/${userId}`, z.object({ users: z.array(UserSchema) }));
404
467
  if (response.users.length === 0) {
405
468
  throw new ZohoProjectsError(`User not found: ${userId}`, 404);
406
469
  }
@@ -410,10 +473,10 @@ export function createZohoProjectsClient(config) {
410
473
  * List users for a specific project
411
474
  */
412
475
  async listForProject(projectId, params) {
413
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/users/`, UserListResponseSchema, {
476
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/users`, UserListResponseSchema, {
414
477
  params: {
415
- index: params?.index ?? 0,
416
- range: params?.range ?? DEFAULT_PAGE_SIZE,
478
+ page: params?.page ?? params?.index ?? 1,
479
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
417
480
  },
418
481
  });
419
482
  return {
@@ -425,28 +488,28 @@ export function createZohoProjectsClient(config) {
425
488
  * Invite a new user to the portal
426
489
  */
427
490
  async invite(data) {
428
- const response = await requestWithValidation(`${basePath}/users/`, z.object({ users: z.array(UserSchema) }), { method: "POST", data });
491
+ const response = await requestWithValidation(`${basePath}/users`, z.object({ users: z.array(UserSchema) }), { method: "POST", data });
429
492
  return response.users[0];
430
493
  },
431
494
  /**
432
495
  * Add a user to a project
433
496
  */
434
497
  async addToProject(projectId, data) {
435
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/users/`, z.object({ users: z.array(UserSchema) }), { method: "POST", data });
498
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/users`, z.object({ users: z.array(UserSchema) }), { method: "POST", data });
436
499
  return response.users[0];
437
500
  },
438
501
  /**
439
502
  * Update a user's details
440
503
  */
441
504
  async update(userId, data) {
442
- const response = await requestWithValidation(`${basePath}/users/${userId}/`, z.object({ users: z.array(UserSchema) }), { method: "PUT", data });
505
+ const response = await requestWithValidation(`${basePath}/users/${userId}`, z.object({ users: z.array(UserSchema) }), { method: "PUT", data });
443
506
  return response.users[0];
444
507
  },
445
508
  /**
446
509
  * Remove a user from the portal
447
510
  */
448
511
  async delete(userId) {
449
- await request(`${basePath}/users/${userId}/`, {
512
+ await request(`${basePath}/users/${userId}`, {
450
513
  method: "DELETE",
451
514
  });
452
515
  },
@@ -454,7 +517,7 @@ export function createZohoProjectsClient(config) {
454
517
  * Remove a user from a project
455
518
  */
456
519
  async removeFromProject(projectId, userId) {
457
- await request(`${basePath}/projects/${projectId}/users/${userId}/`, {
520
+ await request(`${basePath}/projects/${projectId}/users/${userId}`, {
458
521
  method: "DELETE",
459
522
  });
460
523
  },
@@ -464,10 +527,10 @@ export function createZohoProjectsClient(config) {
464
527
  */
465
528
  tags: {
466
529
  async list(params) {
467
- const response = await requestWithValidation(`${basePath}/tags/`, TagListResponseSchema, {
530
+ const response = await requestWithValidation(`${basePath}/tags`, TagListResponseSchema, {
468
531
  params: {
469
- index: params?.index ?? 0,
470
- range: params?.range ?? DEFAULT_PAGE_SIZE,
532
+ page: params?.page ?? params?.index ?? 1,
533
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
471
534
  },
472
535
  });
473
536
  return { data: response.tags, pageInfo: response.page_info };
@@ -476,25 +539,25 @@ export function createZohoProjectsClient(config) {
476
539
  return collectAll(this.iterate(options));
477
540
  },
478
541
  iterate(options) {
479
- return autoPaginate((index, range) => this.list({ index, range }), options);
542
+ return autoPaginate((page, per_page) => this.list({ page, per_page }), options);
480
543
  },
481
544
  async get(tagId) {
482
- const response = await requestWithValidation(`${basePath}/tags/${tagId}/`, z.object({ tags: z.array(TagSchema) }));
545
+ const response = await requestWithValidation(`${basePath}/tags/${tagId}`, z.object({ tags: z.array(TagSchema) }));
483
546
  if (response.tags.length === 0) {
484
547
  throw new ZohoProjectsError(`Tag not found: ${tagId}`, 404);
485
548
  }
486
549
  return response.tags[0];
487
550
  },
488
551
  async create(data) {
489
- const response = await requestWithValidation(`${basePath}/tags/`, z.object({ tags: z.array(TagSchema) }), { method: "POST", data });
552
+ const response = await requestWithValidation(`${basePath}/tags`, z.object({ tags: z.array(TagSchema) }), { method: "POST", data });
490
553
  return response.tags[0];
491
554
  },
492
555
  async update(tagId, data) {
493
- const response = await requestWithValidation(`${basePath}/tags/${tagId}/`, z.object({ tags: z.array(TagSchema) }), { method: "PUT", data });
556
+ const response = await requestWithValidation(`${basePath}/tags/${tagId}`, z.object({ tags: z.array(TagSchema) }), { method: "PUT", data });
494
557
  return response.tags[0];
495
558
  },
496
559
  async delete(tagId) {
497
- await request(`${basePath}/tags/${tagId}/`, { method: "DELETE" });
560
+ await request(`${basePath}/tags/${tagId}`, { method: "DELETE" });
498
561
  },
499
562
  },
500
563
  /**
@@ -502,10 +565,10 @@ export function createZohoProjectsClient(config) {
502
565
  */
503
566
  roles: {
504
567
  async list(params) {
505
- const response = await requestWithValidation(`${basePath}/roles/`, RoleListResponseSchema, {
568
+ const response = await requestWithValidation(`${basePath}/roles`, RoleListResponseSchema, {
506
569
  params: {
507
- index: params?.index ?? 0,
508
- range: params?.range ?? DEFAULT_PAGE_SIZE,
570
+ page: params?.page ?? params?.index ?? 1,
571
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
509
572
  },
510
573
  });
511
574
  return { data: response.roles, pageInfo: response.page_info };
@@ -514,25 +577,25 @@ export function createZohoProjectsClient(config) {
514
577
  return collectAll(this.iterate(options));
515
578
  },
516
579
  iterate(options) {
517
- return autoPaginate((index, range) => this.list({ index, range }), options);
580
+ return autoPaginate((page, per_page) => this.list({ page, per_page }), options);
518
581
  },
519
582
  async get(roleId) {
520
- const response = await requestWithValidation(`${basePath}/roles/${roleId}/`, z.object({ roles: z.array(RoleSchema) }));
583
+ const response = await requestWithValidation(`${basePath}/roles/${roleId}`, z.object({ roles: z.array(RoleSchema) }));
521
584
  if (response.roles.length === 0) {
522
585
  throw new ZohoProjectsError(`Role not found: ${roleId}`, 404);
523
586
  }
524
587
  return response.roles[0];
525
588
  },
526
589
  async create(data) {
527
- const response = await requestWithValidation(`${basePath}/roles/`, z.object({ roles: z.array(RoleSchema) }), { method: "POST", data });
590
+ const response = await requestWithValidation(`${basePath}/roles`, z.object({ roles: z.array(RoleSchema) }), { method: "POST", data });
528
591
  return response.roles[0];
529
592
  },
530
593
  async update(roleId, data) {
531
- const response = await requestWithValidation(`${basePath}/roles/${roleId}/`, z.object({ roles: z.array(RoleSchema) }), { method: "PUT", data });
594
+ const response = await requestWithValidation(`${basePath}/roles/${roleId}`, z.object({ roles: z.array(RoleSchema) }), { method: "PUT", data });
532
595
  return response.roles[0];
533
596
  },
534
597
  async delete(roleId) {
535
- await request(`${basePath}/roles/${roleId}/`, { method: "DELETE" });
598
+ await request(`${basePath}/roles/${roleId}`, { method: "DELETE" });
536
599
  },
537
600
  },
538
601
  /**
@@ -540,10 +603,10 @@ export function createZohoProjectsClient(config) {
540
603
  */
541
604
  profiles: {
542
605
  async list(params) {
543
- const response = await requestWithValidation(`${basePath}/profiles/`, ProfileListResponseSchema, {
606
+ const response = await requestWithValidation(`${basePath}/profiles`, ProfileListResponseSchema, {
544
607
  params: {
545
- index: params?.index ?? 0,
546
- range: params?.range ?? DEFAULT_PAGE_SIZE,
608
+ page: params?.page ?? params?.index ?? 1,
609
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
547
610
  },
548
611
  });
549
612
  return { data: response.profiles, pageInfo: response.page_info };
@@ -552,25 +615,25 @@ export function createZohoProjectsClient(config) {
552
615
  return collectAll(this.iterate(options));
553
616
  },
554
617
  iterate(options) {
555
- return autoPaginate((index, range) => this.list({ index, range }), options);
618
+ return autoPaginate((page, per_page) => this.list({ page, per_page }), options);
556
619
  },
557
620
  async get(profileId) {
558
- const response = await requestWithValidation(`${basePath}/profiles/${profileId}/`, z.object({ profiles: z.array(ProfileSchema) }));
621
+ const response = await requestWithValidation(`${basePath}/profiles/${profileId}`, z.object({ profiles: z.array(ProfileSchema) }));
559
622
  if (response.profiles.length === 0) {
560
623
  throw new ZohoProjectsError(`Profile not found: ${profileId}`, 404);
561
624
  }
562
625
  return response.profiles[0];
563
626
  },
564
627
  async create(data) {
565
- const response = await requestWithValidation(`${basePath}/profiles/`, z.object({ profiles: z.array(ProfileSchema) }), { method: "POST", data });
628
+ const response = await requestWithValidation(`${basePath}/profiles`, z.object({ profiles: z.array(ProfileSchema) }), { method: "POST", data });
566
629
  return response.profiles[0];
567
630
  },
568
631
  async update(profileId, data) {
569
- const response = await requestWithValidation(`${basePath}/profiles/${profileId}/`, z.object({ profiles: z.array(ProfileSchema) }), { method: "PUT", data });
632
+ const response = await requestWithValidation(`${basePath}/profiles/${profileId}`, z.object({ profiles: z.array(ProfileSchema) }), { method: "PUT", data });
570
633
  return response.profiles[0];
571
634
  },
572
635
  async delete(profileId) {
573
- await request(`${basePath}/profiles/${profileId}/`, { method: "DELETE" });
636
+ await request(`${basePath}/profiles/${profileId}`, { method: "DELETE" });
574
637
  },
575
638
  },
576
639
  /**
@@ -578,10 +641,10 @@ export function createZohoProjectsClient(config) {
578
641
  */
579
642
  clients: {
580
643
  async list(params) {
581
- const response = await requestWithValidation(`${basePath}/clients/`, ClientListResponseSchema, {
644
+ const response = await requestWithValidation(`${basePath}/clients`, ClientListResponseSchema, {
582
645
  params: {
583
- index: params?.index ?? 0,
584
- range: params?.range ?? DEFAULT_PAGE_SIZE,
646
+ page: params?.page ?? params?.index ?? 1,
647
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
585
648
  },
586
649
  });
587
650
  return { data: response.clients, pageInfo: response.page_info };
@@ -590,25 +653,25 @@ export function createZohoProjectsClient(config) {
590
653
  return collectAll(this.iterate(options));
591
654
  },
592
655
  iterate(options) {
593
- return autoPaginate((index, range) => this.list({ index, range }), options);
656
+ return autoPaginate((page, per_page) => this.list({ page, per_page }), options);
594
657
  },
595
658
  async get(clientId) {
596
- const response = await requestWithValidation(`${basePath}/clients/${clientId}/`, z.object({ clients: z.array(ClientSchema) }));
659
+ const response = await requestWithValidation(`${basePath}/clients/${clientId}`, z.object({ clients: z.array(ClientSchema) }));
597
660
  if (response.clients.length === 0) {
598
661
  throw new ZohoProjectsError(`Client not found: ${clientId}`, 404);
599
662
  }
600
663
  return response.clients[0];
601
664
  },
602
665
  async create(data) {
603
- const response = await requestWithValidation(`${basePath}/clients/`, z.object({ clients: z.array(ClientSchema) }), { method: "POST", data });
666
+ const response = await requestWithValidation(`${basePath}/clients`, z.object({ clients: z.array(ClientSchema) }), { method: "POST", data });
604
667
  return response.clients[0];
605
668
  },
606
669
  async update(clientId, data) {
607
- const response = await requestWithValidation(`${basePath}/clients/${clientId}/`, z.object({ clients: z.array(ClientSchema) }), { method: "PUT", data });
670
+ const response = await requestWithValidation(`${basePath}/clients/${clientId}`, z.object({ clients: z.array(ClientSchema) }), { method: "PUT", data });
608
671
  return response.clients[0];
609
672
  },
610
673
  async delete(clientId) {
611
- await request(`${basePath}/clients/${clientId}/`, { method: "DELETE" });
674
+ await request(`${basePath}/clients/${clientId}`, { method: "DELETE" });
612
675
  },
613
676
  },
614
677
  /**
@@ -616,10 +679,10 @@ export function createZohoProjectsClient(config) {
616
679
  */
617
680
  contacts: {
618
681
  async list(params) {
619
- const response = await requestWithValidation(`${basePath}/contacts/`, ContactListResponseSchema, {
682
+ const response = await requestWithValidation(`${basePath}/contacts`, ContactListResponseSchema, {
620
683
  params: {
621
- index: params?.index ?? 0,
622
- range: params?.range ?? DEFAULT_PAGE_SIZE,
684
+ page: params?.page ?? params?.index ?? 1,
685
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
623
686
  },
624
687
  });
625
688
  return { data: response.contacts, pageInfo: response.page_info };
@@ -628,25 +691,25 @@ export function createZohoProjectsClient(config) {
628
691
  return collectAll(this.iterate(options));
629
692
  },
630
693
  iterate(options) {
631
- return autoPaginate((index, range) => this.list({ index, range }), options);
694
+ return autoPaginate((page, per_page) => this.list({ page, per_page }), options);
632
695
  },
633
696
  async get(contactId) {
634
- const response = await requestWithValidation(`${basePath}/contacts/${contactId}/`, z.object({ contacts: z.array(ContactSchema) }));
697
+ const response = await requestWithValidation(`${basePath}/contacts/${contactId}`, z.object({ contacts: z.array(ContactSchema) }));
635
698
  if (response.contacts.length === 0) {
636
699
  throw new ZohoProjectsError(`Contact not found: ${contactId}`, 404);
637
700
  }
638
701
  return response.contacts[0];
639
702
  },
640
703
  async create(data) {
641
- const response = await requestWithValidation(`${basePath}/contacts/`, z.object({ contacts: z.array(ContactSchema) }), { method: "POST", data });
704
+ const response = await requestWithValidation(`${basePath}/contacts`, z.object({ contacts: z.array(ContactSchema) }), { method: "POST", data });
642
705
  return response.contacts[0];
643
706
  },
644
707
  async update(contactId, data) {
645
- const response = await requestWithValidation(`${basePath}/contacts/${contactId}/`, z.object({ contacts: z.array(ContactSchema) }), { method: "PUT", data });
708
+ const response = await requestWithValidation(`${basePath}/contacts/${contactId}`, z.object({ contacts: z.array(ContactSchema) }), { method: "PUT", data });
646
709
  return response.contacts[0];
647
710
  },
648
711
  async delete(contactId) {
649
- await request(`${basePath}/contacts/${contactId}/`, { method: "DELETE" });
712
+ await request(`${basePath}/contacts/${contactId}`, { method: "DELETE" });
650
713
  },
651
714
  },
652
715
  /**
@@ -654,10 +717,10 @@ export function createZohoProjectsClient(config) {
654
717
  */
655
718
  groups: {
656
719
  async list(params) {
657
- const response = await requestWithValidation(`${basePath}/groups/`, ProjectGroupListResponseSchema, {
720
+ const response = await requestWithValidation(`${basePath}/groups`, ProjectGroupListResponseSchema, {
658
721
  params: {
659
- index: params?.index ?? 0,
660
- range: params?.range ?? DEFAULT_PAGE_SIZE,
722
+ page: params?.page ?? params?.index ?? 1,
723
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
661
724
  },
662
725
  });
663
726
  return { data: response.groups, pageInfo: response.page_info };
@@ -666,25 +729,25 @@ export function createZohoProjectsClient(config) {
666
729
  return collectAll(this.iterate(options));
667
730
  },
668
731
  iterate(options) {
669
- return autoPaginate((index, range) => this.list({ index, range }), options);
732
+ return autoPaginate((page, per_page) => this.list({ page, per_page }), options);
670
733
  },
671
734
  async get(groupId) {
672
- const response = await requestWithValidation(`${basePath}/groups/${groupId}/`, z.object({ groups: z.array(ProjectGroupSchema) }));
735
+ const response = await requestWithValidation(`${basePath}/groups/${groupId}`, z.object({ groups: z.array(ProjectGroupSchema) }));
673
736
  if (response.groups.length === 0) {
674
737
  throw new ZohoProjectsError(`Project group not found: ${groupId}`, 404);
675
738
  }
676
739
  return response.groups[0];
677
740
  },
678
741
  async create(data) {
679
- const response = await requestWithValidation(`${basePath}/groups/`, z.object({ groups: z.array(ProjectGroupSchema) }), { method: "POST", data });
742
+ const response = await requestWithValidation(`${basePath}/groups`, z.object({ groups: z.array(ProjectGroupSchema) }), { method: "POST", data });
680
743
  return response.groups[0];
681
744
  },
682
745
  async update(groupId, data) {
683
- const response = await requestWithValidation(`${basePath}/groups/${groupId}/`, z.object({ groups: z.array(ProjectGroupSchema) }), { method: "PUT", data });
746
+ const response = await requestWithValidation(`${basePath}/groups/${groupId}`, z.object({ groups: z.array(ProjectGroupSchema) }), { method: "PUT", data });
684
747
  return response.groups[0];
685
748
  },
686
749
  async delete(groupId) {
687
- await request(`${basePath}/groups/${groupId}/`, { method: "DELETE" });
750
+ await request(`${basePath}/groups/${groupId}`, { method: "DELETE" });
688
751
  },
689
752
  },
690
753
  /**
@@ -692,10 +755,10 @@ export function createZohoProjectsClient(config) {
692
755
  */
693
756
  leaves: {
694
757
  async list(params) {
695
- const response = await requestWithValidation(`${basePath}/leaves/`, LeaveListResponseSchema, {
758
+ const response = await requestWithValidation(`${basePath}/leaves`, LeaveListResponseSchema, {
696
759
  params: {
697
- index: params?.index ?? 0,
698
- range: params?.range ?? DEFAULT_PAGE_SIZE,
760
+ page: params?.page ?? params?.index ?? 1,
761
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
699
762
  },
700
763
  });
701
764
  return { data: response.leaves, pageInfo: response.page_info };
@@ -704,25 +767,25 @@ export function createZohoProjectsClient(config) {
704
767
  return collectAll(this.iterate(options));
705
768
  },
706
769
  iterate(options) {
707
- return autoPaginate((index, range) => this.list({ index, range }), options);
770
+ return autoPaginate((page, per_page) => this.list({ page, per_page }), options);
708
771
  },
709
772
  async get(leaveId) {
710
- const response = await requestWithValidation(`${basePath}/leaves/${leaveId}/`, z.object({ leaves: z.array(LeaveSchema) }));
773
+ const response = await requestWithValidation(`${basePath}/leaves/${leaveId}`, z.object({ leaves: z.array(LeaveSchema) }));
711
774
  if (response.leaves.length === 0) {
712
775
  throw new ZohoProjectsError(`Leave not found: ${leaveId}`, 404);
713
776
  }
714
777
  return response.leaves[0];
715
778
  },
716
779
  async create(data) {
717
- const response = await requestWithValidation(`${basePath}/leaves/`, z.object({ leaves: z.array(LeaveSchema) }), { method: "POST", data });
780
+ const response = await requestWithValidation(`${basePath}/leaves`, z.object({ leaves: z.array(LeaveSchema) }), { method: "POST", data });
718
781
  return response.leaves[0];
719
782
  },
720
783
  async update(leaveId, data) {
721
- const response = await requestWithValidation(`${basePath}/leaves/${leaveId}/`, z.object({ leaves: z.array(LeaveSchema) }), { method: "PUT", data });
784
+ const response = await requestWithValidation(`${basePath}/leaves/${leaveId}`, z.object({ leaves: z.array(LeaveSchema) }), { method: "PUT", data });
722
785
  return response.leaves[0];
723
786
  },
724
787
  async delete(leaveId) {
725
- await request(`${basePath}/leaves/${leaveId}/`, { method: "DELETE" });
788
+ await request(`${basePath}/leaves/${leaveId}`, { method: "DELETE" });
726
789
  },
727
790
  },
728
791
  /**
@@ -730,10 +793,10 @@ export function createZohoProjectsClient(config) {
730
793
  */
731
794
  teams: {
732
795
  async list(params) {
733
- const response = await requestWithValidation(`${basePath}/teams/`, TeamListResponseSchema, {
796
+ const response = await requestWithValidation(`${basePath}/teams`, TeamListResponseSchema, {
734
797
  params: {
735
- index: params?.index ?? 0,
736
- range: params?.range ?? DEFAULT_PAGE_SIZE,
798
+ page: params?.page ?? params?.index ?? 1,
799
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
737
800
  },
738
801
  });
739
802
  return { data: response.teams, pageInfo: response.page_info };
@@ -742,32 +805,32 @@ export function createZohoProjectsClient(config) {
742
805
  return collectAll(this.iterate(options));
743
806
  },
744
807
  iterate(options) {
745
- return autoPaginate((index, range) => this.list({ index, range }), options);
808
+ return autoPaginate((page, per_page) => this.list({ page, per_page }), options);
746
809
  },
747
810
  async get(teamId) {
748
- const response = await requestWithValidation(`${basePath}/teams/${teamId}/`, z.object({ teams: z.array(TeamSchema) }));
811
+ const response = await requestWithValidation(`${basePath}/teams/${teamId}`, z.object({ teams: z.array(TeamSchema) }));
749
812
  if (response.teams.length === 0) {
750
813
  throw new ZohoProjectsError(`Team not found: ${teamId}`, 404);
751
814
  }
752
815
  return response.teams[0];
753
816
  },
754
817
  async create(data) {
755
- const response = await requestWithValidation(`${basePath}/teams/`, z.object({ teams: z.array(TeamSchema) }), { method: "POST", data });
818
+ const response = await requestWithValidation(`${basePath}/teams`, z.object({ teams: z.array(TeamSchema) }), { method: "POST", data });
756
819
  return response.teams[0];
757
820
  },
758
821
  async update(teamId, data) {
759
- const response = await requestWithValidation(`${basePath}/teams/${teamId}/`, z.object({ teams: z.array(TeamSchema) }), { method: "PUT", data });
822
+ const response = await requestWithValidation(`${basePath}/teams/${teamId}`, z.object({ teams: z.array(TeamSchema) }), { method: "PUT", data });
760
823
  return response.teams[0];
761
824
  },
762
825
  async delete(teamId) {
763
- await request(`${basePath}/teams/${teamId}/`, { method: "DELETE" });
826
+ await request(`${basePath}/teams/${teamId}`, { method: "DELETE" });
764
827
  },
765
828
  async addMembers(teamId, data) {
766
829
  const response = await requestWithValidation(`${basePath}/teams/${teamId}/members/`, z.object({ teams: z.array(TeamSchema) }), { method: "POST", data });
767
830
  return response.teams[0];
768
831
  },
769
832
  async removeMember(teamId, userId) {
770
- await request(`${basePath}/teams/${teamId}/members/${userId}/`, {
833
+ await request(`${basePath}/teams/${teamId}/members/${userId}`, {
771
834
  method: "DELETE",
772
835
  });
773
836
  },
@@ -777,10 +840,10 @@ export function createZohoProjectsClient(config) {
777
840
  */
778
841
  tasklists: {
779
842
  async list(projectId, params) {
780
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/tasklists/`, TaskListListResponseSchema, {
843
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/tasklists`, TaskListListResponseSchema, {
781
844
  params: {
782
- index: params?.index ?? 0,
783
- range: params?.range ?? DEFAULT_PAGE_SIZE,
845
+ page: params?.page ?? params?.index ?? 1,
846
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
784
847
  },
785
848
  });
786
849
  return { data: response.tasklists, pageInfo: response.page_info };
@@ -789,25 +852,28 @@ export function createZohoProjectsClient(config) {
789
852
  return collectAll(this.iterate(projectId, options));
790
853
  },
791
854
  iterate(projectId, options) {
792
- return autoPaginate((index, range) => this.list(projectId, { index, range }), options);
855
+ return autoPaginate((page, per_page) => this.list(projectId, { page, per_page }), options);
793
856
  },
794
857
  async get(projectId, tasklistId) {
795
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/tasklists/${tasklistId}/`, z.object({ tasklists: z.array(TaskListSchema) }));
858
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/tasklists/${tasklistId}`, z.object({ tasklists: z.array(TaskListSchema) }));
796
859
  if (response.tasklists.length === 0) {
797
860
  throw new ZohoProjectsError(`Task list not found: ${tasklistId}`, 404);
798
861
  }
799
862
  return response.tasklists[0];
800
863
  },
801
864
  async create(projectId, data) {
802
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/tasklists/`, z.object({ tasklists: z.array(TaskListSchema) }), { method: "POST", data });
803
- return response.tasklists[0];
865
+ // V3 API returns the tasklist directly, not wrapped in array
866
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/tasklists`, TaskListSchema, { method: "POST", data });
867
+ return response;
804
868
  },
805
869
  async update(projectId, tasklistId, data) {
806
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/tasklists/${tasklistId}/`, z.object({ tasklists: z.array(TaskListSchema) }), { method: "PUT", data });
807
- return response.tasklists[0];
870
+ // V3 API returns the tasklist directly, not wrapped in array
871
+ // V3 API uses PATCH method for updates
872
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/tasklists/${tasklistId}`, TaskListSchema, { method: "PATCH", data });
873
+ return response;
808
874
  },
809
875
  async delete(projectId, tasklistId) {
810
- await request(`${basePath}/projects/${projectId}/tasklists/${tasklistId}/`, { method: "DELETE" });
876
+ await request(`${basePath}/projects/${projectId}/tasklists/${tasklistId}`, { method: "DELETE" });
811
877
  },
812
878
  },
813
879
  /**
@@ -816,37 +882,73 @@ export function createZohoProjectsClient(config) {
816
882
  */
817
883
  phases: {
818
884
  async list(projectId, params) {
819
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/milestones/`, PhaseListResponseSchema, {
885
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/milestones`, PhaseListResponseSchema, {
820
886
  params: {
821
- index: params?.index ?? 0,
822
- range: params?.range ?? DEFAULT_PAGE_SIZE,
887
+ page: params?.page ?? params?.index ?? 1,
888
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
823
889
  },
824
890
  });
825
- return { data: response.milestones, pageInfo: response.page_info };
891
+ // V3 API returns page_info as array, normalize to object
892
+ let pageInfo;
893
+ if (response.page_info) {
894
+ if (Array.isArray(response.page_info)) {
895
+ const info = response.page_info[0];
896
+ if (info) {
897
+ pageInfo = {
898
+ page: info.page,
899
+ per_page: info.per_page,
900
+ has_more_page: info.has_next_page,
901
+ };
902
+ }
903
+ }
904
+ else {
905
+ pageInfo = response.page_info;
906
+ }
907
+ }
908
+ return { data: response.milestones, pageInfo };
826
909
  },
827
910
  async listAll(projectId, options) {
828
911
  return collectAll(this.iterate(projectId, options));
829
912
  },
830
913
  iterate(projectId, options) {
831
- return autoPaginate((index, range) => this.list(projectId, { index, range }), options);
914
+ return autoPaginate((page, per_page) => this.list(projectId, { page, per_page }), options);
832
915
  },
833
916
  async get(projectId, phaseId) {
834
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/milestones/${phaseId}/`, z.object({ milestones: z.array(PhaseSchema) }));
835
- if (response.milestones.length === 0) {
836
- throw new ZohoProjectsError(`Phase not found: ${phaseId}`, 404);
917
+ // V3 API returns milestone directly, legacy wraps in {milestones: [...]}
918
+ const schema = z.union([
919
+ PhaseSchema, // V3: returns milestone directly
920
+ z.object({ milestones: z.array(PhaseSchema) }), // Legacy
921
+ ]);
922
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/milestones/${phaseId}`, schema);
923
+ // Handle both formats
924
+ if (response && typeof response === "object" && "milestones" in response) {
925
+ const legacyResponse = response;
926
+ if (legacyResponse.milestones.length === 0) {
927
+ throw new ZohoProjectsError(`Phase not found: ${phaseId}`, 404);
928
+ }
929
+ return legacyResponse.milestones[0];
837
930
  }
838
- return response.milestones[0];
931
+ return response;
839
932
  },
840
933
  async create(projectId, data) {
841
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/milestones/`, z.object({ milestones: z.array(PhaseSchema) }), { method: "POST", data });
842
- return response.milestones[0];
934
+ // V3 API returns milestone directly, legacy wraps in {milestones: [...]}
935
+ const schema = z.union([
936
+ PhaseSchema, // V3: returns milestone directly
937
+ z.object({ milestones: z.array(PhaseSchema) }), // Legacy
938
+ ]);
939
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/milestones`, schema, { method: "POST", data });
940
+ // Handle both formats
941
+ if (response && typeof response === "object" && "milestones" in response) {
942
+ return response.milestones[0];
943
+ }
944
+ return response;
843
945
  },
844
946
  async update(projectId, phaseId, data) {
845
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/milestones/${phaseId}/`, z.object({ milestones: z.array(PhaseSchema) }), { method: "PUT", data });
947
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/milestones/${phaseId}`, z.object({ milestones: z.array(PhaseSchema) }), { method: "PUT", data });
846
948
  return response.milestones[0];
847
949
  },
848
950
  async delete(projectId, phaseId) {
849
- await request(`${basePath}/projects/${projectId}/milestones/${phaseId}/`, { method: "DELETE" });
951
+ await request(`${basePath}/projects/${projectId}/milestones/${phaseId}`, { method: "DELETE" });
850
952
  },
851
953
  },
852
954
  /**
@@ -855,10 +957,10 @@ export function createZohoProjectsClient(config) {
855
957
  */
856
958
  issues: {
857
959
  async list(projectId, params) {
858
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/bugs/`, IssueListResponseSchema, {
960
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/bugs`, IssueListResponseSchema, {
859
961
  params: {
860
- index: params?.index ?? 0,
861
- range: params?.range ?? DEFAULT_PAGE_SIZE,
962
+ page: params?.page ?? params?.index ?? 1,
963
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
862
964
  },
863
965
  });
864
966
  return { data: response.bugs, pageInfo: response.page_info };
@@ -867,25 +969,25 @@ export function createZohoProjectsClient(config) {
867
969
  return collectAll(this.iterate(projectId, options));
868
970
  },
869
971
  iterate(projectId, options) {
870
- return autoPaginate((index, range) => this.list(projectId, { index, range }), options);
972
+ return autoPaginate((page, per_page) => this.list(projectId, { page, per_page }), options);
871
973
  },
872
974
  async get(projectId, issueId) {
873
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/bugs/${issueId}/`, z.object({ bugs: z.array(IssueSchema) }));
975
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/bugs/${issueId}`, z.object({ bugs: z.array(IssueSchema) }));
874
976
  if (response.bugs.length === 0) {
875
977
  throw new ZohoProjectsError(`Issue not found: ${issueId}`, 404);
876
978
  }
877
979
  return response.bugs[0];
878
980
  },
879
981
  async create(projectId, data) {
880
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/bugs/`, z.object({ bugs: z.array(IssueSchema) }), { method: "POST", data });
982
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/bugs`, z.object({ bugs: z.array(IssueSchema) }), { method: "POST", data });
881
983
  return response.bugs[0];
882
984
  },
883
985
  async update(projectId, issueId, data) {
884
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/bugs/${issueId}/`, z.object({ bugs: z.array(IssueSchema) }), { method: "PUT", data });
986
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/bugs/${issueId}`, z.object({ bugs: z.array(IssueSchema) }), { method: "PUT", data });
885
987
  return response.bugs[0];
886
988
  },
887
989
  async delete(projectId, issueId) {
888
- await request(`${basePath}/projects/${projectId}/bugs/${issueId}/`, {
990
+ await request(`${basePath}/projects/${projectId}/bugs/${issueId}`, {
889
991
  method: "DELETE",
890
992
  });
891
993
  },
@@ -895,10 +997,10 @@ export function createZohoProjectsClient(config) {
895
997
  */
896
998
  forums: {
897
999
  async list(projectId, params) {
898
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/forums/`, ForumListResponseSchema, {
1000
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/forums`, ForumListResponseSchema, {
899
1001
  params: {
900
- index: params?.index ?? 0,
901
- range: params?.range ?? DEFAULT_PAGE_SIZE,
1002
+ page: params?.page ?? params?.index ?? 1,
1003
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
902
1004
  },
903
1005
  });
904
1006
  return { data: response.forums, pageInfo: response.page_info };
@@ -907,25 +1009,25 @@ export function createZohoProjectsClient(config) {
907
1009
  return collectAll(this.iterate(projectId, options));
908
1010
  },
909
1011
  iterate(projectId, options) {
910
- return autoPaginate((index, range) => this.list(projectId, { index, range }), options);
1012
+ return autoPaginate((page, per_page) => this.list(projectId, { page, per_page }), options);
911
1013
  },
912
1014
  async get(projectId, forumId) {
913
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/forums/${forumId}/`, z.object({ forums: z.array(ForumSchema) }));
1015
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/forums/${forumId}`, z.object({ forums: z.array(ForumSchema) }));
914
1016
  if (response.forums.length === 0) {
915
1017
  throw new ZohoProjectsError(`Forum not found: ${forumId}`, 404);
916
1018
  }
917
1019
  return response.forums[0];
918
1020
  },
919
1021
  async create(projectId, data) {
920
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/forums/`, z.object({ forums: z.array(ForumSchema) }), { method: "POST", data });
1022
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/forums`, z.object({ forums: z.array(ForumSchema) }), { method: "POST", data });
921
1023
  return response.forums[0];
922
1024
  },
923
1025
  async update(projectId, forumId, data) {
924
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/forums/${forumId}/`, z.object({ forums: z.array(ForumSchema) }), { method: "PUT", data });
1026
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/forums/${forumId}`, z.object({ forums: z.array(ForumSchema) }), { method: "PUT", data });
925
1027
  return response.forums[0];
926
1028
  },
927
1029
  async delete(projectId, forumId) {
928
- await request(`${basePath}/projects/${projectId}/forums/${forumId}/`, {
1030
+ await request(`${basePath}/projects/${projectId}/forums/${forumId}`, {
929
1031
  method: "DELETE",
930
1032
  });
931
1033
  },
@@ -935,10 +1037,10 @@ export function createZohoProjectsClient(config) {
935
1037
  */
936
1038
  events: {
937
1039
  async list(projectId, params) {
938
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/events/`, EventListResponseSchema, {
1040
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/events`, EventListResponseSchema, {
939
1041
  params: {
940
- index: params?.index ?? 0,
941
- range: params?.range ?? DEFAULT_PAGE_SIZE,
1042
+ page: params?.page ?? params?.index ?? 1,
1043
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
942
1044
  },
943
1045
  });
944
1046
  return { data: response.events, pageInfo: response.page_info };
@@ -947,84 +1049,178 @@ export function createZohoProjectsClient(config) {
947
1049
  return collectAll(this.iterate(projectId, options));
948
1050
  },
949
1051
  iterate(projectId, options) {
950
- return autoPaginate((index, range) => this.list(projectId, { index, range }), options);
1052
+ return autoPaginate((page, per_page) => this.list(projectId, { page, per_page }), options);
951
1053
  },
952
1054
  async get(projectId, eventId) {
953
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/events/${eventId}/`, z.object({ events: z.array(EventSchema) }));
1055
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/events/${eventId}`, z.object({ events: z.array(EventSchema) }));
954
1056
  if (response.events.length === 0) {
955
1057
  throw new ZohoProjectsError(`Event not found: ${eventId}`, 404);
956
1058
  }
957
1059
  return response.events[0];
958
1060
  },
959
1061
  async create(projectId, data) {
960
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/events/`, z.object({ events: z.array(EventSchema) }), { method: "POST", data });
1062
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/events`, z.object({ events: z.array(EventSchema) }), { method: "POST", data });
961
1063
  return response.events[0];
962
1064
  },
963
1065
  async update(projectId, eventId, data) {
964
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/events/${eventId}/`, z.object({ events: z.array(EventSchema) }), { method: "PUT", data });
1066
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/events/${eventId}`, z.object({ events: z.array(EventSchema) }), { method: "PUT", data });
965
1067
  return response.events[0];
966
1068
  },
967
1069
  async delete(projectId, eventId) {
968
- await request(`${basePath}/projects/${projectId}/events/${eventId}/`, {
1070
+ await request(`${basePath}/projects/${projectId}/events/${eventId}`, {
969
1071
  method: "DELETE",
970
1072
  });
971
1073
  },
972
1074
  },
973
1075
  /**
974
1076
  * Attachments API (project-scoped)
975
- * Note: Upload requires multipart/form-data
1077
+ * Note: V3 API requires entity_type and entity_id for listing
1078
+ * Note: V3 Upload requires WorkDrive integration - use workdrive.upload() then associate()
976
1079
  */
977
1080
  attachments: {
978
- async list(projectId, params) {
979
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/attachments/`, AttachmentListResponseSchema, {
1081
+ /**
1082
+ * List attachments for an entity (V3 API)
1083
+ * Note: V3 requires entity_type and entity_id parameters
1084
+ * @param projectId - Project ID
1085
+ * @param params - List parameters including required entity_type and entity_id
1086
+ */
1087
+ async listForEntity(projectId, params) {
1088
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/attachments`, AttachmentListResponseSchema, {
980
1089
  params: {
981
- index: params?.index ?? 0,
982
- range: params?.range ?? DEFAULT_PAGE_SIZE,
1090
+ entity_type: params.entity_type,
1091
+ entity_id: params.entity_id,
1092
+ page: params.page ?? 1,
1093
+ per_page: params.per_page ?? DEFAULT_PAGE_SIZE,
983
1094
  },
984
1095
  });
985
- return { data: response.attachments, pageInfo: response.page_info };
1096
+ // Handle both V3 (attachment) and legacy (attachments) response formats
1097
+ const attachments = "attachment" in response ? response.attachment : response.attachments;
1098
+ return { data: attachments, pageInfo: response.page_info };
986
1099
  },
987
- async listAll(projectId, options) {
988
- return collectAll(this.iterate(projectId, options));
1100
+ /**
1101
+ * List all attachments for an entity with auto-pagination
1102
+ */
1103
+ async listAllForEntity(projectId, params, options) {
1104
+ return collectAll(this.iterateForEntity(projectId, params, options));
989
1105
  },
990
- iterate(projectId, options) {
991
- return autoPaginate((index, range) => this.list(projectId, { index, range }), options);
1106
+ /**
1107
+ * Iterate over all attachments for an entity with auto-pagination
1108
+ */
1109
+ iterateForEntity(projectId, params, options) {
1110
+ return autoPaginate((page, per_page) => this.listForEntity(projectId, { ...params, page, per_page }), options);
1111
+ },
1112
+ /**
1113
+ * Convenience method: List attachments for a task
1114
+ */
1115
+ async listForTask(projectId, taskId, params) {
1116
+ return this.listForEntity(projectId, {
1117
+ entity_type: "task",
1118
+ entity_id: taskId,
1119
+ ...params,
1120
+ });
1121
+ },
1122
+ /**
1123
+ * Convenience method: List attachments for a bug/issue
1124
+ */
1125
+ async listForIssue(projectId, issueId, params) {
1126
+ return this.listForEntity(projectId, {
1127
+ entity_type: "bug",
1128
+ entity_id: issueId,
1129
+ ...params,
1130
+ });
992
1131
  },
993
1132
  async get(projectId, attachmentId) {
994
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/attachments/${attachmentId}/`, z.object({ attachments: z.array(AttachmentSchema) }));
995
- if (response.attachments.length === 0) {
996
- throw new ZohoProjectsError(`Attachment not found: ${attachmentId}`, 404);
1133
+ // V3 API may return attachment directly or wrapped
1134
+ const schema = z.union([
1135
+ AttachmentSchema,
1136
+ z.object({ attachments: z.array(AttachmentSchema) }),
1137
+ ]);
1138
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/attachments/${attachmentId}`, schema);
1139
+ if (response && typeof response === "object" && "attachments" in response) {
1140
+ const wrapped = response;
1141
+ if (wrapped.attachments.length === 0) {
1142
+ throw new ZohoProjectsError(`Attachment not found: ${attachmentId}`, 404);
1143
+ }
1144
+ return wrapped.attachments[0];
997
1145
  }
998
- return response.attachments[0];
1146
+ return response;
999
1147
  },
1000
1148
  /**
1001
- * Upload an attachment
1149
+ * Associate an existing attachment with an entity (V3 API)
1150
+ * Use this after uploading a file to WorkDrive
1002
1151
  * @param projectId - Project ID
1003
- * @param file - File buffer or stream
1004
- * @param filename - Original filename
1005
- * @param options - Additional options
1152
+ * @param attachmentId - Attachment ID (from WorkDrive upload)
1153
+ * @param data - Entity association details
1006
1154
  */
1007
- async upload(projectId, file, filename, options) {
1155
+ async associate(projectId, attachmentId, data) {
1008
1156
  const FormData = (await import("form-data")).default;
1009
1157
  const formData = new FormData();
1010
- formData.append("uploaddoc", file, filename);
1011
- if (options?.name)
1012
- formData.append("name", options.name);
1013
- if (options?.description)
1014
- formData.append("description", options.description);
1015
- if (options?.entity_type)
1016
- formData.append("entity_type", options.entity_type);
1017
- if (options?.entity_id)
1018
- formData.append("entity_id", options.entity_id);
1019
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/attachments/`, z.object({ attachments: z.array(AttachmentSchema) }), {
1158
+ formData.append("entity_type", data.entity_type);
1159
+ formData.append("entity_id", data.entity_id);
1160
+ await request(`${basePath}/projects/${projectId}/attachments/${attachmentId}`, {
1161
+ method: "POST",
1162
+ data: formData,
1163
+ headers: formData.getHeaders(),
1164
+ });
1165
+ },
1166
+ /**
1167
+ * Add attachments to an entity using attachment IDs (V3 API)
1168
+ * @param projectId - Project ID
1169
+ * @param entityType - Entity type (task, bug, forum)
1170
+ * @param entityId - Entity ID
1171
+ * @param data - Attachment IDs to add
1172
+ */
1173
+ async addToEntity(projectId, entityType, entityId, data) {
1174
+ const FormData = (await import("form-data")).default;
1175
+ const formData = new FormData();
1176
+ formData.append("attachment_ids", JSON.stringify(data.attachment_ids));
1177
+ await request(`${basePath}/module/${entityType}/entity/${entityId}/attachments`, {
1020
1178
  method: "POST",
1021
1179
  data: formData,
1022
1180
  headers: formData.getHeaders(),
1023
1181
  });
1024
- return response.attachments[0];
1025
1182
  },
1026
1183
  async delete(projectId, attachmentId) {
1027
- await request(`${basePath}/projects/${projectId}/attachments/${attachmentId}/`, { method: "DELETE" });
1184
+ await request(`${basePath}/projects/${projectId}/attachments/${attachmentId}`, { method: "DELETE" });
1185
+ },
1186
+ },
1187
+ /**
1188
+ * WorkDrive API for file uploads
1189
+ * V3 attachments require uploading to WorkDrive first, then associating with entities
1190
+ */
1191
+ workdrive: {
1192
+ /**
1193
+ * Upload a file to WorkDrive
1194
+ * After uploading, use attachments.associate() to link to a task/issue
1195
+ * @param file - File buffer or stream
1196
+ * @param filename - Original filename
1197
+ * @param options - Upload options including parent_id (folder ID)
1198
+ * @returns WorkDrive file object with ID to use for association
1199
+ */
1200
+ async upload(file, filename, options) {
1201
+ const FormData = (await import("form-data")).default;
1202
+ const formData = new FormData();
1203
+ formData.append("content", file, {
1204
+ filename: options.filename ?? filename,
1205
+ contentType: "application/octet-stream",
1206
+ });
1207
+ formData.append("parent_id", options.parent_id);
1208
+ if (options.override_name_exist !== undefined) {
1209
+ formData.append("override-name-exist", options.override_name_exist.toString());
1210
+ }
1211
+ // WorkDrive has its own API base URL
1212
+ const workdriveUrl = apiUrl.replace("projectsapi", "workdrive");
1213
+ const token = await tokenManager.getValidToken();
1214
+ const response = await httpClient.post(`${workdriveUrl}/api/v1/upload`, formData, {
1215
+ headers: {
1216
+ ...formData.getHeaders(),
1217
+ Authorization: `Zoho-oauthtoken ${token}`,
1218
+ },
1219
+ });
1220
+ if (!response.data.data || response.data.data.length === 0) {
1221
+ throw new ZohoProjectsError("WorkDrive upload returned no data");
1222
+ }
1223
+ return response.data.data[0];
1028
1224
  },
1029
1225
  },
1030
1226
  /**
@@ -1032,10 +1228,10 @@ export function createZohoProjectsClient(config) {
1032
1228
  */
1033
1229
  documents: {
1034
1230
  async list(projectId, params) {
1035
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/documents/`, DocumentListResponseSchema, {
1231
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/documents`, DocumentListResponseSchema, {
1036
1232
  params: {
1037
- index: params?.index ?? 0,
1038
- range: params?.range ?? DEFAULT_PAGE_SIZE,
1233
+ page: params?.page ?? params?.index ?? 1,
1234
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
1039
1235
  folder_id: params?.folder_id,
1040
1236
  },
1041
1237
  });
@@ -1045,10 +1241,10 @@ export function createZohoProjectsClient(config) {
1045
1241
  return collectAll(this.iterate(projectId, options));
1046
1242
  },
1047
1243
  iterate(projectId, options) {
1048
- return autoPaginate((index, range) => this.list(projectId, { index, range }), options);
1244
+ return autoPaginate((page, per_page) => this.list(projectId, { page, per_page }), options);
1049
1245
  },
1050
1246
  async get(projectId, documentId) {
1051
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/documents/${documentId}/`, z.object({ documents: z.array(DocumentSchema) }));
1247
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/documents/${documentId}`, z.object({ documents: z.array(DocumentSchema) }));
1052
1248
  if (response.documents.length === 0) {
1053
1249
  throw new ZohoProjectsError(`Document not found: ${documentId}`, 404);
1054
1250
  }
@@ -1067,7 +1263,7 @@ export function createZohoProjectsClient(config) {
1067
1263
  formData.append("description", options.description);
1068
1264
  if (options?.folder_id)
1069
1265
  formData.append("folder_id", options.folder_id);
1070
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/documents/`, z.object({ documents: z.array(DocumentSchema) }), {
1266
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/documents`, z.object({ documents: z.array(DocumentSchema) }), {
1071
1267
  method: "POST",
1072
1268
  data: formData,
1073
1269
  headers: formData.getHeaders(),
@@ -1075,25 +1271,25 @@ export function createZohoProjectsClient(config) {
1075
1271
  return response.documents[0];
1076
1272
  },
1077
1273
  async update(projectId, documentId, data) {
1078
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/documents/${documentId}/`, z.object({ documents: z.array(DocumentSchema) }), { method: "PUT", data });
1274
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/documents/${documentId}`, z.object({ documents: z.array(DocumentSchema) }), { method: "PUT", data });
1079
1275
  return response.documents[0];
1080
1276
  },
1081
1277
  async delete(projectId, documentId) {
1082
- await request(`${basePath}/projects/${projectId}/documents/${documentId}/`, { method: "DELETE" });
1278
+ await request(`${basePath}/projects/${projectId}/documents/${documentId}`, { method: "DELETE" });
1083
1279
  },
1084
1280
  // Folder operations
1085
1281
  folders: {
1086
1282
  async list(projectId, params) {
1087
1283
  const response = await requestWithValidation(`${basePath}/projects/${projectId}/folders/`, DocumentFolderListResponseSchema, {
1088
1284
  params: {
1089
- index: params?.index ?? 0,
1090
- range: params?.range ?? DEFAULT_PAGE_SIZE,
1285
+ page: params?.page ?? params?.index ?? 1,
1286
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
1091
1287
  },
1092
1288
  });
1093
1289
  return { data: response.folders, pageInfo: response.page_info };
1094
1290
  },
1095
1291
  async get(projectId, folderId) {
1096
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/folders/${folderId}/`, z.object({ folders: z.array(DocumentFolderSchema) }));
1292
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/folders/${folderId}`, z.object({ folders: z.array(DocumentFolderSchema) }));
1097
1293
  if (response.folders.length === 0) {
1098
1294
  throw new ZohoProjectsError(`Folder not found: ${folderId}`, 404);
1099
1295
  }
@@ -1104,11 +1300,11 @@ export function createZohoProjectsClient(config) {
1104
1300
  return response.folders[0];
1105
1301
  },
1106
1302
  async update(projectId, folderId, data) {
1107
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/folders/${folderId}/`, z.object({ folders: z.array(DocumentFolderSchema) }), { method: "PUT", data });
1303
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/folders/${folderId}`, z.object({ folders: z.array(DocumentFolderSchema) }), { method: "PUT", data });
1108
1304
  return response.folders[0];
1109
1305
  },
1110
1306
  async delete(projectId, folderId) {
1111
- await request(`${basePath}/projects/${projectId}/folders/${folderId}/`, { method: "DELETE" });
1307
+ await request(`${basePath}/projects/${projectId}/folders/${folderId}`, { method: "DELETE" });
1112
1308
  },
1113
1309
  },
1114
1310
  },
@@ -1154,10 +1350,10 @@ export function createZohoProjectsClient(config) {
1154
1350
  const commentsPath = `${basePath}/projects/${projectId}/${entityType}/${entityId}/comments`;
1155
1351
  return {
1156
1352
  async list(params) {
1157
- const response = await requestWithValidation(`${commentsPath}/`, CommentListResponseSchema, {
1353
+ const response = await requestWithValidation(`${commentsPath}`, CommentListResponseSchema, {
1158
1354
  params: {
1159
- index: params?.index ?? 0,
1160
- range: params?.range ?? DEFAULT_PAGE_SIZE,
1355
+ page: params?.page ?? params?.index ?? 1,
1356
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
1161
1357
  },
1162
1358
  });
1163
1359
  return { data: response.comments, pageInfo: response.page_info };
@@ -1166,25 +1362,25 @@ export function createZohoProjectsClient(config) {
1166
1362
  return collectAll(this.iterate(options));
1167
1363
  },
1168
1364
  iterate(options) {
1169
- return autoPaginate((index, range) => this.list({ index, range }), options);
1365
+ return autoPaginate((page, per_page) => this.list({ page, per_page }), options);
1170
1366
  },
1171
1367
  async get(commentId) {
1172
- const response = await requestWithValidation(`${commentsPath}/${commentId}/`, z.object({ comments: z.array(CommentSchema) }));
1368
+ const response = await requestWithValidation(`${commentsPath}/${commentId}`, z.object({ comments: z.array(CommentSchema) }));
1173
1369
  if (response.comments.length === 0) {
1174
1370
  throw new ZohoProjectsError(`Comment not found: ${commentId}`, 404);
1175
1371
  }
1176
1372
  return response.comments[0];
1177
1373
  },
1178
1374
  async create(data) {
1179
- const response = await requestWithValidation(`${commentsPath}/`, z.object({ comments: z.array(CommentSchema) }), { method: "POST", data });
1375
+ const response = await requestWithValidation(`${commentsPath}`, z.object({ comments: z.array(CommentSchema) }), { method: "POST", data });
1180
1376
  return response.comments[0];
1181
1377
  },
1182
1378
  async update(commentId, data) {
1183
- const response = await requestWithValidation(`${commentsPath}/${commentId}/`, z.object({ comments: z.array(CommentSchema) }), { method: "PUT", data });
1379
+ const response = await requestWithValidation(`${commentsPath}/${commentId}`, z.object({ comments: z.array(CommentSchema) }), { method: "PUT", data });
1184
1380
  return response.comments[0];
1185
1381
  },
1186
1382
  async delete(commentId) {
1187
- await request(`${commentsPath}/${commentId}/`, {
1383
+ await request(`${commentsPath}/${commentId}`, {
1188
1384
  method: "DELETE",
1189
1385
  });
1190
1386
  },
@@ -1233,10 +1429,10 @@ export function createZohoProjectsClient(config) {
1233
1429
  const followersPath = `${basePath}/projects/${projectId}/${entityType}/${entityId}/followers`;
1234
1430
  return {
1235
1431
  async list(params) {
1236
- const response = await requestWithValidation(`${followersPath}/`, FollowerListResponseSchema, {
1432
+ const response = await requestWithValidation(`${followersPath}`, FollowerListResponseSchema, {
1237
1433
  params: {
1238
- index: params?.index ?? 0,
1239
- range: params?.range ?? DEFAULT_PAGE_SIZE,
1434
+ page: params?.page ?? params?.index ?? 1,
1435
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
1240
1436
  },
1241
1437
  });
1242
1438
  return { data: response.followers, pageInfo: response.page_info };
@@ -1245,14 +1441,14 @@ export function createZohoProjectsClient(config) {
1245
1441
  return collectAll(this.iterate(options));
1246
1442
  },
1247
1443
  iterate(options) {
1248
- return autoPaginate((index, range) => this.list({ index, range }), options);
1444
+ return autoPaginate((page, per_page) => this.list({ page, per_page }), options);
1249
1445
  },
1250
1446
  async add(data) {
1251
- const response = await requestWithValidation(`${followersPath}/`, z.object({ followers: z.array(FollowerSchema) }), { method: "POST", data });
1447
+ const response = await requestWithValidation(`${followersPath}`, z.object({ followers: z.array(FollowerSchema) }), { method: "POST", data });
1252
1448
  return response.followers;
1253
1449
  },
1254
1450
  async remove(userId) {
1255
- await request(`${followersPath}/${userId}/`, {
1451
+ await request(`${followersPath}/${userId}`, {
1256
1452
  method: "DELETE",
1257
1453
  });
1258
1454
  },
@@ -1280,10 +1476,10 @@ export function createZohoProjectsClient(config) {
1280
1476
  */
1281
1477
  dashboards: {
1282
1478
  async list(params) {
1283
- const response = await requestWithValidation(`${basePath}/dashboards/`, DashboardListResponseSchema, {
1479
+ const response = await requestWithValidation(`${basePath}/dashboards`, DashboardListResponseSchema, {
1284
1480
  params: {
1285
- index: params?.index ?? 0,
1286
- range: params?.range ?? DEFAULT_PAGE_SIZE,
1481
+ page: params?.page ?? params?.index ?? 1,
1482
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
1287
1483
  },
1288
1484
  });
1289
1485
  return { data: response.dashboards, pageInfo: response.page_info };
@@ -1292,25 +1488,25 @@ export function createZohoProjectsClient(config) {
1292
1488
  return collectAll(this.iterate(options));
1293
1489
  },
1294
1490
  iterate(options) {
1295
- return autoPaginate((index, range) => this.list({ index, range }), options);
1491
+ return autoPaginate((page, per_page) => this.list({ page, per_page }), options);
1296
1492
  },
1297
1493
  async get(dashboardId) {
1298
- const response = await requestWithValidation(`${basePath}/dashboards/${dashboardId}/`, z.object({ dashboards: z.array(DashboardSchema) }));
1494
+ const response = await requestWithValidation(`${basePath}/dashboards/${dashboardId}`, z.object({ dashboards: z.array(DashboardSchema) }));
1299
1495
  if (response.dashboards.length === 0) {
1300
1496
  throw new ZohoProjectsError(`Dashboard not found: ${dashboardId}`, 404);
1301
1497
  }
1302
1498
  return response.dashboards[0];
1303
1499
  },
1304
1500
  async create(data) {
1305
- const response = await requestWithValidation(`${basePath}/dashboards/`, z.object({ dashboards: z.array(DashboardSchema) }), { method: "POST", data });
1501
+ const response = await requestWithValidation(`${basePath}/dashboards`, z.object({ dashboards: z.array(DashboardSchema) }), { method: "POST", data });
1306
1502
  return response.dashboards[0];
1307
1503
  },
1308
1504
  async update(dashboardId, data) {
1309
- const response = await requestWithValidation(`${basePath}/dashboards/${dashboardId}/`, z.object({ dashboards: z.array(DashboardSchema) }), { method: "PUT", data });
1505
+ const response = await requestWithValidation(`${basePath}/dashboards/${dashboardId}`, z.object({ dashboards: z.array(DashboardSchema) }), { method: "PUT", data });
1310
1506
  return response.dashboards[0];
1311
1507
  },
1312
1508
  async delete(dashboardId) {
1313
- await request(`${basePath}/dashboards/${dashboardId}/`, {
1509
+ await request(`${basePath}/dashboards/${dashboardId}`, {
1314
1510
  method: "DELETE",
1315
1511
  });
1316
1512
  },
@@ -1321,10 +1517,10 @@ export function createZohoProjectsClient(config) {
1321
1517
  const widgetsPath = `${basePath}/dashboards/${dashboardId}/widgets`;
1322
1518
  return {
1323
1519
  async list(params) {
1324
- const response = await requestWithValidation(`${widgetsPath}/`, WidgetListResponseSchema, {
1520
+ const response = await requestWithValidation(`${widgetsPath}`, WidgetListResponseSchema, {
1325
1521
  params: {
1326
- index: params?.index ?? 0,
1327
- range: params?.range ?? DEFAULT_PAGE_SIZE,
1522
+ page: params?.page ?? params?.index ?? 1,
1523
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
1328
1524
  },
1329
1525
  });
1330
1526
  return { data: response.widgets, pageInfo: response.page_info };
@@ -1333,25 +1529,25 @@ export function createZohoProjectsClient(config) {
1333
1529
  return collectAll(this.iterate(options));
1334
1530
  },
1335
1531
  iterate(options) {
1336
- return autoPaginate((index, range) => this.list({ index, range }), options);
1532
+ return autoPaginate((page, per_page) => this.list({ page, per_page }), options);
1337
1533
  },
1338
1534
  async get(widgetId) {
1339
- const response = await requestWithValidation(`${widgetsPath}/${widgetId}/`, z.object({ widgets: z.array(WidgetSchema) }));
1535
+ const response = await requestWithValidation(`${widgetsPath}/${widgetId}`, z.object({ widgets: z.array(WidgetSchema) }));
1340
1536
  if (response.widgets.length === 0) {
1341
1537
  throw new ZohoProjectsError(`Widget not found: ${widgetId}`, 404);
1342
1538
  }
1343
1539
  return response.widgets[0];
1344
1540
  },
1345
1541
  async create(data) {
1346
- const response = await requestWithValidation(`${widgetsPath}/`, z.object({ widgets: z.array(WidgetSchema) }), { method: "POST", data });
1542
+ const response = await requestWithValidation(`${widgetsPath}`, z.object({ widgets: z.array(WidgetSchema) }), { method: "POST", data });
1347
1543
  return response.widgets[0];
1348
1544
  },
1349
1545
  async update(widgetId, data) {
1350
- const response = await requestWithValidation(`${widgetsPath}/${widgetId}/`, z.object({ widgets: z.array(WidgetSchema) }), { method: "PUT", data });
1546
+ const response = await requestWithValidation(`${widgetsPath}/${widgetId}`, z.object({ widgets: z.array(WidgetSchema) }), { method: "PUT", data });
1351
1547
  return response.widgets[0];
1352
1548
  },
1353
1549
  async delete(widgetId) {
1354
- await request(`${widgetsPath}/${widgetId}/`, {
1550
+ await request(`${widgetsPath}/${widgetId}`, {
1355
1551
  method: "DELETE",
1356
1552
  });
1357
1553
  },
@@ -1364,10 +1560,10 @@ export function createZohoProjectsClient(config) {
1364
1560
  */
1365
1561
  reports: {
1366
1562
  async list(params) {
1367
- const response = await requestWithValidation(`${basePath}/reports/`, ReportListResponseSchema, {
1563
+ const response = await requestWithValidation(`${basePath}/reports`, ReportListResponseSchema, {
1368
1564
  params: {
1369
- index: params?.index ?? 0,
1370
- range: params?.range ?? DEFAULT_PAGE_SIZE,
1565
+ page: params?.page ?? params?.index ?? 1,
1566
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
1371
1567
  },
1372
1568
  });
1373
1569
  return { data: response.reports, pageInfo: response.page_info };
@@ -1376,10 +1572,10 @@ export function createZohoProjectsClient(config) {
1376
1572
  return collectAll(this.iterate(options));
1377
1573
  },
1378
1574
  iterate(options) {
1379
- return autoPaginate((index, range) => this.list({ index, range }), options);
1575
+ return autoPaginate((page, per_page) => this.list({ page, per_page }), options);
1380
1576
  },
1381
1577
  async get(reportId) {
1382
- const response = await requestWithValidation(`${basePath}/reports/${reportId}/`, z.object({ reports: z.array(ReportSchema) }));
1578
+ const response = await requestWithValidation(`${basePath}/reports/${reportId}`, z.object({ reports: z.array(ReportSchema) }));
1383
1579
  if (response.reports.length === 0) {
1384
1580
  throw new ZohoProjectsError(`Report not found: ${reportId}`, 404);
1385
1581
  }
@@ -1406,10 +1602,10 @@ export function createZohoProjectsClient(config) {
1406
1602
  * List reports for a specific project
1407
1603
  */
1408
1604
  async listForProject(projectId, params) {
1409
- const response = await requestWithValidation(`${basePath}/projects/${projectId}/reports/`, ReportListResponseSchema, {
1605
+ const response = await requestWithValidation(`${basePath}/projects/${projectId}/reports`, ReportListResponseSchema, {
1410
1606
  params: {
1411
- index: params?.index ?? 0,
1412
- range: params?.range ?? DEFAULT_PAGE_SIZE,
1607
+ page: params?.page ?? params?.index ?? 1,
1608
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
1413
1609
  },
1414
1610
  });
1415
1611
  return { data: response.reports, pageInfo: response.page_info };
@@ -1424,7 +1620,7 @@ export function createZohoProjectsClient(config) {
1424
1620
  * Search across all entities
1425
1621
  */
1426
1622
  async query(query) {
1427
- const response = await requestWithValidation(`${basePath}/search/`, SearchResponseSchema, {
1623
+ const response = await requestWithValidation(`${basePath}/search`, SearchResponseSchema, {
1428
1624
  params: {
1429
1625
  search_term: query.search_term,
1430
1626
  entity_type: query.entity_type,
@@ -1486,13 +1682,13 @@ export function createZohoProjectsClient(config) {
1486
1682
  * List items in trash
1487
1683
  */
1488
1684
  async list(params) {
1489
- const response = await requestWithValidation(`${basePath}/trash/`, TrashListResponseSchema, {
1685
+ const response = await requestWithValidation(`${basePath}/trash`, TrashListResponseSchema, {
1490
1686
  params: {
1491
1687
  entity_type: params?.entity_type,
1492
1688
  project_id: params?.project_id,
1493
1689
  deleted_by: params?.deleted_by,
1494
- index: params?.index ?? 0,
1495
- range: params?.range ?? DEFAULT_PAGE_SIZE,
1690
+ page: params?.page ?? params?.index ?? 1,
1691
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
1496
1692
  },
1497
1693
  });
1498
1694
  const items = response.trash || response.deleted_items || [];
@@ -1508,7 +1704,7 @@ export function createZohoProjectsClient(config) {
1508
1704
  * Iterate over all trash items with auto-pagination
1509
1705
  */
1510
1706
  iterate(params, options) {
1511
- return autoPaginate((index, range) => this.list({ ...params, index, range }), options);
1707
+ return autoPaginate((page, per_page) => this.list({ ...params, page, per_page }), options);
1512
1708
  },
1513
1709
  /**
1514
1710
  * Restore an item from trash
@@ -1521,7 +1717,7 @@ export function createZohoProjectsClient(config) {
1521
1717
  * Permanently delete an item from trash
1522
1718
  */
1523
1719
  async permanentDelete(entityType, itemId) {
1524
- await request(`${basePath}/trash/${entityType}/${itemId}/`, {
1720
+ await request(`${basePath}/trash/${entityType}/${itemId}`, {
1525
1721
  method: "DELETE",
1526
1722
  });
1527
1723
  },
@@ -1530,8 +1726,8 @@ export function createZohoProjectsClient(config) {
1530
1726
  */
1531
1727
  async empty(entityType) {
1532
1728
  const path = entityType
1533
- ? `${basePath}/trash/${entityType}/`
1534
- : `${basePath}/trash/`;
1729
+ ? `${basePath}/trash/${entityType}`
1730
+ : `${basePath}/trash`;
1535
1731
  await request(path, { method: "DELETE" });
1536
1732
  },
1537
1733
  /**
@@ -1557,7 +1753,7 @@ export function createZohoProjectsClient(config) {
1557
1753
  * Get a specific portal by ID
1558
1754
  */
1559
1755
  async get(portalId) {
1560
- const response = await requestWithValidation(`/restapi/portals/${portalId}/`, z.object({ portals: z.array(PortalSchema) }));
1756
+ const response = await requestWithValidation(`/restapi/portals/${portalId}`, z.object({ portals: z.array(PortalSchema) }));
1561
1757
  if (response.portals.length === 0) {
1562
1758
  throw new ZohoProjectsError(`Portal not found: ${portalId}`, 404);
1563
1759
  }
@@ -1579,7 +1775,7 @@ export function createZohoProjectsClient(config) {
1579
1775
  * List all modules in the portal
1580
1776
  */
1581
1777
  async list(params) {
1582
- const response = await requestWithValidation(`${basePath}/settings/modules/`, ModuleListResponseSchema, {
1778
+ const response = await requestWithValidation(`${basePath}/settings/modules`, ModuleListResponseSchema, {
1583
1779
  params: {
1584
1780
  is_customized: params?.is_customized,
1585
1781
  is_default: params?.is_default,
@@ -1592,7 +1788,7 @@ export function createZohoProjectsClient(config) {
1592
1788
  * Get a specific module by ID
1593
1789
  */
1594
1790
  async get(moduleId) {
1595
- const response = await requestWithValidation(`${basePath}/settings/modules/${moduleId}/`, z.object({ modules: z.array(ModuleSchema) }));
1791
+ const response = await requestWithValidation(`${basePath}/settings/modules/${moduleId}`, z.object({ modules: z.array(ModuleSchema) }));
1596
1792
  if (response.modules.length === 0) {
1597
1793
  throw new ZohoProjectsError(`Module not found: ${moduleId}`, 404);
1598
1794
  }
@@ -1609,7 +1805,7 @@ export function createZohoProjectsClient(config) {
1609
1805
  * Get a specific field definition
1610
1806
  */
1611
1807
  async getField(moduleId, fieldId) {
1612
- const response = await requestWithValidation(`${basePath}/settings/modules/${moduleId}/fields/${fieldId}/`, z.object({ fields: z.array(ModuleFieldSchema) }));
1808
+ const response = await requestWithValidation(`${basePath}/settings/modules/${moduleId}/fields/${fieldId}`, z.object({ fields: z.array(ModuleFieldSchema) }));
1613
1809
  if (response.fields.length === 0) {
1614
1810
  throw new ZohoProjectsError(`Field not found: ${fieldId}`, 404);
1615
1811
  }
@@ -1766,10 +1962,10 @@ export function createZohoProjectsClient(config) {
1766
1962
  * List custom views for a specific entity type
1767
1963
  */
1768
1964
  async list(entityType, params) {
1769
- const response = await requestWithValidation(`${basePath}/${entityType}/customviews/`, CustomViewListResponseSchema, {
1965
+ const response = await requestWithValidation(`${basePath}/${entityType}/customviews`, CustomViewListResponseSchema, {
1770
1966
  params: {
1771
- index: params?.index ?? 0,
1772
- range: params?.range ?? DEFAULT_PAGE_SIZE,
1967
+ page: params?.page ?? params?.index ?? 1,
1968
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
1773
1969
  },
1774
1970
  });
1775
1971
  return { data: response.customviews, pageInfo: response.page_info };
@@ -1784,13 +1980,13 @@ export function createZohoProjectsClient(config) {
1784
1980
  * Iterate over all custom views with auto-pagination
1785
1981
  */
1786
1982
  iterate(entityType, options) {
1787
- return autoPaginate((index, range) => this.list(entityType, { index, range }), options);
1983
+ return autoPaginate((page, per_page) => this.list(entityType, { page, per_page }), options);
1788
1984
  },
1789
1985
  /**
1790
1986
  * Get a specific custom view
1791
1987
  */
1792
1988
  async get(entityType, viewId) {
1793
- const response = await requestWithValidation(`${basePath}/${entityType}/customviews/${viewId}/`, z.object({ customviews: z.array(CustomViewSchema) }));
1989
+ const response = await requestWithValidation(`${basePath}/${entityType}/customviews/${viewId}`, z.object({ customviews: z.array(CustomViewSchema) }));
1794
1990
  if (response.customviews.length === 0) {
1795
1991
  throw new ZohoProjectsError(`Custom view not found: ${viewId}`, 404);
1796
1992
  }
@@ -1800,21 +1996,21 @@ export function createZohoProjectsClient(config) {
1800
1996
  * Create a custom view
1801
1997
  */
1802
1998
  async create(entityType, data) {
1803
- const response = await requestWithValidation(`${basePath}/${entityType}/customviews/`, z.object({ customviews: z.array(CustomViewSchema) }), { method: "POST", data });
1999
+ const response = await requestWithValidation(`${basePath}/${entityType}/customviews`, z.object({ customviews: z.array(CustomViewSchema) }), { method: "POST", data });
1804
2000
  return response.customviews[0];
1805
2001
  },
1806
2002
  /**
1807
2003
  * Update a custom view
1808
2004
  */
1809
2005
  async update(entityType, viewId, data) {
1810
- const response = await requestWithValidation(`${basePath}/${entityType}/customviews/${viewId}/`, z.object({ customviews: z.array(CustomViewSchema) }), { method: "PUT", data });
2006
+ const response = await requestWithValidation(`${basePath}/${entityType}/customviews/${viewId}`, z.object({ customviews: z.array(CustomViewSchema) }), { method: "PUT", data });
1811
2007
  return response.customviews[0];
1812
2008
  },
1813
2009
  /**
1814
2010
  * Delete a custom view
1815
2011
  */
1816
2012
  async delete(entityType, viewId) {
1817
- await request(`${basePath}/${entityType}/customviews/${viewId}/`, {
2013
+ await request(`${basePath}/${entityType}/customviews/${viewId}`, {
1818
2014
  method: "DELETE",
1819
2015
  });
1820
2016
  },
@@ -1870,10 +2066,10 @@ export function createZohoProjectsClient(config) {
1870
2066
  * List all blueprints
1871
2067
  */
1872
2068
  async list(params) {
1873
- const response = await requestWithValidation(`${basePath}/settings/blueprints/`, BlueprintListResponseSchema, {
2069
+ const response = await requestWithValidation(`${basePath}/settings/blueprints`, BlueprintListResponseSchema, {
1874
2070
  params: {
1875
- index: params?.index ?? 0,
1876
- range: params?.range ?? DEFAULT_PAGE_SIZE,
2071
+ page: params?.page ?? params?.index ?? 1,
2072
+ per_page: params?.per_page ?? params?.range ?? DEFAULT_PAGE_SIZE,
1877
2073
  },
1878
2074
  });
1879
2075
  return { data: response.blueprints, pageInfo: response.page_info };
@@ -1888,13 +2084,13 @@ export function createZohoProjectsClient(config) {
1888
2084
  * Iterate over all blueprints with auto-pagination
1889
2085
  */
1890
2086
  iterate(options) {
1891
- return autoPaginate((index, range) => this.list({ index, range }), options);
2087
+ return autoPaginate((page, per_page) => this.list({ page, per_page }), options);
1892
2088
  },
1893
2089
  /**
1894
2090
  * Get a specific blueprint
1895
2091
  */
1896
2092
  async get(blueprintId) {
1897
- const response = await requestWithValidation(`${basePath}/settings/blueprints/${blueprintId}/`, z.object({ blueprints: z.array(BlueprintSchema) }));
2093
+ const response = await requestWithValidation(`${basePath}/settings/blueprints/${blueprintId}`, z.object({ blueprints: z.array(BlueprintSchema) }));
1898
2094
  if (response.blueprints.length === 0) {
1899
2095
  throw new ZohoProjectsError(`Blueprint not found: ${blueprintId}`, 404);
1900
2096
  }
@@ -1921,7 +2117,7 @@ export function createZohoProjectsClient(config) {
1921
2117
  */
1922
2118
  async executeTransition(projectId, moduleType, transitionId, data) {
1923
2119
  const modulePath = moduleType === "Task" ? "tasks" : "bugs";
1924
- await request(`${basePath}/projects/${projectId}/${modulePath}/${data.entity_id}/transitions/${transitionId}/`, {
2120
+ await request(`${basePath}/projects/${projectId}/${modulePath}/${data.entity_id}/transitions/${transitionId}`, {
1925
2121
  method: "POST",
1926
2122
  data: {
1927
2123
  skip_bug_validation: data.skip_bug_validation,