Skip to content

feat(oagen[1/2]): Add new endpoints to the SDK via autogeneration#1535

Open
gjtorikian wants to merge 8 commits intomainfrom
oagen/1/new-apis
Open

feat(oagen[1/2]): Add new endpoints to the SDK via autogeneration#1535
gjtorikian wants to merge 8 commits intomainfrom
oagen/1/new-apis

Conversation

@gjtorikian
Copy link
Copy Markdown
Contributor

@gjtorikian gjtorikian commented Mar 26, 2026

Description

This PR adds new SDK support for five new WorkOS API surfaces:

  • AdminPortal: previously, this was under the folder portal, which is a terrible name. I @deprecated those files in favor of this one with a better naming system. The SDK autogenerated wants to call these AdminPortal.
  • Permissions: full CRUD for authorization permissions. Previously this was confusingly tucked under authorization; that, too, has been marked as @deprecate, considering that the SDK generator and API docs treat them separately than the other /authorization endpoints. feat(oagen[2/2]): Update existing SDK modules with generated types, docs, and serializers #1536 reverts this.
  • Radar was completely missing and has been introduced
  • Webhooks endpoints were missing and have been introduced
  • WorkOS Connect (missing, introduced) includes Applications and ApplicationClientSecrets sub-services for managing Connect Applications and their secrets

In the cases above where the SDK autogen is choosing a name (AdminPortal, Permissions), we can, of course, override these, but I happen to agree with it.

Note: this is the first of two PRs.

Why so many changed files?

Each change above was broken into its own commit. The final commit, 2b5ca3c, introduces a set of new changes across the SDK.

Each auto-generated service class imports typed interfaces for its request/response models and enum types. These types are split into separate files by the generator (one interface per schema). The 14 dependency files break down as:

  • src/common/interfaces/: shared enum/literal-union types (e.g., RadarStandaloneResponseVerdict, WebhookEndpointJsonStatus, GenerateLinkIntent) referenced
    by Radar, WebhooksEndpoints, and AdminPortal model interfaces
  • src/authorization/: UpdateOrganizationRole interface and serializer, imported by the Permissions serializer because both share the same wire shape for role-permission updates

gjtorikian and others added 8 commits March 26, 2026 11:46
Adds imports and readonly properties for AdminPortal, Permissions, Radar,
WebhooksEndpoints, WorkOSConnect, Applications, and ApplicationClientSecrets.
Adds createPaginatedList helper used by generated paginated list methods.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds the minimal set of auto-generated interfaces and serializers
needed by the new service classes:
- 12 common enum/type interfaces (referenced by radar, webhooks, admin-portal models)
- 2 authorization model files (referenced by permissions serializer)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@gjtorikian gjtorikian requested review from a team as code owners March 26, 2026 20:08
@gjtorikian gjtorikian requested a review from cmatheson March 26, 2026 20:08
@gjtorikian gjtorikian changed the title feat(oagen[1/2]): Add new endpoints to the SDK via autogeneration feat(oagen[1/3]): Add new endpoints to the SDK via autogeneration Mar 26, 2026
@gjtorikian gjtorikian changed the title feat(oagen[1/3]): Add new endpoints to the SDK via autogeneration feat(oagen[1/2]): Add new endpoints to the SDK via autogeneration Mar 26, 2026
@nicknisi
Copy link
Copy Markdown
Member

@greptileai

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 13, 2026

Greptile Summary

This PR introduces five new SDK surfaces via autogeneration — AdminPortal, Permissions, Radar, WebhooksEndpoints, and WorkOSConnect (Applications + ApplicationClientSecrets) — along with deprecation stubs on the old Portal and Authorization permission methods. The overall structure (serialize/deserialize pairs, createPaginatedList, shared test helpers) is consistent and well-organized.

  • applications.ts create(): the default switch branch returns the raw camelCase TypeScript model directly to workos.post() instead of a serialized snake_case payload — any call where applicationType is missing or unexpected will silently send malformed data to the API.
  • applicationClientSecrets.list(): the method name, JSDoc, and endpoint URL (/client_secrets) imply a collection, but the return type is a single ApplicationCredentialsListItem with no pagination — needs clarification or a rename to get().

Confidence Score: 4/5

Safe to merge after the two P1 issues in applications.ts and application-client-secrets.ts are addressed

Two P1 findings: the unserialized default payload in Applications.create() would send camelCase keys to the API in production, and the list() method on ApplicationClientSecrets has a name/return-type mismatch that could confuse callers. All other new services look correct and well-tested.

src/workos-connect/applications.ts (default case in create()), src/workos-connect/application-client-secrets.ts (list() return type vs. endpoint semantics)

Important Files Changed

