From aa3b3b20653b8a12bdf2c7dc9727c8157201a7f0 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 23 Jul 2026 19:48:20 +0000 Subject: [PATCH 1/2] feat: Remove /bulk endpoints from api-spec (C-19507) (#171) * Remove /bulk endpoints from api-spec (C-19507) Removes the `/bulk` resource and all of its endpoints: - POST /bulk (create job) - POST /bulk/{job_id} (add users) - GET /bulk/{job_id} (get job) - POST /bulk/{job_id}/run (run job) - GET /bulk/{job_id}/users (get users) Deletes the corresponding paths and now-orphaned Bulk component schemas from specs/openapi.yml and the `bulk` resource from specs/stainless.yml. The unrelated /users/{user_id}/preferences bulk update/replace endpoints are untouched. Co-Authored-By: Claude Opus 4.8 * Remove bulk e2e test The /bulk endpoints were removed, so the generated SDK no longer exposes courier.bulk. Delete the corresponding e2e test. Co-Authored-By: Claude Opus 4.8 * Remove bulk SDK examples The /bulk endpoints were removed, so the generated SDKs no longer expose the bulk resource. Delete the per-language bulk example files (csharp, go, java, kotlin, php, python, ruby) that reference client.bulk() and were breaking the example compile/test checks. Co-Authored-By: Claude Opus 4.8 --------- Co-authored-by: Claude Opus 4.8 --- .stats.yml | 8 +- api.md | 18 --- src/client.ts | 25 ---- src/resources/bulk.ts | 210 ------------------------------- src/resources/index.ts | 11 -- tests/api-resources/bulk.test.ts | 152 ---------------------- 6 files changed, 4 insertions(+), 420 deletions(-) delete mode 100644 src/resources/bulk.ts delete mode 100644 tests/api-resources/bulk.test.ts diff --git a/.stats.yml b/.stats.yml index 44174988..e2794c72 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 139 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier/courier-b03f5346718dcd7b22f15893445ea593ab87535c90c540fc5a796c6ecd54d779.yml -openapi_spec_hash: 15677d3be4ecd33eab154080b4bc437f -config_hash: dcd58abf1e56b114bdce22336ca3d570 +configured_endpoints: 134 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/courier/courier-b053468fefe6d757f86f3233ebbc52d80329ed2a6e7a6740fee01e4028a4c3b9.yml +openapi_spec_hash: 416835e693de0fe19945be90a787d3f3 +config_hash: 8d28dbeabe9d4dcc7d5b8c021a4cbbd7 diff --git a/api.md b/api.md index 8ee88ea4..41751b9b 100644 --- a/api.md +++ b/api.md @@ -271,24 +271,6 @@ Methods: - client.brands.list({ ...params }) -> BrandListResponse - client.brands.delete(brandID) -> void -# Bulk - -Types: - -- InboundBulkMessage -- InboundBulkMessageUser -- BulkCreateJobResponse -- BulkListUsersResponse -- BulkRetrieveJobResponse - -Methods: - -- client.bulk.addUsers(jobID, { ...params }) -> void -- client.bulk.createJob({ ...params }) -> BulkCreateJobResponse -- client.bulk.listUsers(jobID, { ...params }) -> BulkListUsersResponse -- client.bulk.retrieveJob(jobID) -> BulkRetrieveJobResponse -- client.bulk.runJob(jobID) -> void - # Digests Types: diff --git a/src/client.ts b/src/client.ts index 137bd376..64bab0ae 100644 --- a/src/client.ts +++ b/src/client.ts @@ -55,17 +55,6 @@ import { Logo, WidgetBackground, } from './resources/brands'; -import { - Bulk, - BulkAddUsersParams, - BulkCreateJobParams, - BulkCreateJobResponse, - BulkListUsersParams, - BulkListUsersResponse, - BulkRetrieveJobResponse, - InboundBulkMessage, - InboundBulkMessageUser, -} from './resources/bulk'; import { Inbound, InboundTrackEventParams, InboundTrackEventResponse } from './resources/inbound'; import { MessageContentResponse, @@ -980,7 +969,6 @@ export class Courier { automations: API.Automations = new API.Automations(this); journeys: API.Journeys = new API.Journeys(this); brands: API.Brands = new API.Brands(this); - bulk: API.Bulk = new API.Bulk(this); digests: API.Digests = new API.Digests(this); inbound: API.Inbound = new API.Inbound(this); lists: API.Lists = new API.Lists(this); @@ -1003,7 +991,6 @@ Courier.Auth = Auth; Courier.Automations = Automations; Courier.Journeys = Journeys; Courier.Brands = Brands; -Courier.Bulk = Bulk; Courier.Digests = Digests; Courier.Inbound = Inbound; Courier.Lists = Lists; @@ -1138,18 +1125,6 @@ export declare namespace Courier { type BrandListParams as BrandListParams, }; - export { - Bulk as Bulk, - type InboundBulkMessage as InboundBulkMessage, - type InboundBulkMessageUser as InboundBulkMessageUser, - type BulkCreateJobResponse as BulkCreateJobResponse, - type BulkListUsersResponse as BulkListUsersResponse, - type BulkRetrieveJobResponse as BulkRetrieveJobResponse, - type BulkAddUsersParams as BulkAddUsersParams, - type BulkCreateJobParams as BulkCreateJobParams, - type BulkListUsersParams as BulkListUsersParams, - }; - export { Digests as Digests, type DigestCategory as DigestCategory, diff --git a/src/resources/bulk.ts b/src/resources/bulk.ts deleted file mode 100644 index 923f1268..00000000 --- a/src/resources/bulk.ts +++ /dev/null @@ -1,210 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import { APIResource } from '../core/resource'; -import * as BulkAPI from './bulk'; -import * as Shared from './shared'; -import { APIPromise } from '../core/api-promise'; -import { buildHeaders } from '../internal/headers'; -import { RequestOptions } from '../internal/request-options'; -import { path } from '../internal/utils/path'; - -export class Bulk extends APIResource { - /** - * Ingest user data into a Bulk Job. - * - * **Important**: For email-based bulk jobs, each user must include `profile.email` - * for provider routing to work correctly. The `to.email` field is not sufficient - * for email provider routing. - */ - addUsers(jobID: string, body: BulkAddUsersParams, options?: RequestOptions): APIPromise { - return this._client.post(path`/bulk/${jobID}`, { - body, - ...options, - headers: buildHeaders([{ Accept: '*/*' }, options?.headers]), - }); - } - - /** - * Creates a new bulk job for sending messages to multiple recipients. - * - * **Required**: `message.event` (event ID or notification ID) - * - * **Optional (V2 format)**: `message.template` (notification ID) or - * `message.content` (Elemental content) can be provided to override the - * notification associated with the event. - */ - createJob(body: BulkCreateJobParams, options?: RequestOptions): APIPromise { - return this._client.post('/bulk', { body, ...options }); - } - - /** - * Get Bulk Job Users - */ - listUsers( - jobID: string, - query: BulkListUsersParams | null | undefined = {}, - options?: RequestOptions, - ): APIPromise { - return this._client.get(path`/bulk/${jobID}/users`, { query, ...options }); - } - - /** - * Get a bulk job - */ - retrieveJob(jobID: string, options?: RequestOptions): APIPromise { - return this._client.get(path`/bulk/${jobID}`, options); - } - - /** - * Run a bulk job - */ - runJob(jobID: string, options?: RequestOptions): APIPromise { - return this._client.post(path`/bulk/${jobID}/run`, { - ...options, - headers: buildHeaders([{ Accept: '*/*' }, options?.headers]), - }); - } -} - -/** - * Bulk message definition. Supports two formats: - * - * - V1 format: Requires `event` field (event ID or notification ID) - * - V2 format: Optionally use `template` (notification ID) or `content` (Elemental - * content) in addition to `event` - */ -export interface InboundBulkMessage { - /** - * Event ID or Notification ID (required). Can be either a Notification ID (e.g., - * "FRH3QXM9E34W4RKP7MRC8NZ1T8V8") or a custom Event ID (e.g., "welcome-email") - * mapped to a notification. - */ - event: string; - - brand?: string | null; - - /** - * Elemental content (optional, for V2 format). When provided, this will be used - * instead of the notification associated with the `event` field. - */ - content?: Shared.ElementalContentSugar | Shared.ElementalContent | null; - - data?: { [key: string]: unknown } | null; - - locale?: { [key: string]: { [key: string]: unknown } } | null; - - override?: { [key: string]: unknown } | null; - - /** - * Notification ID or template ID (optional, for V2 format). When provided, this - * will be used instead of the notification associated with the `event` field. - */ - template?: string | null; -} - -export interface InboundBulkMessageUser { - /** - * User-specific data that will be merged with message.data - */ - data?: unknown; - - preferences?: Shared.RecipientPreferences | null; - - /** - * User profile information. For email-based bulk jobs, `profile.email` is required - * for provider routing to determine if the message can be delivered. The email - * address should be provided here rather than in `to.email`. - */ - profile?: { [key: string]: unknown } | null; - - /** - * User ID (legacy field, use profile or to.user_id instead) - */ - recipient?: string | null; - - /** - * Optional recipient information. Note: For email provider routing, use - * `profile.email` instead of `to.email`. The `to` field is primarily used for - * recipient identification and data merging. - */ - to?: Shared.UserRecipient | null; -} - -export interface BulkCreateJobResponse { - jobId: string; -} - -export interface BulkListUsersResponse { - items: Array; - - paging: Shared.Paging; -} - -export namespace BulkListUsersResponse { - export interface Item extends BulkAPI.InboundBulkMessageUser { - status: 'PENDING' | 'ENQUEUED' | 'ERROR'; - - messageId?: string | null; - } -} - -export interface BulkRetrieveJobResponse { - job: BulkRetrieveJobResponse.Job; -} - -export namespace BulkRetrieveJobResponse { - export interface Job { - /** - * Bulk message definition. Supports two formats: - * - * - V1 format: Requires `event` field (event ID or notification ID) - * - V2 format: Optionally use `template` (notification ID) or `content` (Elemental - * content) in addition to `event` - */ - definition: BulkAPI.InboundBulkMessage; - - enqueued: number; - - failures: number; - - received: number; - - status: 'CREATED' | 'PROCESSING' | 'COMPLETED' | 'ERROR'; - } -} - -export interface BulkAddUsersParams { - users: Array; -} - -export interface BulkCreateJobParams { - /** - * Bulk message definition. Supports two formats: - * - * - V1 format: Requires `event` field (event ID or notification ID) - * - V2 format: Optionally use `template` (notification ID) or `content` (Elemental - * content) in addition to `event` - */ - message: InboundBulkMessage; -} - -export interface BulkListUsersParams { - /** - * A unique identifier that allows for fetching the next set of users added to the - * bulk job - */ - cursor?: string | null; -} - -export declare namespace Bulk { - export { - type InboundBulkMessage as InboundBulkMessage, - type InboundBulkMessageUser as InboundBulkMessageUser, - type BulkCreateJobResponse as BulkCreateJobResponse, - type BulkListUsersResponse as BulkListUsersResponse, - type BulkRetrieveJobResponse as BulkRetrieveJobResponse, - type BulkAddUsersParams as BulkAddUsersParams, - type BulkCreateJobParams as BulkCreateJobParams, - type BulkListUsersParams as BulkListUsersParams, - }; -} diff --git a/src/resources/index.ts b/src/resources/index.ts index c8e46572..ee7ed6f0 100644 --- a/src/resources/index.ts +++ b/src/resources/index.ts @@ -46,17 +46,6 @@ export { type BrandUpdateParams, type BrandListParams, } from './brands'; -export { - Bulk, - type InboundBulkMessage, - type InboundBulkMessageUser, - type BulkCreateJobResponse, - type BulkListUsersResponse, - type BulkRetrieveJobResponse, - type BulkAddUsersParams, - type BulkCreateJobParams, - type BulkListUsersParams, -} from './bulk'; export { Digests, type DigestCategory, diff --git a/tests/api-resources/bulk.test.ts b/tests/api-resources/bulk.test.ts deleted file mode 100644 index 2cec3e13..00000000 --- a/tests/api-resources/bulk.test.ts +++ /dev/null @@ -1,152 +0,0 @@ -// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. - -import Courier from '@trycourier/courier'; - -const client = new Courier({ - apiKey: 'My API Key', - baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', -}); - -describe('resource bulk', () => { - // Mock server tests are disabled - test.skip('addUsers: only required params', async () => { - const responsePromise = client.bulk.addUsers('job_id', { users: [{}] }); - const rawResponse = await responsePromise.asResponse(); - expect(rawResponse).toBeInstanceOf(Response); - const response = await responsePromise; - expect(response).not.toBeInstanceOf(Response); - const dataAndResponse = await responsePromise.withResponse(); - expect(dataAndResponse.data).toBe(response); - expect(dataAndResponse.response).toBe(rawResponse); - }); - - // Mock server tests are disabled - test.skip('addUsers: required and optional params', async () => { - const response = await client.bulk.addUsers('job_id', { - users: [ - { - data: {}, - preferences: { - categories: { - foo: { - status: 'OPTED_IN', - channel_preferences: [{ channel: 'direct_message' }], - rules: [{ until: 'until', start: 'start' }], - }, - }, - notifications: { - foo: { - status: 'OPTED_IN', - channel_preferences: [{ channel: 'direct_message' }], - rules: [{ until: 'until', start: 'start' }], - }, - }, - }, - profile: { foo: 'bar' }, - recipient: 'recipient', - to: { - account_id: 'account_id', - context: { tenant_id: 'tenant_id' }, - data: { foo: 'bar' }, - email: 'email', - list_id: 'list_id', - locale: 'locale', - phone_number: 'phone_number', - preferences: { - notifications: { - foo: { - status: 'OPTED_IN', - channel_preferences: [{ channel: 'direct_message' }], - rules: [{ until: 'until', start: 'start' }], - source: 'subscription', - }, - }, - categories: { - foo: { - status: 'OPTED_IN', - channel_preferences: [{ channel: 'direct_message' }], - rules: [{ until: 'until', start: 'start' }], - source: 'subscription', - }, - }, - templateId: 'templateId', - }, - tenant_id: 'tenant_id', - user_id: 'user_id', - }, - }, - ], - }); - }); - - // Mock server tests are disabled - test.skip('createJob: only required params', async () => { - const responsePromise = client.bulk.createJob({ message: { event: 'event' } }); - const rawResponse = await responsePromise.asResponse(); - expect(rawResponse).toBeInstanceOf(Response); - const response = await responsePromise; - expect(response).not.toBeInstanceOf(Response); - const dataAndResponse = await responsePromise.withResponse(); - expect(dataAndResponse.data).toBe(response); - expect(dataAndResponse.response).toBe(rawResponse); - }); - - // Mock server tests are disabled - test.skip('createJob: required and optional params', async () => { - const response = await client.bulk.createJob({ - message: { - event: 'event', - brand: 'brand', - content: { body: 'body', title: 'title' }, - data: { foo: 'bar' }, - locale: { foo: { foo: 'bar' } }, - override: { foo: 'bar' }, - template: 'template', - }, - }); - }); - - // Mock server tests are disabled - test.skip('listUsers', async () => { - const responsePromise = client.bulk.listUsers('job_id'); - const rawResponse = await responsePromise.asResponse(); - expect(rawResponse).toBeInstanceOf(Response); - const response = await responsePromise; - expect(response).not.toBeInstanceOf(Response); - const dataAndResponse = await responsePromise.withResponse(); - expect(dataAndResponse.data).toBe(response); - expect(dataAndResponse.response).toBe(rawResponse); - }); - - // Mock server tests are disabled - test.skip('listUsers: request options and params are passed correctly', async () => { - // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error - await expect( - client.bulk.listUsers('job_id', { cursor: 'cursor' }, { path: '/_stainless_unknown_path' }), - ).rejects.toThrow(Courier.NotFoundError); - }); - - // Mock server tests are disabled - test.skip('retrieveJob', async () => { - const responsePromise = client.bulk.retrieveJob('job_id'); - const rawResponse = await responsePromise.asResponse(); - expect(rawResponse).toBeInstanceOf(Response); - const response = await responsePromise; - expect(response).not.toBeInstanceOf(Response); - const dataAndResponse = await responsePromise.withResponse(); - expect(dataAndResponse.data).toBe(response); - expect(dataAndResponse.response).toBe(rawResponse); - }); - - // Mock server tests are disabled - test.skip('runJob', async () => { - const responsePromise = client.bulk.runJob('job_id'); - const rawResponse = await responsePromise.asResponse(); - expect(rawResponse).toBeInstanceOf(Response); - const response = await responsePromise; - expect(response).not.toBeInstanceOf(Response); - const dataAndResponse = await responsePromise.withResponse(); - expect(dataAndResponse.data).toBe(response); - expect(dataAndResponse.response).toBe(rawResponse); - }); -}); From 6e13469c1ba3f404956da6d9123fa6a04a27723b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 23 Jul 2026 19:48:38 +0000 Subject: [PATCH 2/2] release: 7.21.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 8 ++++++++ package.json | 2 +- src/version.ts | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 7f814dac..3107bf4b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "7.20.0" + ".": "7.21.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 1624d90c..a38f1cab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 7.21.0 (2026-07-23) + +Full Changelog: [v7.20.0...v7.21.0](https://github.com/trycourier/courier-node/compare/v7.20.0...v7.21.0) + +### Features + +* Remove /bulk endpoints from api-spec (C-19507) ([#171](https://github.com/trycourier/courier-node/issues/171)) ([aa3b3b2](https://github.com/trycourier/courier-node/commit/aa3b3b20653b8a12bdf2c7dc9727c8157201a7f0)) + ## 7.20.0 (2026-07-20) Full Changelog: [v7.19.2...v7.20.0](https://github.com/trycourier/courier-node/compare/v7.19.2...v7.20.0) diff --git a/package.json b/package.json index 1b7fc842..607827f8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@trycourier/courier", - "version": "7.20.0", + "version": "7.21.0", "description": "The official TypeScript library for the Courier API", "author": "Courier ", "types": "dist/index.d.ts", diff --git a/src/version.ts b/src/version.ts index 7b73ac28..709152b6 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const VERSION = '7.20.0'; // x-release-please-version +export const VERSION = '7.21.0'; // x-release-please-version