diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 7f814da..3107bf4 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "7.20.0"
+ ".": "7.21.0"
}
diff --git a/.stats.yml b/.stats.yml
index 4417498..e2794c7 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/CHANGELOG.md b/CHANGELOG.md
index 1624d90..a38f1ca 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/api.md b/api.md
index 8ee88ea..41751b9 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/package.json b/package.json
index 1b7fc84..607827f 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/client.ts b/src/client.ts
index 137bd37..64bab0a 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 923f126..0000000
--- 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 c8e4657..ee7ed6f 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/src/version.ts b/src/version.ts
index 7b73ac2..709152b 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
diff --git a/tests/api-resources/bulk.test.ts b/tests/api-resources/bulk.test.ts
deleted file mode 100644
index 2cec3e1..0000000
--- 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);
- });
-});