Filename Overview
src/workos-connect/applications.ts Adds CRUD for Connect Applications; create() default switch case sends unserialized camelCase payload to the API instead of snake_case wire format
src/workos-connect/application-client-secrets.ts Adds client secret management; list() returns a single ApplicationCredentialsListItem rather than a paginated collection — naming and return type are inconsistent with the endpoint's plural URL
src/workos.ts Registers 7 new service properties and two static server URL constants; incorrectly marked as fully auto-generated despite being the hand-maintained SDK entry point
src/radar/radar.ts New Radar service with assess, updateRadarAttempt, updateRadarList, and deleteRadarListEntry — clean implementation with proper serialize/deserialize pairs
src/webhooks/webhooks-endpoints.ts New WebhooksEndpoints CRUD service using createPaginatedList correctly; full test coverage including pagination, empty results, and 422 error cases
src/permissions/permissions.ts New Permissions CRUD service; correctly uses createPaginatedList for list() and consistent with existing Authorization deprecation stubs
src/admin-portal/admin-portal.ts New AdminPortal service with generatePortalLink; mirrors the deprecated Portal class with updated naming and proper serializers
src/workos-connect/work-os-connect.ts New WorkOSConnect service exposing completeLogin for standalone connect auth flows; straightforward POST implementation
src/common/utils/fetch-and-deserialize.ts Adds createPaginatedList helper used across all new list endpoints; marked as auto-generated despite containing pre-existing hand-written code
src/common/utils/test-utils.ts Adds shared test helpers (testUnauthorized, testPaginatedList, testEmptyResults, testPaginationParams) that reduce boilerplate across all new spec files
src/authorization/authorization.ts Deprecates permission-related methods in favour of the new Permissions service with appropriate JSDoc redirections

Class Diagram

%%{init: {'theme': 'neutral'}}%%
classDiagram
    class WorkOS {
        +adminPortal: AdminPortal
        +permissions: Permissions
        +radar: Radar
        +webhooksEndpoints: WebhooksEndpoints
        +workOsConnect: WorkOSConnect
        +applications: Applications
        +applicationClientSecrets: ApplicationClientSecrets
    }
    class AdminPortal {
        +generatePortalLink(payload) PortalLinkResponse
    }
    class Permissions {
        +list(options?) AutoPaginatable
        +create(payload) Permission
        +find(slug) AuthorizationPermission
        +update(slug, payload) AuthorizationPermission
        +delete(slug) void
    }
    class Radar {
        +assess(payload) RadarStandaloneResponse
        +updateRadarAttempt(id, payload) void
        +updateRadarList(type, action, payload) RadarListEntryAlreadyPresentResponse
        +deleteRadarListEntry(type, action, payload) void
    }
    class WebhooksEndpoints {
        +list(options?) AutoPaginatable
        +create(payload) WebhookEndpointJson
        +update(id, payload) WebhookEndpointJson
        +delete(id) void
    }
    class WorkOSConnect {
        +completeLogin(payload) ExternalAuthCompleteResponse
    }
    class Applications {
        +list(options?) AutoPaginatable
        +create(payload) ConnectApplication
        +find(id) ConnectApplication
        +update(id, payload) ConnectApplication
        +delete(id) void
    }
    class ApplicationClientSecrets {
        +list(id) ApplicationCredentialsListItem
        +create(id, payload) NewConnectApplicationSecret
        +delete(id) void
    }
    WorkOS --> AdminPortal
    WorkOS --> Permissions
    WorkOS --> Radar
    WorkOS --> WebhooksEndpoints
    WorkOS --> WorkOSConnect
    WorkOS --> Applications
    WorkOS --> ApplicationClientSecrets
Loading

Reviews (1): Last reviewed commit: "feat(node): add generated model dependen..." | Re-trigger Greptile

Comment on lines +77 to +85

