Summary
workos.pipes.deleteUserConnectedAccount() silently drops its organizationId option, so the organization_id query parameter never reaches the API. Deleting an organization-scoped connected account therefore always fails with a 404 while the account itself stays connected:
NotFoundException: Data Installation not found: 'slug=linear userland_user_id=user_XXX organization_id=null'.
Calling the same REST endpoint directly with ?organization_id=… deletes the account fine, so the API is not at fault — the parameter is lost inside the SDK.
This is easy to miss in application code: treating 404 as "already deleted" is a natural pattern for a delete call, which turns this bug into deletions that appear to succeed while the connected account (and its stored credentials) survive.
Root cause
The generated Pipes method wraps its query in an options object:
// as published in 10.8.0
async deleteUserConnectedAccount(options) {
const { userId, slug } = options;
await this.workos.delete(
`/user_management/users/${encodeURIComponent(userId)}/connected_accounts/${encodeURIComponent(slug)}`,
{ query: { ...(options.organizationId !== undefined && { organization_id: options.organizationId }) } },
);
}
But unlike WorkOS#get/#post/#put — which take an options object with a query field — WorkOS#delete expects the query object itself as its second argument:
async delete(path, query) {
this.requireApiKey(path);
await this.client.delete(path, { params: query });
}
So the HTTP client receives params: { query: { organization_id: … } } and the query string never contains organization_id.
Other generated call sites pass the query directly and are fine, e.g.:
await this.workos.delete(`/authorization/resources/${resourceId}`, query);
deleteUserConnectedAccount appears to be the only affected method at the moment. It looks like it came in with the OpenAPI-generated Pipes module (#1625), where the generator emitted the { query: … } shape that get expects. The asymmetric WorkOS#delete(path, query) signature makes this an easy trap for the generator, so it may be worth normalising the signature (or adding a lint/codegen check) in addition to fixing this method.
Reproduction
- Authorize a data integration with an
organizationId so the resulting connected account is org-scoped.
- Run:
await workos.pipes.deleteUserConnectedAccount({ userId, slug: "linear", organizationId });
- →
NotFoundException with organization_id=null in the message, and the connected account remains connected (verify via GET /user_management/users/{userId}/data_providers?organization_id=…).
- Run the REST call directly:
DELETE /user_management/users/{userId}/connected_accounts/linear?organization_id={organizationId}
→ the connected account is deleted.
Expected behaviour
organizationId is serialised as the organization_id query parameter and the org-scoped connected account is deleted.
Workaround
Bypass the generated method and call the endpoint through the base client:
await workos.delete(
`/user_management/users/${encodeURIComponent(userId)}/connected_accounts/${encodeURIComponent(slug)}`,
{ organization_id: organizationId },
);
Environment
@workos-inc/node 10.8.0 (latest release at time of writing)
Summary
workos.pipes.deleteUserConnectedAccount()silently drops itsorganizationIdoption, so theorganization_idquery parameter never reaches the API. Deleting an organization-scoped connected account therefore always fails with a 404 while the account itself staysconnected:Calling the same REST endpoint directly with
?organization_id=…deletes the account fine, so the API is not at fault — the parameter is lost inside the SDK.This is easy to miss in application code: treating 404 as "already deleted" is a natural pattern for a delete call, which turns this bug into deletions that appear to succeed while the connected account (and its stored credentials) survive.
Root cause
The generated Pipes method wraps its query in an options object:
But unlike
WorkOS#get/#post/#put— which take an options object with aqueryfield —WorkOS#deleteexpects the query object itself as its second argument:So the HTTP client receives
params: { query: { organization_id: … } }and the query string never containsorganization_id.Other generated call sites pass the query directly and are fine, e.g.:
deleteUserConnectedAccountappears to be the only affected method at the moment. It looks like it came in with the OpenAPI-generated Pipes module (#1625), where the generator emitted the{ query: … }shape thatgetexpects. The asymmetricWorkOS#delete(path, query)signature makes this an easy trap for the generator, so it may be worth normalising the signature (or adding a lint/codegen check) in addition to fixing this method.Reproduction
organizationIdso the resulting connected account is org-scoped.NotFoundExceptionwithorganization_id=nullin the message, and the connected account remainsconnected(verify viaGET /user_management/users/{userId}/data_providers?organization_id=…).Expected behaviour
organizationIdis serialised as theorganization_idquery parameter and the org-scoped connected account is deleted.Workaround
Bypass the generated method and call the endpoint through the base client:
Environment
@workos-inc/node10.8.0 (latest release at time of writing)