From 49767a03c8f35e1ad728a5e7a0c6563d96f87c7b Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Fri, 16 Oct 2026 11:29:59 -0400 Subject: [PATCH 1/2] feat(types): add the user_connection_status_requested event Slack sends this event when it has no connection status for a user, so apps answer with apps.user.connection.update. Adding it to SlackEvent types the event in Bolt listeners. --- .changeset/user-connection-status-requested-event.md | 5 +++++ packages/types/src/events/index.ts | 9 ++++++++- packages/types/src/events/user.ts | 5 +++++ packages/types/test/events/user.test-d.ts | 12 ++++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 .changeset/user-connection-status-requested-event.md create mode 100644 packages/types/test/events/user.test-d.ts diff --git a/.changeset/user-connection-status-requested-event.md b/.changeset/user-connection-status-requested-event.md new file mode 100644 index 000000000..f368bb275 --- /dev/null +++ b/.changeset/user-connection-status-requested-event.md @@ -0,0 +1,5 @@ +--- +"@slack/types": minor +--- + +feat(types): add the [`user_connection_status_requested`](https://docs.slack.dev/reference/events/user_connection_status_requested) event interface diff --git a/packages/types/src/events/index.ts b/packages/types/src/events/index.ts index a3dddebe2..ff93ff535 100644 --- a/packages/types/src/events/index.ts +++ b/packages/types/src/events/index.ts @@ -87,7 +87,13 @@ import type { TeamRenameEvent, } from './team'; import type { TokensRevokedEvent } from './token'; -import type { UserChangeEvent, UserHuddleChangedEvent, UserProfileChangedEvent, UserStatusChangedEvent } from './user'; +import type { + UserChangeEvent, + UserConnectionStatusRequestedEvent, + UserHuddleChangedEvent, + UserProfileChangedEvent, + UserStatusChangedEvent, +} from './user'; export * from './agent'; export * from './app'; @@ -205,6 +211,7 @@ export type SlackEvent = // TODO: url_verification event is missing, but maybe we don't need it? // NOTE: url_verification does not use the envelope, but it's also not interesting for an app developer. it's omitted. | UserChangeEvent + | UserConnectionStatusRequestedEvent | UserHuddleChangedEvent | UserProfileChangedEvent | UserStatusChangedEvent diff --git a/packages/types/src/events/user.ts b/packages/types/src/events/user.ts index 3c5b61f2f..a45c97c45 100644 --- a/packages/types/src/events/user.ts +++ b/packages/types/src/events/user.ts @@ -82,6 +82,11 @@ export interface UserChangeEvent { event_ts: string; } +export interface UserConnectionStatusRequestedEvent { + type: 'user_connection_status_requested'; + user: string; +} + export interface UserHuddleChangedEvent { type: 'user_huddle_changed'; user: { diff --git a/packages/types/test/events/user.test-d.ts b/packages/types/test/events/user.test-d.ts new file mode 100644 index 000000000..d1a1f0157 --- /dev/null +++ b/packages/types/test/events/user.test-d.ts @@ -0,0 +1,12 @@ +import { expectAssignable, expectError } from 'tsd'; +import type { SlackEvent, UserConnectionStatusRequestedEvent } from '../../src/index'; + +const statusRequested: UserConnectionStatusRequestedEvent = { + type: 'user_connection_status_requested', + user: 'U0123456789', +}; +expectAssignable(statusRequested); + +expectError({ + type: 'user_connection_status_requested', +}); From bfe2711b7eb2fbdf00d4cbbd80c68c8d868f1ce3 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Thu, 22 Oct 2026 16:20:33 -0400 Subject: [PATCH 2/2] feat(types): add the user_connection event Adds UserConnectionEvent, a union of the connect and disconnect subtypes, to SlackEvent. Checking subtype === 'connect' narrows trigger_id to a string, so Bolt listeners can open the connection modal without a cast. Also adds event_ts to UserConnectionStatusRequestedEvent, which every payload carries. Co-Authored-By: Claude --- .../user-connection-status-requested-event.md | 2 +- packages/types/src/events/index.ts | 2 ++ packages/types/src/events/user.ts | 19 ++++++++++ packages/types/test/events/user.test-d.ts | 35 +++++++++++++++++-- 4 files changed, 55 insertions(+), 3 deletions(-) diff --git a/.changeset/user-connection-status-requested-event.md b/.changeset/user-connection-status-requested-event.md index f368bb275..d442e1033 100644 --- a/.changeset/user-connection-status-requested-event.md +++ b/.changeset/user-connection-status-requested-event.md @@ -2,4 +2,4 @@ "@slack/types": minor --- -feat(types): add the [`user_connection_status_requested`](https://docs.slack.dev/reference/events/user_connection_status_requested) event interface +feat(types): add the [`user_connection`](https://docs.slack.dev/reference/events/user_connection) and [`user_connection_status_requested`](https://docs.slack.dev/reference/events/user_connection_status_requested) event interfaces diff --git a/packages/types/src/events/index.ts b/packages/types/src/events/index.ts index ff93ff535..1810f5402 100644 --- a/packages/types/src/events/index.ts +++ b/packages/types/src/events/index.ts @@ -89,6 +89,7 @@ import type { import type { TokensRevokedEvent } from './token'; import type { UserChangeEvent, + UserConnectionEvent, UserConnectionStatusRequestedEvent, UserHuddleChangedEvent, UserProfileChangedEvent, @@ -211,6 +212,7 @@ export type SlackEvent = // TODO: url_verification event is missing, but maybe we don't need it? // NOTE: url_verification does not use the envelope, but it's also not interesting for an app developer. it's omitted. | UserChangeEvent + | UserConnectionEvent | UserConnectionStatusRequestedEvent | UserHuddleChangedEvent | UserProfileChangedEvent diff --git a/packages/types/src/events/user.ts b/packages/types/src/events/user.ts index a45c97c45..ece2d67db 100644 --- a/packages/types/src/events/user.ts +++ b/packages/types/src/events/user.ts @@ -82,9 +82,28 @@ export interface UserChangeEvent { event_ts: string; } +export type UserConnectionEvent = UserConnectionConnectEvent | UserConnectionDisconnectEvent; + +export interface UserConnectionConnectEvent { + type: 'user_connection'; + subtype: 'connect'; + user: string; + trigger_id: string; + event_ts: string; +} + +export interface UserConnectionDisconnectEvent { + type: 'user_connection'; + subtype: 'disconnect'; + user: string; + enterprise_id?: string; + event_ts: string; +} + export interface UserConnectionStatusRequestedEvent { type: 'user_connection_status_requested'; user: string; + event_ts: string; } export interface UserHuddleChangedEvent { diff --git a/packages/types/test/events/user.test-d.ts b/packages/types/test/events/user.test-d.ts index d1a1f0157..d730cb5f4 100644 --- a/packages/types/test/events/user.test-d.ts +++ b/packages/types/test/events/user.test-d.ts @@ -1,9 +1,40 @@ -import { expectAssignable, expectError } from 'tsd'; -import type { SlackEvent, UserConnectionStatusRequestedEvent } from '../../src/index'; +import { expectAssignable, expectError, expectType } from 'tsd'; +import type { SlackEvent, UserConnectionEvent, UserConnectionStatusRequestedEvent } from '../../src/index'; + +const connect: UserConnectionEvent = { + type: 'user_connection', + subtype: 'connect', + user: 'U0123456789', + trigger_id: '1293638028594.1249184885746.0d121a0e01d2e7a795ecc7a62880a406', + event_ts: '1764264284.841251', +}; +expectAssignable(connect); + +const disconnect: UserConnectionEvent = { + type: 'user_connection', + subtype: 'disconnect', + enterprise_id: 'E0123456789', + user: 'U0123456789', + event_ts: '1764264317.061589', +}; +expectAssignable(disconnect); + +expectError({ + type: 'user_connection', + subtype: 'connect', + user: 'U0123456789', + event_ts: '1764264284.841251', +}); + +declare const userConnection: UserConnectionEvent; +if (userConnection.subtype === 'connect') { + expectType(userConnection.trigger_id); +} const statusRequested: UserConnectionStatusRequestedEvent = { type: 'user_connection_status_requested', user: 'U0123456789', + event_ts: '1764264284.841251', }; expectAssignable(statusRequested);