Skip to content

Commit ec79025

Browse files
fix(events): Type Pipes connected account events
Pipes events are available through listEvents, but EventName excluded them and returned payloads fell through the untyped serializer path. Aligning the SDK with the API schema removes consumer casts. Addresses #1685
1 parent 5237aa2 commit ec79025

5 files changed

Lines changed: 262 additions & 1 deletion

File tree

‎src/common/interfaces/event.interface.ts‎

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ import {
5757
FeatureFlagResponse,
5858
} from '../../feature-flags/interfaces';
5959
import { Group, GroupResponse } from '../../groups/interfaces';
60+
import { ConnectedAccountState } from '../../pipes/interfaces';
6061
import {
6162
VaultDataCreatedEventData,
6263
VaultDataCreatedEventResponseData,
@@ -817,6 +818,98 @@ export interface GroupMemberRemovedEventResponse extends EventResponseBase {
817818
data: GroupMemberEventResponseData;
818819
}
819820

821+
export interface PipesConnectedAccount {
822+
object: 'connected_account';
823+
id: string;
824+
dataIntegrationId: string;
825+
providerSlug: string;
826+
userId: string | null;
827+
organizationId: string | null;
828+
scopes: string[];
829+
state: ConnectedAccountState;
830+
createdAt: string;
831+
updatedAt: string;
832+
}
833+
834+
export interface PipesConnectedAccountResponse {
835+
object: 'connected_account';
836+
id: string;
837+
data_integration_id: string;
838+
provider_slug: string;
839+
user_id: string | null;
840+
organization_id: string | null;
841+
scopes: string[];
842+
state: ConnectedAccountState;
843+
created_at: string;
844+
updated_at: string;
845+
}
846+
847+
export interface PipesConnectionFailed {
848+
object: 'connection_failed';
849+
dataIntegrationId: string;
850+
providerSlug: string;
851+
userId: string | null;
852+
organizationId: string | null;
853+
errorCode: string;
854+
errorReason: string | null;
855+
providerError: string | null;
856+
providerErrorDescription: string | null;
857+
createdAt: string;
858+
}
859+
860+
export interface PipesConnectionFailedResponse {
861+
object: 'connection_failed';
862+
data_integration_id: string;
863+
provider_slug: string;
864+
user_id: string | null;
865+
organization_id: string | null;
866+
error_code: string;
867+
error_reason: string | null;
868+
provider_error: string | null;
869+
provider_error_description: string | null;
870+
created_at: string;
871+
}
872+
873+
export interface PipesConnectedAccountConnectedEvent extends EventBase {
874+
event: 'pipes.connected_account.connected';
875+
data: PipesConnectedAccount;
876+
}
877+
878+
export interface PipesConnectedAccountConnectedEventResponse extends EventResponseBase {
879+
event: 'pipes.connected_account.connected';
880+
data: PipesConnectedAccountResponse;
881+
}
882+
883+
export interface PipesConnectedAccountConnectionFailedEvent extends EventBase {
884+
event: 'pipes.connected_account.connection_failed';
885+
data: PipesConnectionFailed;
886+
}
887+
888+
export interface PipesConnectedAccountConnectionFailedEventResponse extends EventResponseBase {
889+
event: 'pipes.connected_account.connection_failed';
890+
data: PipesConnectionFailedResponse;
891+
}
892+
893+
export interface PipesConnectedAccountDisconnectedEvent extends EventBase {
894+
event: 'pipes.connected_account.disconnected';
895+
data: PipesConnectedAccount;
896+
}
897+
898+
export interface PipesConnectedAccountDisconnectedEventResponse extends EventResponseBase {
899+
event: 'pipes.connected_account.disconnected';
900+
data: PipesConnectedAccountResponse;
901+
}
902+
903+
export interface PipesConnectedAccountReauthorizationNeededEvent extends EventBase {
904+
event: 'pipes.connected_account.reauthorization_needed';
905+
data: PipesConnectedAccount;
906+
}
907+
908+
export interface PipesConnectedAccountReauthorizationNeededEventResponse extends EventResponseBase {
909+
event: 'pipes.connected_account.reauthorization_needed';
910+
data: PipesConnectedAccountResponse;
911+
}
912+
820913
export interface VaultDataCreatedEvent extends EventBase {
821914
event: 'vault.data.created';
822915
data: VaultDataCreatedEventData;
@@ -993,6 +1086,10 @@ export type Event =
9931086
| GroupDeletedEvent
9941087
| GroupMemberAddedEvent
9951088
| GroupMemberRemovedEvent
1089+
| PipesConnectedAccountConnectedEvent
1090+
| PipesConnectedAccountConnectionFailedEvent
1091+
| PipesConnectedAccountDisconnectedEvent
1092+
| PipesConnectedAccountReauthorizationNeededEvent
9961093
| VaultDataCreatedEvent
9971094
| VaultDataUpdatedEvent
9981095
| VaultDataReadEvent
@@ -1075,6 +1172,10 @@ export type EventResponse =
10751172
| GroupDeletedEventResponse
10761173
| GroupMemberAddedEventResponse
10771174
| GroupMemberRemovedEventResponse
1175+
| PipesConnectedAccountConnectedEventResponse
1176+
| PipesConnectedAccountConnectionFailedEventResponse
1177+
| PipesConnectedAccountDisconnectedEventResponse
1178+
| PipesConnectedAccountReauthorizationNeededEventResponse
10781179
| VaultDataCreatedEventResponse
10791180
| VaultDataUpdatedEventResponse
10801181
| VaultDataReadEventResponse

‎src/common/serializers/event.serializer.ts‎

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,15 @@ import { deserializeOrganizationDomain } from '../../organization-domains/serial
2020
import { deserializeOrganizationMembership } from '../../user-management/serializers/organization-membership.serializer';
2121
import { deserializeRoleEvent } from '../../user-management/serializers/role.serializer';
2222
import { deserializeSession } from '../../user-management/serializers/session.serializer';
23-
import { Event, EventBase, EventResponse } from '../interfaces';
23+
import {
24+
Event,
25+
EventBase,
26+
EventResponse,
27+
PipesConnectedAccount,
28+
PipesConnectedAccountResponse,
29+
PipesConnectionFailed,
30+
PipesConnectionFailedResponse,
31+
} from '../interfaces';
2432
import { deserializeAuthenticationRadarRiskDetectedEvent } from '../../user-management/serializers/authentication-radar-risk-event-serializer';
2533
import { deserializeApiKey } from '../../api-keys/serializers/api-key.serializer';
2634
import { deserializeOrganizationRoleEvent } from '../../authorization/serializers/organization-role.serializer';
@@ -41,6 +49,36 @@ import {
4149
} from '../../vault/serializers/vault-event.serializer';
4250
import { deserializeOrganizationDomainVerificationFailed } from '../../organization-domains/serializers/organization-domain-verification-failed.serializer';
4351

52+
const deserializePipesConnectedAccount = (
53+
connectedAccount: PipesConnectedAccountResponse,
54+
): PipesConnectedAccount => ({
55+
object: connectedAccount.object,
56+
id: connectedAccount.id,
57+
dataIntegrationId: connectedAccount.data_integration_id,
58+
providerSlug: connectedAccount.provider_slug,
59+
userId: connectedAccount.user_id,
60+
organizationId: connectedAccount.organization_id,
61+
scopes: connectedAccount.scopes,
62+
state: connectedAccount.state,
63+
createdAt: connectedAccount.created_at,
64+
updatedAt: connectedAccount.updated_at,
65+
});
66+
67+
const deserializePipesConnectionFailed = (
68+
connectionFailed: PipesConnectionFailedResponse,
69+
): PipesConnectionFailed => ({
70+
object: connectionFailed.object,
71+
dataIntegrationId: connectionFailed.data_integration_id,
72+
providerSlug: connectionFailed.provider_slug,
73+
userId: connectionFailed.user_id,
74+
organizationId: connectionFailed.organization_id,
75+
errorCode: connectionFailed.error_code,
76+
errorReason: connectionFailed.error_reason,
77+
providerError: connectionFailed.provider_error,
78+
providerErrorDescription: connectionFailed.provider_error_description,
79+
createdAt: connectionFailed.created_at,
80+
});
81+
4482
export const deserializeEvent = (event: EventResponse): Event => {
4583
const eventBase: EventBase = {
4684
id: event.id,
@@ -261,6 +299,20 @@ export const deserializeEvent = (event: EventResponse): Event => {
261299
organizationMembershipId: event.data.organization_membership_id,
262300
},
263301
};
302+
case 'pipes.connected_account.connected':
303+
case 'pipes.connected_account.disconnected':
304+
case 'pipes.connected_account.reauthorization_needed':
305+
return {
306+
...eventBase,
307+
event: event.event,
308+
data: deserializePipesConnectedAccount(event.data),
309+
};
310+
case 'pipes.connected_account.connection_failed':
311+
return {
312+
...eventBase,
313+
event: event.event,
314+
data: deserializePipesConnectionFailed(event.data),
315+
};
264316
case 'vault.data.created':
265317
return {
266318
...eventBase,

‎src/events/events.spec.ts‎

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ import {
1818
OrganizationDomainCreatedEventResponse,
1919
OrganizationDomainVerificationFailedEvent,
2020
OrganizationDomainVerificationFailedEventResponse,
21+
PipesConnectedAccountConnectedEvent,
22+
PipesConnectedAccountConnectedEventResponse,
23+
PipesConnectedAccountConnectionFailedEvent,
24+
PipesConnectedAccountConnectionFailedEventResponse,
2125
VaultDataCreatedEvent,
2226
VaultDataCreatedEventResponse,
2327
VaultDataUpdatedEvent,
@@ -169,6 +173,106 @@ describe('Event', () => {
169173
});
170174
});
171175

176+
describe('Pipes connected account events', () => {
177+
it('accepts event filters and deserializes payloads', async () => {
178+
const connectedResponse: PipesConnectedAccountConnectedEventResponse = {
179+
id: 'event_connected',
180+
created_at: '2026-08-25T12:00:00.000Z',
181+
context: { client_id: 'client_123' },
182+
event: 'pipes.connected_account.connected',
183+
data: {
184+
object: 'connected_account',
185+
id: 'data_installation_123',
186+
data_integration_id: 'data_integration_123',
187+
provider_slug: 'github',
188+
user_id: 'user_123',
189+
organization_id: null,
190+
scopes: ['repo', 'user:email'],
191+
state: 'connected',
192+
created_at: '2026-08-25T11:00:00.000Z',
193+
updated_at: '2026-08-25T12:00:00.000Z',
194+
},
195+
};
196+
const connectionFailedResponse: PipesConnectedAccountConnectionFailedEventResponse =
197+
{
198+
id: 'event_connection_failed',
199+
created_at: '2026-08-25T13:00:00.000Z',
200+
event: 'pipes.connected_account.connection_failed',
201+
data: {
202+
object: 'connection_failed',
203+
data_integration_id: 'data_integration_123',
204+
provider_slug: 'github',
205+
user_id: 'user_123',
206+
organization_id: null,
207+
error_code: 'authorization_code_exchange_error',
208+
error_reason: 'The authorization code has expired.',
209+
provider_error: 'access_denied',
210+
provider_error_description:
211+
'The user denied the authorization request.',
212+
created_at: '2026-08-25T13:00:00.000Z',
213+
},
214+
};
215+
const connected: PipesConnectedAccountConnectedEvent = {
216+
id: 'event_connected',
217+
createdAt: '2026-08-25T12:00:00.000Z',
218+
context: { client_id: 'client_123' },
219+
event: 'pipes.connected_account.connected',
220+
data: {
221+
object: 'connected_account',
222+
id: 'data_installation_123',
223+
dataIntegrationId: 'data_integration_123',
224+
providerSlug: 'github',
225+
userId: 'user_123',
226+
organizationId: null,
227+
scopes: ['repo', 'user:email'],
228+
state: 'connected',
229+
createdAt: '2026-08-25T11:00:00.000Z',
230+
updatedAt: '2026-08-25T12:00:00.000Z',
231+
},
232+
};
233+
const connectionFailed: PipesConnectedAccountConnectionFailedEvent = {
234+
id: 'event_connection_failed',
235+
createdAt: '2026-08-25T13:00:00.000Z',
236+
context: undefined,
237+
event: 'pipes.connected_account.connection_failed',
238+
data: {
239+
object: 'connection_failed',
240+
dataIntegrationId: 'data_integration_123',
241+
providerSlug: 'github',
242+
userId: 'user_123',
243+
organizationId: null,
244+
errorCode: 'authorization_code_exchange_error',
245+
errorReason: 'The authorization code has expired.',
246+
providerError: 'access_denied',
247+
providerErrorDescription:
248+
'The user denied the authorization request.',
249+
createdAt: '2026-08-25T13:00:00.000Z',
250+
},
251+
};
252+
253+
fetchOnce({
254+
object: 'list',
255+
data: [connectedResponse, connectionFailedResponse],
256+
list_metadata: {},
257+
});
258+
259+
const list = await workos.events.listEvents({
260+
events: [
261+
'pipes.connected_account.connected',
262+
'pipes.connected_account.connection_failed',
263+
'pipes.connected_account.disconnected',
264+
'pipes.connected_account.reauthorization_needed',
265+
],
266+
});
267+
268+
expect(list).toEqual({
269+
object: 'list',
270+
data: [connected, connectionFailed],
271+
listMetadata: {},
272+
});
273+
});
274+
});
275+
172276
describe('feature flag events', () => {
173277
it('deserializes flag.created events', async () => {
174278
const flagCreatedResponse: FlagCreatedEventResponse = {

‎src/webhooks/interfaces/create-webhook-endpoint-events.interface.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ export const CreateWebhookEndpointEvents = {
7777
PermissionDeleted: 'permission.deleted',
7878
PermissionUpdated: 'permission.updated',
7979
PipesConnectedAccountConnected: 'pipes.connected_account.connected',
80+
PipesConnectedAccountConnectionFailed:
81+
'pipes.connected_account.connection_failed',
8082
PipesConnectedAccountDisconnected: 'pipes.connected_account.disconnected',
8183
PipesConnectedAccountReauthorizationNeeded:
8284
'pipes.connected_account.reauthorization_needed',

‎src/webhooks/interfaces/update-webhook-endpoint-events.interface.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ export const UpdateWebhookEndpointEvents = {
7777
PermissionDeleted: 'permission.deleted',
7878
PermissionUpdated: 'permission.updated',
7979
PipesConnectedAccountConnected: 'pipes.connected_account.connected',
80+
PipesConnectedAccountConnectionFailed:
81+
'pipes.connected_account.connection_failed',
8082
PipesConnectedAccountDisconnected: 'pipes.connected_account.disconnected',
8183
PipesConnectedAccountReauthorizationNeeded:
8284
'pipes.connected_account.reauthorization_needed',

0 commit comments

Comments
 (0)