From a8266f5d08ecec585ba6698bf82b3b9755834130 Mon Sep 17 00:00:00 2001 From: millerm30 Date: Tue, 15 Sep 2026 17:20:22 -0230 Subject: [PATCH 1/6] Point widget REST at CDK in-app paths and drop Slack/web-push from the client. Keep Basic clientId:userId[:hashedUserId] auth so existing widgets still work against the new API. Co-authored-by: Cursor --- lib/api.ts | 8 +-- lib/client.ts | 87 ++++++++-------------------- lib/interfaces.ts | 141 ---------------------------------------------- tests/sdk.test.ts | 64 +++++++-------------- 4 files changed, 46 insertions(+), 254 deletions(-) diff --git a/lib/api.ts b/lib/api.ts index f074973..27b21ab 100644 --- a/lib/api.ts +++ b/lib/api.ts @@ -4,7 +4,7 @@ import { Logger } from './logger'; export const api = async ( method: 'GET' | 'POST' | 'PATCH' | 'DELETE', host: string | API_REGION, - resource: string, + path: string, clientId: string, userId: string, hashedUserId?: string, @@ -12,9 +12,7 @@ export const api = async ( logger?: Logger ): Promise => { const token = generateBasicTokenForUser(clientId, userId, hashedUserId); - const url = `https://${host}/${clientId}/users/${encodeURIComponent( - userId - )}/${resource}`; + const url = `https://${host}${path.startsWith('/') ? path : `/${path}`}`; const headers = { Authorization: `Basic ${token}` @@ -34,7 +32,7 @@ export const api = async ( try { const res = await fetch(url, { method, - body: JSON.stringify(data), + ...(data !== undefined ? { body: JSON.stringify(data) } : {}), headers }); diff --git a/lib/client.ts b/lib/client.ts index 65b710f..5296e92 100644 --- a/lib/client.ts +++ b/lib/client.ts @@ -7,8 +7,6 @@ import { DeliveryOptionsForEmail, DeliveryOptionsForInappWeb, PostUserRequest, - SlackChannel, - SlackUser, User, UserAccountMetadata, WS_REGION @@ -36,9 +34,6 @@ type NotificationAPIClientSDKConfig = { // Debug mode: debug: boolean; - - // SDK Dev Mode: - sdkDevMode: boolean; }; const defaultConfig: NotificationAPIClientSDKConfig = { @@ -53,10 +48,12 @@ const defaultConfig: NotificationAPIClientSDKConfig = { ).toISOString(), onNewInAppNotifications: undefined, keepWebSocketAliveForSeconds: 24 * 60 * 60, // 24 hours - debug: false, - sdkDevMode: false + debug: false }; +const userResourcePath = () => + `/users/${encodeURIComponent(NotificationAPIClientSDK.config.userId)}`; + type NotificationAPIClientSDK = { config: NotificationAPIClientSDKConfig; logger: Logger; @@ -129,17 +126,6 @@ type NotificationAPIClientSDK = { user: { get: () => Promise; }; - slack: { - getOAuthUrl: (props?: { destinationUrl?: string }) => string; - getChannels: () => Promise<{ - channels: SlackChannel[]; - users: SlackUser[]; - me: SlackUser; - hasMoreChannels?: boolean; - hasMoreUsers?: boolean; - }>; - setChannel: (channelId: string) => Promise; - }; }; export const NotificationAPIClientSDK: NotificationAPIClientSDK = { @@ -185,31 +171,41 @@ export const NotificationAPIClientSDK: NotificationAPIClientSDK = { getNotifications: function (before, count) { return NotificationAPIClientSDK.rest.generic( 'GET', - `notifications/INAPP_WEB?count=${count}&before=${before}` + `/user/inapp?count=${count}&before=${encodeURIComponent(before)}` ); }, patchNotifications: function (params) { return NotificationAPIClientSDK.rest.generic( 'PATCH', - 'notifications/INAPP_WEB', + '/user/inapp', params ); }, getPreferences: function () { - return NotificationAPIClientSDK.rest.generic('GET', 'preferences'); + return NotificationAPIClientSDK.rest.generic( + 'GET', + `${userResourcePath()}/preferences` + ); }, postPreferences: function (params) { return NotificationAPIClientSDK.rest.generic( 'POST', - 'preferences', + `${userResourcePath()}/preferences`, params ); }, postUser: function (params: PostUserRequest) { - return NotificationAPIClientSDK.rest.generic('POST', '', params); + return NotificationAPIClientSDK.rest.generic( + 'POST', + userResourcePath(), + params + ); }, getUserAccountMetadata: function () { - return NotificationAPIClientSDK.rest.generic('GET', 'account_metadata'); + return NotificationAPIClientSDK.rest.generic( + 'GET', + '/users/account-metadata' + ); } }, websocket: { @@ -420,47 +416,10 @@ export const NotificationAPIClientSDK: NotificationAPIClientSDK = { user: { get: async () => { - return NotificationAPIClientSDK.rest.generic('GET', '') as Promise; - } - }, - - slack: { - getOAuthUrl: (props?: { destinationUrl?: string }) => { - const sdkDevMode = NotificationAPIClientSDK.config.sdkDevMode; - const domain = sdkDevMode - ? 'localhost:3001' - : NotificationAPIClientSDK.config.host.replace('api.', 'app.'); - // if no redirectUri is provided, use the current page's URL - const destination = props?.destinationUrl || window.location.href; - - const state = encodeURIComponent( - JSON.stringify({ - destination, - userId: NotificationAPIClientSDK.config.userId, - clientId: NotificationAPIClientSDK.config.clientId, - hashedUserId: NotificationAPIClientSDK.config.hashedUserId - }) - ); - - const url = - 'https://slack.com/oauth/v2/authorize?' + - 'client_id=1146598856352.8825220259395' + - '&scope=chat:write,channels:read,channels:join,chat:write.customize,chat:write.public,groups:read,im:read,users:read' + - `&redirect_uri=https://${domain}/slack/oauth/callback` + - `&state=${state}`; - return url; - }, - getChannels: async () => { - const response = await NotificationAPIClientSDK.rest.generic( + return NotificationAPIClientSDK.rest.generic( 'GET', - 'slack/channels' - ); - return response; - }, - setChannel: async (channelId: string) => { - return NotificationAPIClientSDK.identify({ - slackChannel: channelId - }); + userResourcePath() + ) as Promise; } } }; diff --git a/lib/interfaces.ts b/lib/interfaces.ts index 3e38896..69caefd 100644 --- a/lib/interfaces.ts +++ b/lib/interfaces.ts @@ -174,68 +174,11 @@ export interface User { email?: string; number?: string; pushTokens?: PushToken[]; - webPushTokens?: WebPushToken[]; lastSeenTime?: string; createdAt?: string; updatedAt?: string; - slackChannel?: string; - slackToken?: OauthV2AccessResponse; } -interface WebAPICallResult { - ok: boolean; - error?: string; - response_metadata?: { - warnings?: string[]; - next_cursor?: string; - scopes?: string[]; - acceptedScopes?: string[]; - retryAfter?: number; - messages?: string[]; - }; -} - -interface AuthedUser { - access_token?: string; - expires_in?: number; - id?: string; - refresh_token?: string; - scope?: string; - token_type?: string; -} - -interface Enterprise { - id?: string; - name?: string; -} - -export interface IncomingWebhook { - channel?: string; - channel_id?: string; - configuration_url?: string; - url?: string; -} - -type OauthV2AccessResponse = WebAPICallResult & { - access_token?: string; - app_id?: string; - authed_user?: AuthedUser; - bot_user_id?: string; - enterprise?: Enterprise; - error?: string; - expires_in?: number; - incoming_webhook?: IncomingWebhook; - is_enterprise_install?: boolean; - needed?: string; - ok?: boolean; - provided?: string; - refresh_token?: string; - scope?: string; - team?: Enterprise; - token_type?: string; - warning?: string; -}; - export type PostUserRequest = Omit< Partial, 'lastSeenTime' | 'createdAt' | 'updatedAt' @@ -261,90 +204,6 @@ export interface Device { model?: string; } -export interface PushSubscription { - endpoint: string; - keys: { - p256dh: string; - auth: string; - }; -} - -export interface WebPushToken { - sub: PushSubscription; -} export interface UserAccountMetadata { logo: string; - environmentVapidPublicKey: string; - hasWebPushEnabled: boolean; -} - -/* Slack Types Start */ -export interface SlackChannel { - context_team_id?: string; - conversation_host_id?: string; - created?: number; - creator?: string; - id?: string; - internal_team_ids?: string[]; - is_archived?: boolean; - is_channel?: boolean; - is_ext_shared?: boolean; - is_general?: boolean; - is_global_shared?: boolean; - is_group?: boolean; - is_im?: boolean; - is_member?: boolean; - is_moved?: number; - is_mpim?: boolean; - is_org_default?: boolean; - is_org_mandatory?: boolean; - is_org_shared?: boolean; - is_pending_ext_shared?: boolean; - is_private?: boolean; - is_shared?: boolean; - is_user_deleted?: boolean; - name?: string; - name_normalized?: string; - num_members?: number; - pending_connected_team_ids?: string[]; - pending_shared?: string[]; - previous_names?: string[]; - priority?: number; - properties?: unknown; - purpose?: unknown; - shared_team_ids?: string[]; - topic?: unknown; - unlinked?: number; - updated?: number; - user?: string; -} - -export interface SlackUser { - color?: string; - deleted?: boolean; - enterprise_user?: unknown; - has_2fa?: boolean; - id?: string; - is_admin?: boolean; - is_app_user?: boolean; - is_bot?: boolean; - is_connector_bot?: boolean; - is_email_confirmed?: boolean; - is_invited_user?: boolean; - is_owner?: boolean; - is_primary_owner?: boolean; - is_restricted?: boolean; - is_ultra_restricted?: boolean; - is_workflow_bot?: boolean; - locale?: string; - name?: string; - profile?: unknown; - real_name?: string; - team_id?: string; - two_factor_type?: string; - tz?: string; - tz_label?: string; - tz_offset?: number; - updated?: number; - who_can_share_contact_card?: string; } diff --git a/tests/sdk.test.ts b/tests/sdk.test.ts index a04faf7..877b238 100644 --- a/tests/sdk.test.ts +++ b/tests/sdk.test.ts @@ -38,7 +38,7 @@ describe('NotificationAPIClientSDK', () => { expect(global.fetch).toHaveBeenCalledWith( expect.stringContaining( - 'notifications/INAPP_WEB?count=100&before=2023-01-01T00:00:00Z' + '/user/inapp?count=100&before=2023-01-01T00%3A00%3A00Z' ), expect.any(Object) ); @@ -83,7 +83,7 @@ describe('NotificationAPIClientSDK', () => { }); expect(global.fetch).toHaveBeenCalledWith( - 'https://api.notificationapi.com/testClient/users/testUser/notifications/INAPP_WEB', + 'https://api.notificationapi.com/user/inapp', expect.objectContaining({ method: 'PATCH', body: JSON.stringify({ @@ -113,7 +113,7 @@ describe('NotificationAPIClientSDK', () => { }); expect(global.fetch).toHaveBeenCalledWith( - 'https://api.notificationapi.com/testClient/users/testUser/notifications/INAPP_WEB', + 'https://api.notificationapi.com/user/inapp', expect.objectContaining({ method: 'PATCH', body: JSON.stringify({ @@ -143,7 +143,7 @@ describe('NotificationAPIClientSDK', () => { }); expect(global.fetch).toHaveBeenCalledWith( - 'https://api.notificationapi.com/testClient/users/testUser/notifications/INAPP_WEB', + 'https://api.notificationapi.com/user/inapp', expect.objectContaining({ method: 'PATCH', body: expect.stringContaining('"opened":"'), @@ -170,7 +170,7 @@ describe('NotificationAPIClientSDK', () => { }); expect(global.fetch).toHaveBeenCalledWith( - 'https://api.notificationapi.com/testClient/users/testUser/notifications/INAPP_WEB', + 'https://api.notificationapi.com/user/inapp', expect.objectContaining({ method: 'PATCH', body: JSON.stringify({ @@ -203,7 +203,7 @@ describe('NotificationAPIClientSDK', () => { }); expect(global.fetch).toHaveBeenCalledWith( - 'https://api.notificationapi.com/testClient/users/testUser/notifications/INAPP_WEB', + 'https://api.notificationapi.com/user/inapp', expect.objectContaining({ method: 'PATCH', body: JSON.stringify({ @@ -234,7 +234,7 @@ describe('NotificationAPIClientSDK', () => { await sdk.getPreferences(); expect(global.fetch).toHaveBeenCalledWith( - expect.stringContaining('preferences'), + expect.stringContaining('/users/testUser/preferences'), expect.objectContaining({ method: 'GET' }) @@ -259,7 +259,7 @@ describe('NotificationAPIClientSDK', () => { }); expect(global.fetch).toHaveBeenCalledWith( - expect.stringContaining('preferences'), + expect.stringContaining('/users/testUser/preferences'), expect.objectContaining({ method: 'POST', body: JSON.stringify([ @@ -273,7 +273,7 @@ describe('NotificationAPIClientSDK', () => { ); }); - test('updateDeliveryOption should support SLACK channel', async () => { + test('updateDeliveryOption should support PUSH channel', async () => { const mockResponse = { json: jest.fn().mockResolvedValue({}) }; @@ -285,19 +285,19 @@ describe('NotificationAPIClientSDK', () => { }); await sdk.updateDeliveryOption({ - notificationId: 'testSlackNotification', - channel: 'SLACK', + notificationId: 'testPushNotification', + channel: 'PUSH', delivery: 'hourly' }); expect(global.fetch).toHaveBeenCalledWith( - expect.stringContaining('preferences'), + expect.stringContaining('/users/testUser/preferences'), expect.objectContaining({ method: 'POST', body: JSON.stringify([ { - notificationId: 'testSlackNotification', - channel: 'SLACK', + notificationId: 'testPushNotification', + channel: 'PUSH', delivery: 'hourly' } ]) @@ -322,9 +322,7 @@ describe('NotificationAPIClientSDK', () => { await sdk.identify(params); expect(global.fetch).toHaveBeenCalledWith( - expect.stringContaining( - 'https://api.notificationapi.com/testClient/users/testUser/' - ), + expect.stringContaining('https://api.notificationapi.com/users/testUser'), expect.objectContaining({ method: 'POST', body: JSON.stringify(params) @@ -363,35 +361,13 @@ describe('NotificationAPIClientSDK', () => { expect(global.fetch).toHaveBeenCalledWith( expect.stringContaining( - 'https://api.notificationapi.com/testClient/users/testUser/account_metadata' + 'https://api.notificationapi.com/users/account-metadata' ), expect.objectContaining({ method: 'GET' }) ); }); - test('slack.getChannels should return hasMoreChannels and hasMoreUsers from API', async () => { - const mockResponse = { - json: jest.fn().mockResolvedValue({ - channels: [{ id: 'C123', name: 'general' }], - users: [{ id: 'U123', name: 'alice' }], - me: { id: 'U123', name: 'alice' }, - hasMoreChannels: true, - hasMoreUsers: false - }) - }; - (global.fetch as jest.Mock).mockResolvedValue(mockResponse); - - const sdk = NotificationAPIClientSDK.init({ - userId: 'testUser', - clientId: 'testClient' - }); - - const result = await sdk.slack.getChannels(); - - expect(result.hasMoreChannels).toBe(true); - expect(result.hasMoreUsers).toBe(false); - }); }); describe('api function', () => { @@ -404,14 +380,14 @@ describe('api function', () => { const result = await api( 'GET', 'api.notificationapi.com', - 'testResource', + '/testResource', 'testClient', 'testUser', 'hashedTestUser' ); expect(global.fetch).toHaveBeenCalledWith( - 'https://api.notificationapi.com/testClient/users/testUser/testResource', + 'https://api.notificationapi.com/testResource', expect.objectContaining({ method: 'GET', headers: { @@ -430,7 +406,7 @@ describe('api function', () => { await api( 'GET', 'api.notificationapi.com', - 'testResource', + '/testResource', 'testClient', 'testUser' ); @@ -449,7 +425,7 @@ describe('api function', () => { const result = await api( 'GET', 'api.notificationapi.com', - 'testResource', + '/testResource', 'testClient', 'testUser' ); From 69fab4f8a29f79f6033ab52d3d405c4ade314277 Mon Sep 17 00:00:00 2001 From: millerm30 Date: Wed, 16 Sep 2026 09:02:03 -0230 Subject: [PATCH 2/6] fix: always send 3-part widget Basic token (trailing colon when no hash) The backend distinguishes the widget token from a server clientId:clientSecret token by it having 3 parts. When secureMode is off and there is no hashedUserId, the hash slot is sent empty (clientId:userId:) so the token is never mistaken for a server token. Co-authored-by: Cursor --- lib/api.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/api.ts b/lib/api.ts index 27b21ab..26988a3 100644 --- a/lib/api.ts +++ b/lib/api.ts @@ -75,9 +75,9 @@ export const generateBasicTokenForUser = ( userId: string, hashedUserId?: string ) => { - const token = hashedUserId - ? btoa(clientId + ':' + userId + ':' + hashedUserId) - : btoa(clientId + ':' + userId); - - return token; + // Always send the 3-part widget token (clientId:userId:hashedUserId). The + // hash slot stays empty when secureMode is off. The trailing colon is what + // lets the backend distinguish a widget token from a server + // clientId:clientSecret token. + return btoa(clientId + ':' + userId + ':' + (hashedUserId ?? '')); }; From 94ab6410f13ed7652e33749defd0fe42c6cda7db Mon Sep 17 00:00:00 2001 From: millerm30 Date: Wed, 16 Sep 2026 10:35:59 -0230 Subject: [PATCH 3/6] 2.0.0 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 80f52d5..d259bdd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@notificationapi/core", - "version": "1.2.9", + "version": "2.0.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@notificationapi/core", - "version": "1.2.9", + "version": "2.0.0", "devDependencies": { "@types/jest": "^29.5.3", "@types/node": "^20.14.10", diff --git a/package.json b/package.json index 40f0c48..c04c360 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@notificationapi/core", - "version": "1.2.9", + "version": "2.0.0", "type": "module", "main": "dist/main.js", "types": "dist/main.d.ts", From fee03f7acaf206bc23a8f191192eaac789b6f05d Mon Sep 17 00:00:00 2001 From: millerm30 Date: Thu, 17 Sep 2026 16:02:26 -0230 Subject: [PATCH 4/6] feat: update NotificationAPIClientSDK to use endUser paths for notifications and preferences Refactor the NotificationAPIClientSDK to replace user resource paths with endUser resource paths for fetching and updating notifications and preferences. This change ensures that the SDK correctly targets the new API structure for end users. --- lib/client.ts | 11 +++++++---- tests/sdk.test.ts | 18 +++++++++--------- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/client.ts b/lib/client.ts index 5296e92..e0361c1 100644 --- a/lib/client.ts +++ b/lib/client.ts @@ -54,6 +54,9 @@ const defaultConfig: NotificationAPIClientSDKConfig = { const userResourcePath = () => `/users/${encodeURIComponent(NotificationAPIClientSDK.config.userId)}`; +const endUserResourcePath = () => + `/endUsers/${encodeURIComponent(NotificationAPIClientSDK.config.userId)}`; + type NotificationAPIClientSDK = { config: NotificationAPIClientSDKConfig; logger: Logger; @@ -171,26 +174,26 @@ export const NotificationAPIClientSDK: NotificationAPIClientSDK = { getNotifications: function (before, count) { return NotificationAPIClientSDK.rest.generic( 'GET', - `/user/inapp?count=${count}&before=${encodeURIComponent(before)}` + `${endUserResourcePath()}/inapp?count=${count}&before=${encodeURIComponent(before)}` ); }, patchNotifications: function (params) { return NotificationAPIClientSDK.rest.generic( 'PATCH', - '/user/inapp', + `${endUserResourcePath()}/inapp`, params ); }, getPreferences: function () { return NotificationAPIClientSDK.rest.generic( 'GET', - `${userResourcePath()}/preferences` + `${endUserResourcePath()}/preferences` ); }, postPreferences: function (params) { return NotificationAPIClientSDK.rest.generic( 'POST', - `${userResourcePath()}/preferences`, + `${endUserResourcePath()}/preferences`, params ); }, diff --git a/tests/sdk.test.ts b/tests/sdk.test.ts index 877b238..8c0d843 100644 --- a/tests/sdk.test.ts +++ b/tests/sdk.test.ts @@ -38,7 +38,7 @@ describe('NotificationAPIClientSDK', () => { expect(global.fetch).toHaveBeenCalledWith( expect.stringContaining( - '/user/inapp?count=100&before=2023-01-01T00%3A00%3A00Z' + '/endUsers/testUser/inapp?count=100&before=2023-01-01T00%3A00%3A00Z' ), expect.any(Object) ); @@ -83,7 +83,7 @@ describe('NotificationAPIClientSDK', () => { }); expect(global.fetch).toHaveBeenCalledWith( - 'https://api.notificationapi.com/user/inapp', + 'https://api.notificationapi.com/endUsers/testUser/inapp', expect.objectContaining({ method: 'PATCH', body: JSON.stringify({ @@ -113,7 +113,7 @@ describe('NotificationAPIClientSDK', () => { }); expect(global.fetch).toHaveBeenCalledWith( - 'https://api.notificationapi.com/user/inapp', + 'https://api.notificationapi.com/endUsers/testUser/inapp', expect.objectContaining({ method: 'PATCH', body: JSON.stringify({ @@ -143,7 +143,7 @@ describe('NotificationAPIClientSDK', () => { }); expect(global.fetch).toHaveBeenCalledWith( - 'https://api.notificationapi.com/user/inapp', + 'https://api.notificationapi.com/endUsers/testUser/inapp', expect.objectContaining({ method: 'PATCH', body: expect.stringContaining('"opened":"'), @@ -170,7 +170,7 @@ describe('NotificationAPIClientSDK', () => { }); expect(global.fetch).toHaveBeenCalledWith( - 'https://api.notificationapi.com/user/inapp', + 'https://api.notificationapi.com/endUsers/testUser/inapp', expect.objectContaining({ method: 'PATCH', body: JSON.stringify({ @@ -203,7 +203,7 @@ describe('NotificationAPIClientSDK', () => { }); expect(global.fetch).toHaveBeenCalledWith( - 'https://api.notificationapi.com/user/inapp', + 'https://api.notificationapi.com/endUsers/testUser/inapp', expect.objectContaining({ method: 'PATCH', body: JSON.stringify({ @@ -234,7 +234,7 @@ describe('NotificationAPIClientSDK', () => { await sdk.getPreferences(); expect(global.fetch).toHaveBeenCalledWith( - expect.stringContaining('/users/testUser/preferences'), + expect.stringContaining('/endUsers/testUser/preferences'), expect.objectContaining({ method: 'GET' }) @@ -259,7 +259,7 @@ describe('NotificationAPIClientSDK', () => { }); expect(global.fetch).toHaveBeenCalledWith( - expect.stringContaining('/users/testUser/preferences'), + expect.stringContaining('/endUsers/testUser/preferences'), expect.objectContaining({ method: 'POST', body: JSON.stringify([ @@ -291,7 +291,7 @@ describe('NotificationAPIClientSDK', () => { }); expect(global.fetch).toHaveBeenCalledWith( - expect.stringContaining('/users/testUser/preferences'), + expect.stringContaining('/endUsers/testUser/preferences'), expect.objectContaining({ method: 'POST', body: JSON.stringify([ From fb6af0708460485a806cbb7424bc5e81ce639687 Mon Sep 17 00:00:00 2001 From: millerm30 Date: Thu, 17 Sep 2026 17:00:18 -0230 Subject: [PATCH 5/6] refactor: update NotificationAPIClientSDK tests to reflect endUser resource paths Adjust the tests for NotificationAPIClientSDK to ensure they correctly reference the new endUser resource paths for user account metadata and user identification. This aligns the tests with the recent changes in the SDK's API structure. --- lib/client.ts | 4 ++-- tests/sdk.test.ts | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/client.ts b/lib/client.ts index e0361c1..06447f9 100644 --- a/lib/client.ts +++ b/lib/client.ts @@ -200,14 +200,14 @@ export const NotificationAPIClientSDK: NotificationAPIClientSDK = { postUser: function (params: PostUserRequest) { return NotificationAPIClientSDK.rest.generic( 'POST', - userResourcePath(), + endUserResourcePath(), params ); }, getUserAccountMetadata: function () { return NotificationAPIClientSDK.rest.generic( 'GET', - '/users/account-metadata' + '/endUsers/account-metadata' ); } }, diff --git a/tests/sdk.test.ts b/tests/sdk.test.ts index 8c0d843..f7945f2 100644 --- a/tests/sdk.test.ts +++ b/tests/sdk.test.ts @@ -322,7 +322,9 @@ describe('NotificationAPIClientSDK', () => { await sdk.identify(params); expect(global.fetch).toHaveBeenCalledWith( - expect.stringContaining('https://api.notificationapi.com/users/testUser'), + expect.stringContaining( + 'https://api.notificationapi.com/endUsers/testUser' + ), expect.objectContaining({ method: 'POST', body: JSON.stringify(params) @@ -361,7 +363,7 @@ describe('NotificationAPIClientSDK', () => { expect(global.fetch).toHaveBeenCalledWith( expect.stringContaining( - 'https://api.notificationapi.com/users/account-metadata' + 'https://api.notificationapi.com/endUsers/account-metadata' ), expect.objectContaining({ method: 'GET' From 1eace02f9904dce3f01bd6ef5b4be6d9313c21be Mon Sep 17 00:00:00 2001 From: millerm30 Date: Mon, 21 Sep 2026 14:53:03 -0230 Subject: [PATCH 6/6] refactor: point widget REST at lowercase /enduser paths In-app and preferences drop the user id from the URL so they match cdkAuthUser, which reads the user from the token. Identify still posts to /enduser/{userId}. Co-authored-by: Cursor --- lib/client.ts | 12 ++++++------ tests/sdk.test.ts | 22 +++++++++++----------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/client.ts b/lib/client.ts index 06447f9..cd91918 100644 --- a/lib/client.ts +++ b/lib/client.ts @@ -55,7 +55,7 @@ const userResourcePath = () => `/users/${encodeURIComponent(NotificationAPIClientSDK.config.userId)}`; const endUserResourcePath = () => - `/endUsers/${encodeURIComponent(NotificationAPIClientSDK.config.userId)}`; + `/enduser/${encodeURIComponent(NotificationAPIClientSDK.config.userId)}`; type NotificationAPIClientSDK = { config: NotificationAPIClientSDKConfig; @@ -174,26 +174,26 @@ export const NotificationAPIClientSDK: NotificationAPIClientSDK = { getNotifications: function (before, count) { return NotificationAPIClientSDK.rest.generic( 'GET', - `${endUserResourcePath()}/inapp?count=${count}&before=${encodeURIComponent(before)}` + `/enduser/inapp?count=${count}&before=${encodeURIComponent(before)}` ); }, patchNotifications: function (params) { return NotificationAPIClientSDK.rest.generic( 'PATCH', - `${endUserResourcePath()}/inapp`, + '/enduser/inapp', params ); }, getPreferences: function () { return NotificationAPIClientSDK.rest.generic( 'GET', - `${endUserResourcePath()}/preferences` + '/enduser/preferences' ); }, postPreferences: function (params) { return NotificationAPIClientSDK.rest.generic( 'POST', - `${endUserResourcePath()}/preferences`, + '/enduser/preferences', params ); }, @@ -207,7 +207,7 @@ export const NotificationAPIClientSDK: NotificationAPIClientSDK = { getUserAccountMetadata: function () { return NotificationAPIClientSDK.rest.generic( 'GET', - '/endUsers/account-metadata' + '/enduser/account-metadata' ); } }, diff --git a/tests/sdk.test.ts b/tests/sdk.test.ts index f7945f2..e6f99ed 100644 --- a/tests/sdk.test.ts +++ b/tests/sdk.test.ts @@ -38,7 +38,7 @@ describe('NotificationAPIClientSDK', () => { expect(global.fetch).toHaveBeenCalledWith( expect.stringContaining( - '/endUsers/testUser/inapp?count=100&before=2023-01-01T00%3A00%3A00Z' + '/enduser/inapp?count=100&before=2023-01-01T00%3A00%3A00Z' ), expect.any(Object) ); @@ -83,7 +83,7 @@ describe('NotificationAPIClientSDK', () => { }); expect(global.fetch).toHaveBeenCalledWith( - 'https://api.notificationapi.com/endUsers/testUser/inapp', + 'https://api.notificationapi.com/enduser/inapp', expect.objectContaining({ method: 'PATCH', body: JSON.stringify({ @@ -113,7 +113,7 @@ describe('NotificationAPIClientSDK', () => { }); expect(global.fetch).toHaveBeenCalledWith( - 'https://api.notificationapi.com/endUsers/testUser/inapp', + 'https://api.notificationapi.com/enduser/inapp', expect.objectContaining({ method: 'PATCH', body: JSON.stringify({ @@ -143,7 +143,7 @@ describe('NotificationAPIClientSDK', () => { }); expect(global.fetch).toHaveBeenCalledWith( - 'https://api.notificationapi.com/endUsers/testUser/inapp', + 'https://api.notificationapi.com/enduser/inapp', expect.objectContaining({ method: 'PATCH', body: expect.stringContaining('"opened":"'), @@ -170,7 +170,7 @@ describe('NotificationAPIClientSDK', () => { }); expect(global.fetch).toHaveBeenCalledWith( - 'https://api.notificationapi.com/endUsers/testUser/inapp', + 'https://api.notificationapi.com/enduser/inapp', expect.objectContaining({ method: 'PATCH', body: JSON.stringify({ @@ -203,7 +203,7 @@ describe('NotificationAPIClientSDK', () => { }); expect(global.fetch).toHaveBeenCalledWith( - 'https://api.notificationapi.com/endUsers/testUser/inapp', + 'https://api.notificationapi.com/enduser/inapp', expect.objectContaining({ method: 'PATCH', body: JSON.stringify({ @@ -234,7 +234,7 @@ describe('NotificationAPIClientSDK', () => { await sdk.getPreferences(); expect(global.fetch).toHaveBeenCalledWith( - expect.stringContaining('/endUsers/testUser/preferences'), + expect.stringContaining('/enduser/preferences'), expect.objectContaining({ method: 'GET' }) @@ -259,7 +259,7 @@ describe('NotificationAPIClientSDK', () => { }); expect(global.fetch).toHaveBeenCalledWith( - expect.stringContaining('/endUsers/testUser/preferences'), + expect.stringContaining('/enduser/preferences'), expect.objectContaining({ method: 'POST', body: JSON.stringify([ @@ -291,7 +291,7 @@ describe('NotificationAPIClientSDK', () => { }); expect(global.fetch).toHaveBeenCalledWith( - expect.stringContaining('/endUsers/testUser/preferences'), + expect.stringContaining('/enduser/preferences'), expect.objectContaining({ method: 'POST', body: JSON.stringify([ @@ -323,7 +323,7 @@ describe('NotificationAPIClientSDK', () => { expect(global.fetch).toHaveBeenCalledWith( expect.stringContaining( - 'https://api.notificationapi.com/endUsers/testUser' + 'https://api.notificationapi.com/enduser/testUser' ), expect.objectContaining({ method: 'POST', @@ -363,7 +363,7 @@ describe('NotificationAPIClientSDK', () => { expect(global.fetch).toHaveBeenCalledWith( expect.stringContaining( - 'https://api.notificationapi.com/endUsers/account-metadata' + 'https://api.notificationapi.com/enduser/account-metadata' ), expect.objectContaining({ method: 'GET'