/**
* Get a Connect Application
*
* Retrieve details for a specific Connect Application by ID or client ID.
* @param id - The application ID or client ID of the Connect Application.
* @example "conn_app_01HXYZ123456789ABCDEFGHIJ"
* @returns {ConnectApplication}
* @throws {NotFoundException} 404
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 default case sends unserialized camelCase payload to the API

When applicationType is anything other than 'oauth' or 'm2m' (e.g., missing or an unexpected value), the raw TypeScript model with camelCase field names is sent directly to the API. The API expects snake_case wire format, so this silently produces a malformed request. The test even exercises this path via workos.applications.create({} as any), which hits the default branch and sends {} — but because the mock always returns the fixture, the test doesn't catch the mismatch.

The default should throw rather than silently forward the unserialized payload:

Suggested change
/**
* Get a Connect Application
*
* Retrieve details for a specific Connect Application by ID or client ID.
* @param id - The application ID or client ID of the Connect Application.
* @example "conn_app_01HXYZ123456789ABCDEFGHIJ"
* @returns {ConnectApplication}
* @throws {NotFoundException} 404
default:
throw new Error(
`Unknown applicationType: ${(payload as any).applicationType}`,
);

Comment on lines +31 to +38
await this.workos.get<ApplicationCredentialsListItemResponse>(
`/connect/applications/${id}/client_secrets`,
);
return deserializeApplicationCredentialsListItem(data);
}

/**
* Create a new client secret for a Connect Application
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 list() returns a single item, not a paginated list

The method is named list and the JSDoc says "List all client secrets", but the return type is Promise<ApplicationCredentialsListItem> — a single deserialized object. The endpoint /connect/applications/${id}/client_secrets is a collection path that typically returns multiple items. If the API actually returns a list envelope (or an array), the SDK silently drops all but the deserialized single-object interpretation.

The fixture application-credentials-list-item.fixture.json is a single object, which is consistent with the current implementation, but the naming ApplicationCredentialsListItem and the list() method name both suggest this is one item in a larger collection. If the API truly returns a single item at this endpoint, the method should be renamed to get() to reflect that. If it returns a list, it should use createPaginatedList (as done in the WebhooksEndpoints, Permissions, and Applications classes).

@@ -1,3 +1,5 @@
// This file is auto-generated by oagen. Do not edit.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 "Do not edit" comment on the core SDK entry point

workos.ts is the primary hand-maintained class that the rest of the SDK builds on — it contains the HTTP client, auth headers, error handling, and all existing service registrations. Marking it as // This file is auto-generated by oagen. Do not edit. signals to future contributors that manual edits will be overwritten, which may be true for the autogenerated sections but is misleading for the parts of the class that are not auto-generated. The same concern applies to src/common/utils/fetch-and-deserialize.ts and src/common/utils/test-utils.ts, both of which had hand-written code before this PR and now carry the same blanket "Do not edit" comment.

If only certain sections are managed by the generator, consider a more targeted approach (e.g., fenced regions or separate generated files that are imported) rather than marking the entire file as off-limits.

Comment on lines +65 to +72
switch ((payload as any).applicationType) {
case 'oauth':
return serializeCreateOAuthApplication(payload as any);
case 'm2m':
return serializeCreateM2MApplication(payload as any);
default:
return payload;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: This whole switch statement casting the payload to any is making nervous. Is this hiding a larger types issue?

Comment on lines +527 to +528
readonly applications = new Applications(this);
readonly applicationClientSecrets = new ApplicationClientSecrets(this);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Should these be exposed at the top-level or should it be under workos.connect?

readonly permissions = new Permissions(this);
readonly radar = new Radar(this);
readonly webhooksEndpoints = new WebhooksEndpoints(this);
readonly workOsConnect = new WorkOSConnect(this);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Should this just be connect so it's workos.connect instead of workos.workOsConnect?

If not, I'm not a fan of the workOsConnect casing.

Comment on lines +522 to +528
readonly adminPortal = new AdminPortal(this);
readonly permissions = new Permissions(this);
readonly radar = new Radar(this);
readonly webhooksEndpoints = new WebhooksEndpoints(this);
readonly workOsConnect = new WorkOSConnect(this);
readonly applications = new Applications(this);
readonly applicationClientSecrets = new ApplicationClientSecrets(this);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: all these new properties should live with the other properties.

Comment on lines +29 to +35
async list(id: string): Promise<ApplicationCredentialsListItem> {
const { data } =
await this.workos.get<ApplicationCredentialsListItemResponse>(
`/connect/applications/${id}/client_secrets`,
);
return deserializeApplicationCredentialsListItem(data);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: this method is called list but from the looks of it only returns one item. Is this the correct name for it?

workos,
endpoint,
deserializeFn,
params,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AutoPaginatable only hands this callback { limit, after } on each page, so any extended filter options (like organizationId) get dropped after page 1. Merging options in keeps the filters on every page, and putting params last lets the pagination cursor still override after/limit as pages advance.

Suggested change
params,
{ ...options, ...params },

Comment on lines +35 to +48
async list(
options?: ApplicationsListOptions,
): Promise<AutoPaginatable<ConnectApplication, ApplicationsListOptions>> {
return createPaginatedList<
ConnectApplicationResponse,
ConnectApplication,
ApplicationsListOptions
>(
this.workos,
'/connect/applications',
deserializeConnectApplication,
options,
);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: is this going to pass in the deserialized options? Like organizationId instead of organization_id?

import { WorkOS } from '../workos';
import { GeneratePortalLinkIntent } from './interfaces/generate-portal-link-intent.interface';

/** @deprecated Use `AdminPortal` instead. */
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: this was added here but the deprecation message doesn't pass through to the WorkOS property (workos.portal.generateLink, for example). The @deprecated directive should be added to the property in wokros.ts, too.

/** @deprecated Use `workos.permissions.list()` instead. */
async listPermissions(
options?: ListPermissionsOptions,
): Promise<PermissionList> {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: This returns PermissionList which is different from what workos.permissions.list will return (AutoPaginatable). This is technically a breaking change.

Comment on lines +19 to +22
export interface ApplicationsListOptions extends PaginationOptions {
/** Filter Connect Applications by organization ID. */
organizationId?: string;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: should this be externally exported? I don't think it's exposed anywhere.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants