@doist/comms-sdk 0.5.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -121,6 +121,31 @@ const refreshed = await refreshAuthToken({
121
121
  const api = new CommsApi(refreshed.accessToken)
122
122
  ```
123
123
 
124
+ ### REST hooks
125
+
126
+ REST hooks require a Todoist OAuth access token with Todoist Comms audience. Subscribe
127
+ with an HTTPS target URL up to 150 characters and one of the exported
128
+ `HOOK_EVENTS`; the backend will POST webhook payloads to that URL when
129
+ matching events happen:
130
+
131
+ ```typescript
132
+ import { CommsApi, HOOK_EVENTS } from '@doist/comms-sdk'
133
+
134
+ const api = new CommsApi('TODOIST_OAUTH_ACCESS_TOKEN')
135
+
136
+ const subscription = await api.hooks.subscribe({
137
+ targetUrl: 'https://example.com/comms-hook',
138
+ event: 'thread_added',
139
+ workspaceId: 1,
140
+ })
141
+
142
+ console.log(subscription.id, HOOK_EVENTS)
143
+
144
+ await api.hooks.unsubscribe({
145
+ targetUrl: 'https://example.com/comms-hook',
146
+ })
147
+ ```
148
+
124
149
  ### Short-lived processes (CLIs, scripts)
125
150
 
126
151
  On Node, the SDK keeps a connection pool alive across requests so HTTP/2
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HooksClient = void 0;
4
+ const endpoints_1 = require("../consts/endpoints");
5
+ const http_client_1 = require("../transport/http-client");
6
+ const entities_1 = require("../types/entities");
7
+ const base_client_1 = require("./base-client");
8
+ /**
9
+ * Client for `/api/v1/hooks/`. REST hooks require Todoist OAuth bearer tokens
10
+ * that include Comms audience; session tokens are rejected by the backend.
11
+ */
12
+ class HooksClient extends base_client_1.BaseClient {
13
+ /**
14
+ * Subscribes the current OAuth app to a REST hook event.
15
+ *
16
+ * @param args - The hook subscription parameters.
17
+ * @param args.targetUrl - HTTPS endpoint, up to 150 characters, that will receive webhook POSTs.
18
+ * @param args.event - Event name to subscribe to.
19
+ * @param args.workspaceId - Optional workspace filter.
20
+ * @param args.channelId - Optional channel filter.
21
+ * @param args.threadId - Optional thread filter.
22
+ * @param args.conversationId - Optional conversation filter for message events.
23
+ * @returns The `{ status: 'ok', id }` response for the subscription.
24
+ */
25
+ subscribe(args) {
26
+ return (0, http_client_1.request)({
27
+ httpMethod: 'POST',
28
+ baseUri: this.getBaseUri(),
29
+ relativePath: `${endpoints_1.ENDPOINT_HOOKS}/subscribe`,
30
+ apiToken: this.apiToken,
31
+ payload: args,
32
+ customFetch: this.customFetch,
33
+ }).then((response) => entities_1.HookSubscribeResponseSchema.parse(response.data));
34
+ }
35
+ /**
36
+ * Unsubscribes all hooks with the target URL for the current OAuth app.
37
+ *
38
+ * @param args - The hook unsubscribe parameters.
39
+ * @param args.targetUrl - Target URL to remove.
40
+ */
41
+ unsubscribe(args) {
42
+ return (0, http_client_1.request)({
43
+ httpMethod: 'POST',
44
+ baseUri: this.getBaseUri(),
45
+ relativePath: `${endpoints_1.ENDPOINT_HOOKS}/unsubscribe`,
46
+ apiToken: this.apiToken,
47
+ payload: args,
48
+ customFetch: this.customFetch,
49
+ }).then((response) => entities_1.StatusOkSchema.parse(response.data));
50
+ }
51
+ }
52
+ exports.HooksClient = HooksClient;
@@ -7,6 +7,7 @@ const comments_client_1 = require("./clients/comments-client");
7
7
  const conversation_messages_client_1 = require("./clients/conversation-messages-client");
8
8
  const conversations_client_1 = require("./clients/conversations-client");
9
9
  const groups_client_1 = require("./clients/groups-client");
10
+ const hooks_client_1 = require("./clients/hooks-client");
10
11
  const inbox_client_1 = require("./clients/inbox-client");
11
12
  const reactions_client_1 = require("./clients/reactions-client");
12
13
  const search_client_1 = require("./clients/search-client");
@@ -53,6 +54,7 @@ class CommsApi {
53
54
  this.reactions = new reactions_client_1.ReactionsClient(clientConfig);
54
55
  this.search = new search_client_1.SearchClient(clientConfig);
55
56
  this.attachments = new attachments_client_1.AttachmentsClient(clientConfig);
57
+ this.hooks = new hooks_client_1.HooksClient(clientConfig);
56
58
  }
57
59
  /**
58
60
  * Drains the SDK's process-global connection pool. CLIs and scripts
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ENDPOINT_ATTACHMENTS = exports.ENDPOINT_CONVERSATION_MESSAGES = exports.ENDPOINT_SEARCH = exports.ENDPOINT_REACTIONS = exports.ENDPOINT_INBOX = exports.ENDPOINT_NOTIFICATIONS = exports.ENDPOINT_COMMENTS = exports.ENDPOINT_CONVERSATIONS = exports.ENDPOINT_GROUPS = exports.ENDPOINT_THREADS = exports.ENDPOINT_CHANNELS = exports.ENDPOINT_WORKSPACES = exports.ENDPOINT_USERS = void 0;
3
+ exports.ENDPOINT_HOOKS = exports.ENDPOINT_ATTACHMENTS = exports.ENDPOINT_CONVERSATION_MESSAGES = exports.ENDPOINT_SEARCH = exports.ENDPOINT_REACTIONS = exports.ENDPOINT_INBOX = exports.ENDPOINT_NOTIFICATIONS = exports.ENDPOINT_COMMENTS = exports.ENDPOINT_CONVERSATIONS = exports.ENDPOINT_GROUPS = exports.ENDPOINT_THREADS = exports.ENDPOINT_CHANNELS = exports.ENDPOINT_WORKSPACES = exports.ENDPOINT_USERS = void 0;
4
4
  exports.getCommsBaseUri = getCommsBaseUri;
5
5
  const api_version_1 = require("../types/api-version");
6
6
  const BASE_URI = 'https://comms.todoist.com';
@@ -31,3 +31,4 @@ exports.ENDPOINT_REACTIONS = 'reactions';
31
31
  exports.ENDPOINT_SEARCH = 'search';
32
32
  exports.ENDPOINT_CONVERSATION_MESSAGES = 'conversation_messages';
33
33
  exports.ENDPOINT_ATTACHMENTS = 'attachments';
34
+ exports.ENDPOINT_HOOKS = 'hooks';
package/dist/cjs/index.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.closeDefaultDispatcher = exports.CommsApi = exports.WorkspacesClient = exports.WorkspaceUsersClient = exports.UsersClient = exports.ThreadsClient = exports.SearchClient = exports.ReactionsClient = exports.InboxClient = exports.GroupsClient = exports.ConversationsClient = exports.ConversationMessagesClient = exports.CommentsClient = exports.ChannelsClient = exports.AttachmentsClient = void 0;
17
+ exports.closeDefaultDispatcher = exports.CommsApi = exports.WorkspacesClient = exports.WorkspaceUsersClient = exports.UsersClient = exports.ThreadsClient = exports.SearchClient = exports.ReactionsClient = exports.InboxClient = exports.HooksClient = exports.GroupsClient = exports.ConversationsClient = exports.ConversationMessagesClient = exports.CommentsClient = exports.ChannelsClient = exports.AttachmentsClient = void 0;
18
18
  __exportStar(require("./authentication"), exports);
19
19
  var attachments_client_1 = require("./clients/attachments-client");
20
20
  Object.defineProperty(exports, "AttachmentsClient", { enumerable: true, get: function () { return attachments_client_1.AttachmentsClient; } });
@@ -28,6 +28,8 @@ var conversations_client_1 = require("./clients/conversations-client");
28
28
  Object.defineProperty(exports, "ConversationsClient", { enumerable: true, get: function () { return conversations_client_1.ConversationsClient; } });
29
29
  var groups_client_1 = require("./clients/groups-client");
30
30
  Object.defineProperty(exports, "GroupsClient", { enumerable: true, get: function () { return groups_client_1.GroupsClient; } });
31
+ var hooks_client_1 = require("./clients/hooks-client");
32
+ Object.defineProperty(exports, "HooksClient", { enumerable: true, get: function () { return hooks_client_1.HooksClient; } });
31
33
  var inbox_client_1 = require("./clients/inbox-client");
32
34
  Object.defineProperty(exports, "InboxClient", { enumerable: true, get: function () { return inbox_client_1.InboxClient; } });
33
35
  var reactions_client_1 = require("./clients/reactions-client");
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SearchResultSchema = exports.SEARCH_RESULT_TYPES = exports.UnreadConversationSchema = exports.UnreadThreadSchema = exports.InboxThreadSchema = exports.ConversationMessageSchema = exports.ConversationMessageObjectSchema = exports.WorkspaceUserSchema = exports.CommentSchema = exports.CommentObjectSchema = exports.ConversationSchema = exports.ConversationObjectSchema = exports.GroupSchema = exports.ThreadSchema = exports.ThreadObjectSchema = exports.ChannelSchema = exports.ChannelObjectSchema = exports.WorkspaceSchema = exports.UserSchema = exports.BaseUserSchema = exports.RequestAttachmentSchema = exports.AttachmentSchema = exports.StatusOkSchema = exports.SystemMessageSchema = void 0;
3
+ exports.SearchResultSchema = exports.SEARCH_RESULT_TYPES = exports.UnreadConversationSchema = exports.UnreadThreadSchema = exports.InboxThreadSchema = exports.ConversationMessageSchema = exports.ConversationMessageObjectSchema = exports.WorkspaceUserSchema = exports.CommentSchema = exports.CommentObjectSchema = exports.ConversationSchema = exports.ConversationObjectSchema = exports.GroupSchema = exports.ThreadSchema = exports.ThreadObjectSchema = exports.ChannelSchema = exports.ChannelObjectSchema = exports.WorkspaceSchema = exports.UserSchema = exports.BaseUserSchema = exports.RequestAttachmentSchema = exports.AttachmentSchema = exports.HookSubscribeResponseSchema = exports.StatusOkSchema = exports.SystemMessageSchema = void 0;
4
4
  exports.createChannelSchema = createChannelSchema;
5
5
  exports.createThreadSchema = createThreadSchema;
6
6
  exports.createConversationSchema = createConversationSchema;
@@ -27,6 +27,9 @@ exports.SystemMessageSchema = zod_1.z.union([zod_1.z.string(), zod_1.z.unknown()
27
27
  * mark-read / mark-all-read / mute / clear-unread / etc.
28
28
  */
29
29
  exports.StatusOkSchema = zod_1.z.object({ status: zod_1.z.literal('ok') });
30
+ exports.HookSubscribeResponseSchema = exports.StatusOkSchema.extend({
31
+ id: zod_1.z.number().int(),
32
+ });
30
33
  // Attachment entity from API. Mirrors the canonical backend shape produced
31
34
  // by `unify_attachments` / `validate_file_attachment_json`. Only
32
35
  // `attachmentId` and `urlType` are guaranteed; everything else depends on
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.THREAD_ACTIONS = exports.ARCHIVE_FILTER_VALUES = exports.GetOrCreateConversationArgsSchema = exports.GetConversationsArgsSchema = exports.GetCommentsArgsSchema = exports.GetThreadsArgsSchema = exports.GetChannelsArgsSchema = exports.CreateMessageArgsSchema = exports.CreateConversationArgsSchema = exports.UpdateCommentArgsSchema = exports.CreateCommentArgsSchema = exports.UpdateThreadArgsSchema = exports.CreateThreadArgsSchema = exports.UpdateChannelArgsSchema = exports.CreateChannelArgsSchema = void 0;
3
+ exports.THREAD_ACTIONS = exports.UnsubscribeHookArgsSchema = exports.SubscribeHookArgsSchema = exports.SubscribeMessageHookArgsSchema = exports.SubscribeNonMessageHookArgsSchema = exports.SubscribeHookArgsBaseSchema = exports.HookTargetUrlSchema = exports.HookEventSchema = exports.MessageHookEventSchema = exports.NonMessageHookEventSchema = exports.HOOK_EVENTS = exports.MESSAGE_HOOK_EVENTS = exports.NON_MESSAGE_HOOK_EVENTS = exports.ARCHIVE_FILTER_VALUES = exports.GetOrCreateConversationArgsSchema = exports.GetConversationsArgsSchema = exports.GetCommentsArgsSchema = exports.GetThreadsArgsSchema = exports.GetChannelsArgsSchema = exports.CreateMessageArgsSchema = exports.CreateConversationArgsSchema = exports.UpdateCommentArgsSchema = exports.CreateCommentArgsSchema = exports.UpdateThreadArgsSchema = exports.CreateThreadArgsSchema = exports.UpdateChannelArgsSchema = exports.CreateChannelArgsSchema = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const entities_1 = require("./entities");
6
6
  const enums_1 = require("./enums");
@@ -113,5 +113,67 @@ exports.GetOrCreateConversationArgsSchema = zod_1.z.object({
113
113
  });
114
114
  // Inbox
115
115
  exports.ARCHIVE_FILTER_VALUES = ['active', 'archived', 'all'];
116
+ // Hooks
117
+ exports.NON_MESSAGE_HOOK_EVENTS = [
118
+ 'workspace_added',
119
+ 'workspace_updated',
120
+ 'workspace_deleted',
121
+ 'workspace_user_added',
122
+ 'workspace_user_updated',
123
+ 'workspace_user_removed',
124
+ 'channel_added',
125
+ 'channel_updated',
126
+ 'channel_deleted',
127
+ 'channel_user_added',
128
+ 'channel_user_removed',
129
+ 'thread_added',
130
+ 'thread_updated',
131
+ 'thread_deleted',
132
+ 'comment_added',
133
+ 'comment_updated',
134
+ 'comment_deleted',
135
+ 'group_added',
136
+ 'group_updated',
137
+ 'group_deleted',
138
+ 'group_user_added',
139
+ 'group_user_removed',
140
+ ];
141
+ exports.MESSAGE_HOOK_EVENTS = ['message_added', 'message_updated'];
142
+ exports.HOOK_EVENTS = [...exports.NON_MESSAGE_HOOK_EVENTS, ...exports.MESSAGE_HOOK_EVENTS];
143
+ exports.NonMessageHookEventSchema = zod_1.z.enum(exports.NON_MESSAGE_HOOK_EVENTS);
144
+ exports.MessageHookEventSchema = zod_1.z.enum(exports.MESSAGE_HOOK_EVENTS);
145
+ exports.HookEventSchema = zod_1.z.enum(exports.HOOK_EVENTS);
146
+ exports.HookTargetUrlSchema = zod_1.z
147
+ .url()
148
+ .max(150)
149
+ .refine((value) => {
150
+ try {
151
+ return new URL(value).protocol === 'https:';
152
+ }
153
+ catch {
154
+ return false;
155
+ }
156
+ }, { message: 'URL must use HTTPS' });
157
+ exports.SubscribeHookArgsBaseSchema = zod_1.z.object({
158
+ targetUrl: exports.HookTargetUrlSchema,
159
+ workspaceId: zod_1.z.number().nullable().optional(),
160
+ channelId: zod_1.z.string().nullable().optional(),
161
+ threadId: zod_1.z.string().nullable().optional(),
162
+ });
163
+ exports.SubscribeNonMessageHookArgsSchema = exports.SubscribeHookArgsBaseSchema.extend({
164
+ event: exports.NonMessageHookEventSchema,
165
+ conversationId: zod_1.z.never().optional(),
166
+ });
167
+ exports.SubscribeMessageHookArgsSchema = exports.SubscribeHookArgsBaseSchema.extend({
168
+ event: exports.MessageHookEventSchema,
169
+ conversationId: zod_1.z.string().nullable().optional(),
170
+ });
171
+ exports.SubscribeHookArgsSchema = zod_1.z.union([
172
+ exports.SubscribeNonMessageHookArgsSchema,
173
+ exports.SubscribeMessageHookArgsSchema,
174
+ ]);
175
+ exports.UnsubscribeHookArgsSchema = zod_1.z.object({
176
+ targetUrl: exports.HookTargetUrlSchema,
177
+ });
116
178
  // Threads
117
179
  exports.THREAD_ACTIONS = ['close', 'reopen'];
@@ -0,0 +1,48 @@
1
+ import { ENDPOINT_HOOKS } from '../consts/endpoints.js';
2
+ import { request } from '../transport/http-client.js';
3
+ import { HookSubscribeResponseSchema, StatusOkSchema, } from '../types/entities.js';
4
+ import { BaseClient } from './base-client.js';
5
+ /**
6
+ * Client for `/api/v1/hooks/`. REST hooks require Todoist OAuth bearer tokens
7
+ * that include Comms audience; session tokens are rejected by the backend.
8
+ */
9
+ export class HooksClient extends BaseClient {
10
+ /**
11
+ * Subscribes the current OAuth app to a REST hook event.
12
+ *
13
+ * @param args - The hook subscription parameters.
14
+ * @param args.targetUrl - HTTPS endpoint, up to 150 characters, that will receive webhook POSTs.
15
+ * @param args.event - Event name to subscribe to.
16
+ * @param args.workspaceId - Optional workspace filter.
17
+ * @param args.channelId - Optional channel filter.
18
+ * @param args.threadId - Optional thread filter.
19
+ * @param args.conversationId - Optional conversation filter for message events.
20
+ * @returns The `{ status: 'ok', id }` response for the subscription.
21
+ */
22
+ subscribe(args) {
23
+ return request({
24
+ httpMethod: 'POST',
25
+ baseUri: this.getBaseUri(),
26
+ relativePath: `${ENDPOINT_HOOKS}/subscribe`,
27
+ apiToken: this.apiToken,
28
+ payload: args,
29
+ customFetch: this.customFetch,
30
+ }).then((response) => HookSubscribeResponseSchema.parse(response.data));
31
+ }
32
+ /**
33
+ * Unsubscribes all hooks with the target URL for the current OAuth app.
34
+ *
35
+ * @param args - The hook unsubscribe parameters.
36
+ * @param args.targetUrl - Target URL to remove.
37
+ */
38
+ unsubscribe(args) {
39
+ return request({
40
+ httpMethod: 'POST',
41
+ baseUri: this.getBaseUri(),
42
+ relativePath: `${ENDPOINT_HOOKS}/unsubscribe`,
43
+ apiToken: this.apiToken,
44
+ payload: args,
45
+ customFetch: this.customFetch,
46
+ }).then((response) => StatusOkSchema.parse(response.data));
47
+ }
48
+ }
@@ -4,6 +4,7 @@ import { CommentsClient } from './clients/comments-client.js';
4
4
  import { ConversationMessagesClient } from './clients/conversation-messages-client.js';
5
5
  import { ConversationsClient } from './clients/conversations-client.js';
6
6
  import { GroupsClient } from './clients/groups-client.js';
7
+ import { HooksClient } from './clients/hooks-client.js';
7
8
  import { InboxClient } from './clients/inbox-client.js';
8
9
  import { ReactionsClient } from './clients/reactions-client.js';
9
10
  import { SearchClient } from './clients/search-client.js';
@@ -50,6 +51,7 @@ export class CommsApi {
50
51
  this.reactions = new ReactionsClient(clientConfig);
51
52
  this.search = new SearchClient(clientConfig);
52
53
  this.attachments = new AttachmentsClient(clientConfig);
54
+ this.hooks = new HooksClient(clientConfig);
53
55
  }
54
56
  /**
55
57
  * Drains the SDK's process-global connection pool. CLIs and scripts
@@ -27,3 +27,4 @@ export const ENDPOINT_REACTIONS = 'reactions';
27
27
  export const ENDPOINT_SEARCH = 'search';
28
28
  export const ENDPOINT_CONVERSATION_MESSAGES = 'conversation_messages';
29
29
  export const ENDPOINT_ATTACHMENTS = 'attachments';
30
+ export const ENDPOINT_HOOKS = 'hooks';
package/dist/esm/index.js CHANGED
@@ -5,6 +5,7 @@ export { CommentsClient } from './clients/comments-client.js';
5
5
  export { ConversationMessagesClient } from './clients/conversation-messages-client.js';
6
6
  export { ConversationsClient } from './clients/conversations-client.js';
7
7
  export { GroupsClient } from './clients/groups-client.js';
8
+ export { HooksClient } from './clients/hooks-client.js';
8
9
  export { InboxClient } from './clients/inbox-client.js';
9
10
  export { ReactionsClient } from './clients/reactions-client.js';
10
11
  export { SearchClient } from './clients/search-client.js';
@@ -17,6 +17,9 @@ export const SystemMessageSchema = z.union([z.string(), z.unknown()]).nullable()
17
17
  * mark-read / mark-all-read / mute / clear-unread / etc.
18
18
  */
19
19
  export const StatusOkSchema = z.object({ status: z.literal('ok') });
20
+ export const HookSubscribeResponseSchema = StatusOkSchema.extend({
21
+ id: z.number().int(),
22
+ });
20
23
  // Attachment entity from API. Mirrors the canonical backend shape produced
21
24
  // by `unify_attachments` / `validate_file_attachment_json`. Only
22
25
  // `attachmentId` and `urlType` are guaranteed; everything else depends on
@@ -110,5 +110,67 @@ export const GetOrCreateConversationArgsSchema = z.object({
110
110
  });
111
111
  // Inbox
112
112
  export const ARCHIVE_FILTER_VALUES = ['active', 'archived', 'all'];
113
+ // Hooks
114
+ export const NON_MESSAGE_HOOK_EVENTS = [
115
+ 'workspace_added',
116
+ 'workspace_updated',
117
+ 'workspace_deleted',
118
+ 'workspace_user_added',
119
+ 'workspace_user_updated',
120
+ 'workspace_user_removed',
121
+ 'channel_added',
122
+ 'channel_updated',
123
+ 'channel_deleted',
124
+ 'channel_user_added',
125
+ 'channel_user_removed',
126
+ 'thread_added',
127
+ 'thread_updated',
128
+ 'thread_deleted',
129
+ 'comment_added',
130
+ 'comment_updated',
131
+ 'comment_deleted',
132
+ 'group_added',
133
+ 'group_updated',
134
+ 'group_deleted',
135
+ 'group_user_added',
136
+ 'group_user_removed',
137
+ ];
138
+ export const MESSAGE_HOOK_EVENTS = ['message_added', 'message_updated'];
139
+ export const HOOK_EVENTS = [...NON_MESSAGE_HOOK_EVENTS, ...MESSAGE_HOOK_EVENTS];
140
+ export const NonMessageHookEventSchema = z.enum(NON_MESSAGE_HOOK_EVENTS);
141
+ export const MessageHookEventSchema = z.enum(MESSAGE_HOOK_EVENTS);
142
+ export const HookEventSchema = z.enum(HOOK_EVENTS);
143
+ export const HookTargetUrlSchema = z
144
+ .url()
145
+ .max(150)
146
+ .refine((value) => {
147
+ try {
148
+ return new URL(value).protocol === 'https:';
149
+ }
150
+ catch {
151
+ return false;
152
+ }
153
+ }, { message: 'URL must use HTTPS' });
154
+ export const SubscribeHookArgsBaseSchema = z.object({
155
+ targetUrl: HookTargetUrlSchema,
156
+ workspaceId: z.number().nullable().optional(),
157
+ channelId: z.string().nullable().optional(),
158
+ threadId: z.string().nullable().optional(),
159
+ });
160
+ export const SubscribeNonMessageHookArgsSchema = SubscribeHookArgsBaseSchema.extend({
161
+ event: NonMessageHookEventSchema,
162
+ conversationId: z.never().optional(),
163
+ });
164
+ export const SubscribeMessageHookArgsSchema = SubscribeHookArgsBaseSchema.extend({
165
+ event: MessageHookEventSchema,
166
+ conversationId: z.string().nullable().optional(),
167
+ });
168
+ export const SubscribeHookArgsSchema = z.union([
169
+ SubscribeNonMessageHookArgsSchema,
170
+ SubscribeMessageHookArgsSchema,
171
+ ]);
172
+ export const UnsubscribeHookArgsSchema = z.object({
173
+ targetUrl: HookTargetUrlSchema,
174
+ });
113
175
  // Threads
114
176
  export const THREAD_ACTIONS = ['close', 'reopen'];
@@ -0,0 +1,29 @@
1
+ import { type HookSubscribeResponse, type StatusOk } from '../types/entities.js';
2
+ import type { SubscribeHookArgs, UnsubscribeHookArgs } from '../types/requests.js';
3
+ import { BaseClient } from './base-client.js';
4
+ /**
5
+ * Client for `/api/v1/hooks/`. REST hooks require Todoist OAuth bearer tokens
6
+ * that include Comms audience; session tokens are rejected by the backend.
7
+ */
8
+ export declare class HooksClient extends BaseClient {
9
+ /**
10
+ * Subscribes the current OAuth app to a REST hook event.
11
+ *
12
+ * @param args - The hook subscription parameters.
13
+ * @param args.targetUrl - HTTPS endpoint, up to 150 characters, that will receive webhook POSTs.
14
+ * @param args.event - Event name to subscribe to.
15
+ * @param args.workspaceId - Optional workspace filter.
16
+ * @param args.channelId - Optional channel filter.
17
+ * @param args.threadId - Optional thread filter.
18
+ * @param args.conversationId - Optional conversation filter for message events.
19
+ * @returns The `{ status: 'ok', id }` response for the subscription.
20
+ */
21
+ subscribe(args: SubscribeHookArgs): Promise<HookSubscribeResponse>;
22
+ /**
23
+ * Unsubscribes all hooks with the target URL for the current OAuth app.
24
+ *
25
+ * @param args - The hook unsubscribe parameters.
26
+ * @param args.targetUrl - Target URL to remove.
27
+ */
28
+ unsubscribe(args: UnsubscribeHookArgs): Promise<StatusOk>;
29
+ }
@@ -4,6 +4,7 @@ import { CommentsClient } from './clients/comments-client.js';
4
4
  import { ConversationMessagesClient } from './clients/conversation-messages-client.js';
5
5
  import { ConversationsClient } from './clients/conversations-client.js';
6
6
  import { GroupsClient } from './clients/groups-client.js';
7
+ import { HooksClient } from './clients/hooks-client.js';
7
8
  import { InboxClient } from './clients/inbox-client.js';
8
9
  import { ReactionsClient } from './clients/reactions-client.js';
9
10
  import { SearchClient } from './clients/search-client.js';
@@ -46,6 +47,7 @@ export declare class CommsApi {
46
47
  reactions: ReactionsClient;
47
48
  search: SearchClient;
48
49
  attachments: AttachmentsClient;
50
+ hooks: HooksClient;
49
51
  /**
50
52
  * Creates a new Comms API client.
51
53
  *
@@ -23,3 +23,4 @@ export declare const ENDPOINT_REACTIONS = "reactions";
23
23
  export declare const ENDPOINT_SEARCH = "search";
24
24
  export declare const ENDPOINT_CONVERSATION_MESSAGES = "conversation_messages";
25
25
  export declare const ENDPOINT_ATTACHMENTS = "attachments";
26
+ export declare const ENDPOINT_HOOKS = "hooks";
@@ -5,6 +5,7 @@ export { CommentsClient } from './clients/comments-client.js';
5
5
  export { ConversationMessagesClient } from './clients/conversation-messages-client.js';
6
6
  export { ConversationsClient } from './clients/conversations-client.js';
7
7
  export { GroupsClient } from './clients/groups-client.js';
8
+ export { HooksClient } from './clients/hooks-client.js';
8
9
  export { InboxClient } from './clients/inbox-client.js';
9
10
  export { ReactionsClient } from './clients/reactions-client.js';
10
11
  export { SearchClient } from './clients/search-client.js';
@@ -11,6 +11,11 @@ export declare const StatusOkSchema: z.ZodObject<{
11
11
  status: z.ZodLiteral<"ok">;
12
12
  }, z.core.$strip>;
13
13
  export type StatusOk = z.infer<typeof StatusOkSchema>;
14
+ export declare const HookSubscribeResponseSchema: z.ZodObject<{
15
+ status: z.ZodLiteral<"ok">;
16
+ id: z.ZodNumber;
17
+ }, z.core.$strip>;
18
+ export type HookSubscribeResponse = z.infer<typeof HookSubscribeResponseSchema>;
14
19
  export declare const AttachmentSchema: z.ZodObject<{
15
20
  attachmentId: z.ZodString;
16
21
  urlType: z.ZodString;
@@ -264,6 +264,161 @@ export type GetReactionsArgs = {
264
264
  commentId?: string;
265
265
  messageId?: string;
266
266
  };
267
+ export declare const NON_MESSAGE_HOOK_EVENTS: readonly ["workspace_added", "workspace_updated", "workspace_deleted", "workspace_user_added", "workspace_user_updated", "workspace_user_removed", "channel_added", "channel_updated", "channel_deleted", "channel_user_added", "channel_user_removed", "thread_added", "thread_updated", "thread_deleted", "comment_added", "comment_updated", "comment_deleted", "group_added", "group_updated", "group_deleted", "group_user_added", "group_user_removed"];
268
+ export type NonMessageHookEvent = (typeof NON_MESSAGE_HOOK_EVENTS)[number];
269
+ export declare const MESSAGE_HOOK_EVENTS: readonly ["message_added", "message_updated"];
270
+ export type MessageHookEvent = (typeof MESSAGE_HOOK_EVENTS)[number];
271
+ export declare const HOOK_EVENTS: readonly ["workspace_added", "workspace_updated", "workspace_deleted", "workspace_user_added", "workspace_user_updated", "workspace_user_removed", "channel_added", "channel_updated", "channel_deleted", "channel_user_added", "channel_user_removed", "thread_added", "thread_updated", "thread_deleted", "comment_added", "comment_updated", "comment_deleted", "group_added", "group_updated", "group_deleted", "group_user_added", "group_user_removed", "message_added", "message_updated"];
272
+ export type HookEvent = (typeof HOOK_EVENTS)[number];
273
+ export declare const NonMessageHookEventSchema: z.ZodEnum<{
274
+ workspace_added: "workspace_added";
275
+ workspace_updated: "workspace_updated";
276
+ workspace_deleted: "workspace_deleted";
277
+ workspace_user_added: "workspace_user_added";
278
+ workspace_user_updated: "workspace_user_updated";
279
+ workspace_user_removed: "workspace_user_removed";
280
+ channel_added: "channel_added";
281
+ channel_updated: "channel_updated";
282
+ channel_deleted: "channel_deleted";
283
+ channel_user_added: "channel_user_added";
284
+ channel_user_removed: "channel_user_removed";
285
+ thread_added: "thread_added";
286
+ thread_updated: "thread_updated";
287
+ thread_deleted: "thread_deleted";
288
+ comment_added: "comment_added";
289
+ comment_updated: "comment_updated";
290
+ comment_deleted: "comment_deleted";
291
+ group_added: "group_added";
292
+ group_updated: "group_updated";
293
+ group_deleted: "group_deleted";
294
+ group_user_added: "group_user_added";
295
+ group_user_removed: "group_user_removed";
296
+ }>;
297
+ export declare const MessageHookEventSchema: z.ZodEnum<{
298
+ message_added: "message_added";
299
+ message_updated: "message_updated";
300
+ }>;
301
+ export declare const HookEventSchema: z.ZodEnum<{
302
+ workspace_added: "workspace_added";
303
+ workspace_updated: "workspace_updated";
304
+ workspace_deleted: "workspace_deleted";
305
+ workspace_user_added: "workspace_user_added";
306
+ workspace_user_updated: "workspace_user_updated";
307
+ workspace_user_removed: "workspace_user_removed";
308
+ channel_added: "channel_added";
309
+ channel_updated: "channel_updated";
310
+ channel_deleted: "channel_deleted";
311
+ channel_user_added: "channel_user_added";
312
+ channel_user_removed: "channel_user_removed";
313
+ thread_added: "thread_added";
314
+ thread_updated: "thread_updated";
315
+ thread_deleted: "thread_deleted";
316
+ comment_added: "comment_added";
317
+ comment_updated: "comment_updated";
318
+ comment_deleted: "comment_deleted";
319
+ group_added: "group_added";
320
+ group_updated: "group_updated";
321
+ group_deleted: "group_deleted";
322
+ group_user_added: "group_user_added";
323
+ group_user_removed: "group_user_removed";
324
+ message_added: "message_added";
325
+ message_updated: "message_updated";
326
+ }>;
327
+ export declare const HookTargetUrlSchema: z.ZodURL;
328
+ export declare const SubscribeHookArgsBaseSchema: z.ZodObject<{
329
+ targetUrl: z.ZodURL;
330
+ workspaceId: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
331
+ channelId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
332
+ threadId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
333
+ }, z.core.$strip>;
334
+ export declare const SubscribeNonMessageHookArgsSchema: z.ZodObject<{
335
+ targetUrl: z.ZodURL;
336
+ workspaceId: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
337
+ channelId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
338
+ threadId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
339
+ event: z.ZodEnum<{
340
+ workspace_added: "workspace_added";
341
+ workspace_updated: "workspace_updated";
342
+ workspace_deleted: "workspace_deleted";
343
+ workspace_user_added: "workspace_user_added";
344
+ workspace_user_updated: "workspace_user_updated";
345
+ workspace_user_removed: "workspace_user_removed";
346
+ channel_added: "channel_added";
347
+ channel_updated: "channel_updated";
348
+ channel_deleted: "channel_deleted";
349
+ channel_user_added: "channel_user_added";
350
+ channel_user_removed: "channel_user_removed";
351
+ thread_added: "thread_added";
352
+ thread_updated: "thread_updated";
353
+ thread_deleted: "thread_deleted";
354
+ comment_added: "comment_added";
355
+ comment_updated: "comment_updated";
356
+ comment_deleted: "comment_deleted";
357
+ group_added: "group_added";
358
+ group_updated: "group_updated";
359
+ group_deleted: "group_deleted";
360
+ group_user_added: "group_user_added";
361
+ group_user_removed: "group_user_removed";
362
+ }>;
363
+ conversationId: z.ZodOptional<z.ZodNever>;
364
+ }, z.core.$strip>;
365
+ export declare const SubscribeMessageHookArgsSchema: z.ZodObject<{
366
+ targetUrl: z.ZodURL;
367
+ workspaceId: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
368
+ channelId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
369
+ threadId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
370
+ event: z.ZodEnum<{
371
+ message_added: "message_added";
372
+ message_updated: "message_updated";
373
+ }>;
374
+ conversationId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
375
+ }, z.core.$strip>;
376
+ export declare const SubscribeHookArgsSchema: z.ZodUnion<readonly [z.ZodObject<{
377
+ targetUrl: z.ZodURL;
378
+ workspaceId: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
379
+ channelId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
380
+ threadId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
381
+ event: z.ZodEnum<{
382
+ workspace_added: "workspace_added";
383
+ workspace_updated: "workspace_updated";
384
+ workspace_deleted: "workspace_deleted";
385
+ workspace_user_added: "workspace_user_added";
386
+ workspace_user_updated: "workspace_user_updated";
387
+ workspace_user_removed: "workspace_user_removed";
388
+ channel_added: "channel_added";
389
+ channel_updated: "channel_updated";
390
+ channel_deleted: "channel_deleted";
391
+ channel_user_added: "channel_user_added";
392
+ channel_user_removed: "channel_user_removed";
393
+ thread_added: "thread_added";
394
+ thread_updated: "thread_updated";
395
+ thread_deleted: "thread_deleted";
396
+ comment_added: "comment_added";
397
+ comment_updated: "comment_updated";
398
+ comment_deleted: "comment_deleted";
399
+ group_added: "group_added";
400
+ group_updated: "group_updated";
401
+ group_deleted: "group_deleted";
402
+ group_user_added: "group_user_added";
403
+ group_user_removed: "group_user_removed";
404
+ }>;
405
+ conversationId: z.ZodOptional<z.ZodNever>;
406
+ }, z.core.$strip>, z.ZodObject<{
407
+ targetUrl: z.ZodURL;
408
+ workspaceId: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
409
+ channelId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
410
+ threadId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
411
+ event: z.ZodEnum<{
412
+ message_added: "message_added";
413
+ message_updated: "message_updated";
414
+ }>;
415
+ conversationId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
416
+ }, z.core.$strip>]>;
417
+ export type SubscribeHookArgs = z.infer<typeof SubscribeHookArgsSchema>;
418
+ export declare const UnsubscribeHookArgsSchema: z.ZodObject<{
419
+ targetUrl: z.ZodURL;
420
+ }, z.core.$strip>;
421
+ export type UnsubscribeHookArgs = z.infer<typeof UnsubscribeHookArgsSchema>;
267
422
  export type AddChannelUserArgs = {
268
423
  id: string;
269
424
  userId: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doist/comms-sdk",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "A TypeScript wrapper for the Comms REST API.",
5
5
  "author": "Doist developers",
6
6
  "homepage": "https://doist.github.io/comms-sdk-typescript/",