UNPKG

customerio-node

Version:

A node client for the Customer.io event API. http://customer.io

2,712 lines 121 kB
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SendInAppRequest = exports.SendInboxMessageRequest = exports.SendWhatsAppRequest = exports.SendSMSRequest = exports.SendPushRequest = exports.SendEmailRequest = exports.APIClient = exports.DeliveryExportMetric = void 0;
const request_1 = __importDefault(require("./request"));
const regions_1 = require("./regions");
const requests_1 = require("./api/requests");
const utils_1 = require("./utils");
const types_1 = require("./types");
/**
 * Accepted upload extensions mapped to their MIME type. Used to set the multipart
 * part's `Content-Type` client-side when the caller omits `contentType`: an untyped
 * `Blob` is serialized as `application/octet-stream`, which the Assets API rejects,
 * and the API's own extension fallback only runs when the part carries no type at all.
 */
const ASSET_CONTENT_TYPE_BY_EXTENSION = {
    bmp: 'image/bmp',
    jpg: 'image/jpeg',
    jpeg: 'image/jpeg',
    png: 'image/png',
    gif: 'image/gif',
    pdf: 'application/pdf',
};
/** Derive an accepted asset MIME type from a filename's extension, or `undefined` if unrecognized. */
const assetContentTypeForFilename = (filename) => {
    const dot = filename.lastIndexOf('.');
    if (dot < 0) {
        return undefined;
    }
    return ASSET_CONTENT_TYPE_BY_EXTENSION[filename.slice(dot + 1).toLowerCase()];
};
/**
 * Metric to scope a delivery export to. Pass via the `options.metric` field
 * of {@link APIClient.createDeliveriesExport}.
 */
var DeliveryExportMetric;
(function (DeliveryExportMetric) {
    DeliveryExportMetric["Created"] = "created";
    DeliveryExportMetric["Attempted"] = "attempted";
    DeliveryExportMetric["Sent"] = "sent";
    DeliveryExportMetric["Delivered"] = "delivered";
    DeliveryExportMetric["Opened"] = "opened";
    DeliveryExportMetric["Clicked"] = "clicked";
    DeliveryExportMetric["Converted"] = "converted";
    DeliveryExportMetric["Bounced"] = "bounced";
    DeliveryExportMetric["Spammed"] = "spammed";
    DeliveryExportMetric["Unsubscribed"] = "unsubscribed";
    DeliveryExportMetric["Dropped"] = "dropped";
    DeliveryExportMetric["Failed"] = "failed";
    DeliveryExportMetric["Undeliverable"] = "undeliverable";
})(DeliveryExportMetric || (exports.DeliveryExportMetric = DeliveryExportMetric = {}));
const BROADCASTS_ALLOWED_RECIPIENT_FIELDS = {
    ids: ['ids', 'id_ignore_missing'],
    emails: ['emails', 'email_ignore_missing', 'email_add_duplicates'],
    per_user_data: ['per_user_data', 'id_ignore_missing', 'email_ignore_missing', 'email_add_duplicates'],
    data_file_url: ['data_file_url', 'id_ignore_missing', 'email_ignore_missing', 'email_add_duplicates'],
};
const filterRecipientsDataForField = (recipients, field) => {
    return BROADCASTS_ALLOWED_RECIPIENT_FIELDS[field].reduce((obj, field) => {
        if (!!recipients[field]) {
            obj[field] = recipients[field];
        }
        return obj;
    }, {});
};
/**
 * Client for the Customer.io App API.
 *
 * Authenticates with a bearer App API key. Use this client to send
 * transactional messages, trigger broadcasts, look up customers, and
 * manage exports.
 *
 * Every method rejects with a {@link CustomerIORequestError} when the API
 * returns a non-2xx status.
 *
 * @example
 * ```ts
 * import { APIClient, RegionUS, SendEmailRequest } from 'customerio-node';
 *
 * const api = new APIClient(appKey, { region: RegionUS });
 * await api.sendEmail(new SendEmailRequest({
 *   to: 'a@example.com',
 *   identifiers: { email: 'a@example.com' },
 *   transactional_message_id: 'welcome',
 * }));
 * ```
 */
class APIClient {
    appKey;
    defaults;
    request;
    apiRoot;
    /**
     * @param appKey Your Customer.io App API bearer token.
     * @param defaults Optional overrides. Use `region` to select {@link RegionUS} or {@link RegionEU},
     *   `url` to point at a custom host, `timeout` (ms, default `10000`), or any other fetch
     *   {@link RequestDefaults} field — notably `dispatcher` (an undici `Agent` / `ProxyAgent`) for
     *   proxies, custom TLS, or connection keep-alive.
     * @throws If `region` is provided and is not a {@link Region} instance.
     */
    constructor(appKey, defaults = {}) {
        if (defaults.region && !(defaults.region instanceof regions_1.Region)) {
            throw new Error('region must be one of Regions.US or Regions.EU');
        }
        this.appKey = appKey;
        this.defaults = { ...defaults, region: defaults.region || regions_1.RegionUS };
        // `region`/`url` are SDK concerns (they select the host); strip them so the
        // transport receives only fetch init. `retry` is handled by `Request`.
        const { region: _region, url: _url, ...requestDefaults } = this.defaults;
        this.request = new request_1.default(this.appKey, requestDefaults);
        this.apiRoot = this.defaults.url ? this.defaults.url : this.defaults.region.apiUrl;
    }
    /**
     * Send a transactional email.
     *
     * @param req A constructed {@link SendEmailRequest} instance.
     * @returns The parsed JSON response body (includes delivery id).
     * @throws {Error} If `req` is not a {@link SendEmailRequest} instance.
     */
    sendEmail(req) {
        if (!(req instanceof requests_1.SendEmailRequest)) {
            throw new Error('"request" must be an instance of SendEmailRequest');
        }
        return this.request.post(`${this.apiRoot}/send/email`, req.message);
    }
    /**
     * Send a transactional push notification.
     *
     * @param req A constructed {@link SendPushRequest} instance.
     * @returns The parsed JSON response body.
     * @throws {Error} If `req` is not a {@link SendPushRequest} instance.
     */
    sendPush(req) {
        if (!(req instanceof requests_1.SendPushRequest)) {
            throw new Error('"request" must be an instance of SendPushRequest');
        }
        return this.request.post(`${this.apiRoot}/send/push`, req.message);
    }
    /**
     * Send a transactional SMS.
     *
     * @param req A constructed {@link SendSMSRequest} instance.
     * @returns The parsed JSON response body.
     * @throws {Error} If `req` is not a {@link SendSMSRequest} instance.
     */
    sendSMS(req) {
        if (!(req instanceof requests_1.SendSMSRequest)) {
            throw new Error('"request" must be an instance of SendSMSRequest');
        }
        return this.request.post(`${this.apiRoot}/send/sms`, req.message);
    }
    /**
     * Send a transactional WhatsApp message.
     *
     * @param req A constructed {@link SendWhatsAppRequest} instance.
     * @returns The parsed JSON response body.
     * @throws {Error} If `req` is not a {@link SendWhatsAppRequest} instance.
     */
    sendWhatsApp(req) {
        if (!(req instanceof requests_1.SendWhatsAppRequest)) {
            throw new Error('"request" must be an instance of SendWhatsAppRequest');
        }
        return this.request.post(`${this.apiRoot}/send/whatsapp`, req.message);
    }
    /**
     * Send a transactional inbox message.
     *
     * @param req A constructed {@link SendInboxMessageRequest} instance.
     * @returns The parsed JSON response body.
     * @throws {Error} If `req` is not a {@link SendInboxMessageRequest} instance.
     */
    sendInboxMessage(req) {
        if (!(req instanceof requests_1.SendInboxMessageRequest)) {
            throw new Error('"request" must be an instance of SendInboxMessageRequest');
        }
        return this.request.post(`${this.apiRoot}/send/inbox_message`, req.message);
    }
    /**
     * Send a transactional in-app message.
     *
     * @param req A constructed {@link SendInAppRequest} instance.
     * @returns The parsed JSON response body.
     * @throws {Error} If `req` is not a {@link SendInAppRequest} instance.
     */
    sendInApp(req) {
        if (!(req instanceof requests_1.SendInAppRequest)) {
            throw new Error('"request" must be an instance of SendInAppRequest');
        }
        return this.request.post(`${this.apiRoot}/send/in_app`, req.message);
    }
    /**
     * Look up all people in your workspace with a matching email address.
     *
     * @param email Full email address. Will be URL-encoded.
     * @returns The parsed JSON response body (`{ results: [...] }`).
     * @throws {Error} If `email` is not a non-empty string.
     */
    getCustomersByEmail(email) {
        if (typeof email !== 'string' || (0, utils_1.isEmpty)(email)) {
            throw new Error('"email" must be a string');
        }
        return this.request.get(`${this.apiRoot}/customers?email=${encodeURIComponent(email)}`);
    }
    /**
     * Trigger an API-triggered broadcast (campaign).
     *
     * `recipients` may contain one of the special fields `ids`, `emails`,
     * `per_user_data`, or `data_file_url` (with associated `*_ignore_missing` /
     * `email_add_duplicates` flags); when present, that field's allowed
     * companions are forwarded and any other recipients fields are ignored.
     * Otherwise the entire `recipients` object is forwarded verbatim alongside
     * `data` (use this for segment-based recipients).
     *
     * Both `data` and `recipients` are optional; omitting `recipients` sends the
     * broadcast to its configured recipients.
     *
     * Note that the parameters are positional: to pass `recipients` without
     * `data`, pass `undefined` for `data` — e.g.
     * `triggerBroadcast(1, undefined, { emails: ['user@example.com'] })`.
     * Passing the recipient selector as the second argument would send it as
     * liquid `data` and trigger the broadcast's configured recipients instead.
     *
     * @param broadcastId The broadcast (campaign) id.
     * @param data Liquid `data` payload made available to the broadcast template.
     * @param recipients Recipient selector. See above.
     * @returns The parsed JSON response body.
     */
    triggerBroadcast(broadcastId, data, recipients) {
        let payload = {};
        if (data != null && Object.keys(data).length > 0) {
            payload.data = data;
        }
        if (recipients != null && Object.keys(recipients).length > 0) {
            let customRecipientField = Object.keys(BROADCASTS_ALLOWED_RECIPIENT_FIELDS).find((field) => recipients[field]);
            if (customRecipientField) {
                payload = Object.assign(payload, filterRecipientsDataForField(recipients, customRecipientField));
            }
            else {
                payload.recipients = recipients;
            }
        }
        return this.request.post(`${this.apiRoot}/campaigns/${encodeURIComponent(broadcastId)}/triggers`, payload);
    }
    /**
     * List all exports in your workspace.
     *
     * @returns The parsed JSON response body (`{ exports: [...] }`).
     */
    listExports() {
        return this.request.get(`${this.apiRoot}/exports`);
    }
    /**
     * Get metadata for a specific export, including its status.
     *
     * @param id The export id.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `id` is empty.
     */
    getExport(id) {
        if ((0, utils_1.isEmpty)(id)) {
            throw new utils_1.MissingParamError('id');
        }
        return this.request.get(`${this.apiRoot}/exports/${encodeURIComponent(id)}`);
    }
    /**
     * Get a time-limited download URL for an export.
     *
     * Only meaningful once {@link APIClient.getExport} reports the export as ready.
     *
     * @param id The export id.
     * @returns The parsed JSON response body (`{ url: "..." }`).
     * @throws {MissingParamError} If `id` is empty.
     */
    downloadExport(id) {
        if ((0, utils_1.isEmpty)(id)) {
            throw new utils_1.MissingParamError('id');
        }
        return this.request.get(`${this.apiRoot}/exports/${encodeURIComponent(id)}/download`);
    }
    /**
     * Start an export of people matching a filter.
     *
     * @param filters Filter expression (segment / attribute / and / or / not).
     * @returns The parsed JSON response body, including the new export's id.
     * @throws {MissingParamError} If `filters` is `null` or `undefined`.
     */
    createCustomersExport(filters) {
        if (filters == null) {
            throw new utils_1.MissingParamError('filters');
        }
        return this.request.post(`${this.apiRoot}/exports/customers`, { filters });
    }
    /**
     * Start an export of delivery telemetry for a given newsletter.
     *
     * @param newsletterId The newsletter id whose deliveries should be exported.
     * @param options Optional filters — see {@link DeliveryExportRequestOptions}.
     * @returns The parsed JSON response body, including the new export's id.
     * @throws {MissingParamError} If `newsletterId` is empty.
     */
    createDeliveriesExport(newsletterId, options) {
        if ((0, utils_1.isEmpty)(newsletterId)) {
            throw new utils_1.MissingParamError('newsletterId');
        }
        return this.request.post(`${this.apiRoot}/exports/deliveries`, { newsletter_id: newsletterId, ...options });
    }
    /**
     * Get a person's attributes by identifier.
     *
     * @param id The person's identifier value.
     * @param idType Which identifier kind to look up by. Defaults to {@link IdentifierType.Id}.
     * @returns The parsed JSON response body (`{ customer: {...} }`).
     * @throws {MissingParamError} If `id` is empty.
     * @throws {Error} If `idType` is not a valid {@link IdentifierType}.
     */
    getAttributes(id, idType = types_1.IdentifierType.Id) {
        if ((0, utils_1.isEmpty)(id)) {
            throw new utils_1.MissingParamError('id');
        }
        if (!(0, utils_1.isIdentifierType)(idType)) {
            throw new Error('idType must be one of "id", "cio_id", or "email"');
        }
        return this.request.get(`${this.apiRoot}/customers/${encodeURIComponent(id)}/attributes?id_type=${idType}`);
    }
    /**
     * Look up a person's activities (events, attribute changes, message activity, …).
     *
     * @param customerId The person's identifier value.
     * @param options Optional filters/pagination. See {@link CustomerActivitiesOptions}.
     * @returns The parsed JSON response body (`{ activities: [...], next }`).
     * @throws {MissingParamError} If `customerId` is empty.
     * @throws {Error} If `options.idType` is provided and is not a valid {@link IdentifierType}.
     */
    getCustomerActivities(customerId, options = {}) {
        if ((0, utils_1.isEmpty)(customerId)) {
            throw new utils_1.MissingParamError('customerId');
        }
        if (options.idType !== undefined && !(0, utils_1.isIdentifierType)(options.idType)) {
            throw new Error('idType must be one of "id", "cio_id", or "email"');
        }
        const query = (0, utils_1.buildQueryString)({
            id_type: options.idType,
            start: options.start,
            limit: options.limit,
            type: options.type,
            name: options.name,
        });
        return this.request.get(`${this.apiRoot}/customers/${encodeURIComponent(customerId)}/activities${query}`);
    }
    /**
     * Look up messages sent to a person.
     *
     * @param customerId The person's identifier value.
     * @param options Optional filters/pagination. See {@link CustomerMessagesOptions}.
     * @returns The parsed JSON response body (`{ messages: [...], next }`).
     * @throws {MissingParamError} If `customerId` is empty.
     * @throws {Error} If `options.idType` is provided and is not a valid {@link IdentifierType}.
     */
    getCustomerMessages(customerId, options = {}) {
        if ((0, utils_1.isEmpty)(customerId)) {
            throw new utils_1.MissingParamError('customerId');
        }
        if (options.idType !== undefined && !(0, utils_1.isIdentifierType)(options.idType)) {
            throw new Error('idType must be one of "id", "cio_id", or "email"');
        }
        const query = (0, utils_1.buildQueryString)({
            id_type: options.idType,
            start: options.start,
            limit: options.limit,
            start_ts: options.start_ts,
            end_ts: options.end_ts,
        });
        return this.request.get(`${this.apiRoot}/customers/${encodeURIComponent(customerId)}/messages${query}`);
    }
    /**
     * Look up a person's relationships to objects.
     *
     * @param customerId The person's identifier value.
     * @param options Optional pagination. See {@link PaginationOptions}.
     * @returns The parsed JSON response body (`{ identifiers: [...], relationships: [...], next }`).
     * @throws {MissingParamError} If `customerId` is empty.
     */
    getCustomerRelationships(customerId, options = {}) {
        if ((0, utils_1.isEmpty)(customerId)) {
            throw new utils_1.MissingParamError('customerId');
        }
        const query = (0, utils_1.buildQueryString)({ start: options.start, limit: options.limit });
        return this.request.get(`${this.apiRoot}/customers/${encodeURIComponent(customerId)}/relationships${query}`);
    }
    /**
     * Look up the segments a person belongs to.
     *
     * @param customerId The person's identifier value.
     * @param idType Which identifier kind `customerId` is. Defaults to {@link IdentifierType.Id}.
     * @returns The parsed JSON response body (`{ segments: [...] }`).
     * @throws {MissingParamError} If `customerId` is empty.
     * @throws {Error} If `idType` is not a valid {@link IdentifierType}.
     */
    getCustomerSegments(customerId, idType = types_1.IdentifierType.Id) {
        if ((0, utils_1.isEmpty)(customerId)) {
            throw new utils_1.MissingParamError('customerId');
        }
        if (!(0, utils_1.isIdentifierType)(idType)) {
            throw new Error('idType must be one of "id", "cio_id", or "email"');
        }
        const query = (0, utils_1.buildQueryString)({ id_type: idType });
        return this.request.get(`${this.apiRoot}/customers/${encodeURIComponent(customerId)}/segments${query}`);
    }
    /**
     * Look up a person's subscription (topic) preferences.
     *
     * @param customerId The person's identifier value.
     * @param options Optional identifier kind and localization. See {@link CustomerSubscriptionPreferencesOptions}.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `customerId` is empty.
     * @throws {Error} If `options.idType` is provided and is not a valid {@link IdentifierType}.
     */
    getCustomerSubscriptionPreferences(customerId, options = {}) {
        if ((0, utils_1.isEmpty)(customerId)) {
            throw new utils_1.MissingParamError('customerId');
        }
        if (options.idType !== undefined && !(0, utils_1.isIdentifierType)(options.idType)) {
            throw new Error('idType must be one of "id", "cio_id", or "email"');
        }
        const query = (0, utils_1.buildQueryString)({ id_type: options.idType, language: options.language });
        return this.request.get(`${this.apiRoot}/customers/${encodeURIComponent(customerId)}/subscription_preferences${query}`);
    }
    /**
     * Search for people matching a filter.
     *
     * @param filter A segment/attribute filter expression (and/or/not). See {@link Filter}.
     * @param options Optional pagination. See {@link PaginationOptions}.
     * @returns The parsed JSON response body (`{ identifiers: [...], ids: [...], next }`).
     * @throws {MissingParamError} If `filter` is `null` or `undefined`.
     */
    searchCustomers(filter, options = {}) {
        if (filter == null) {
            throw new utils_1.MissingParamError('filter');
        }
        const query = (0, utils_1.buildQueryString)({ start: options.start, limit: options.limit });
        return this.request.post(`${this.apiRoot}/customers${query}`, { filter });
    }
    /**
     * Look up attributes and devices for a set of people in one request.
     *
     * @param ids The identifiers of the people to look up (non-empty).
     * @returns The parsed JSON response body (`{ customers: [...] }`).
     * @throws {MissingParamError} If `ids` is not a non-empty array.
     */
    getCustomersAttributes(ids) {
        if (!Array.isArray(ids) || ids.length === 0) {
            throw new utils_1.MissingParamError('ids');
        }
        return this.request.post(`${this.apiRoot}/customers/attributes`, { ids });
    }
    /**
     * Get an object's attributes.
     *
     * @param objectTypeId The object type's numeric id.
     * @param objectId The object's identifier value.
     * @param idType Which identifier kind `objectId` is (`object_id` or `cio_object_id`).
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `objectTypeId` or `objectId` is empty.
     */
    getObjectAttributes(objectTypeId, objectId, idType) {
        if ((0, utils_1.isEmpty)(objectTypeId)) {
            throw new utils_1.MissingParamError('objectTypeId');
        }
        if ((0, utils_1.isEmpty)(objectId)) {
            throw new utils_1.MissingParamError('objectId');
        }
        if (idType !== undefined && !(0, utils_1.isObjectIdType)(idType)) {
            throw new Error('idType must be one of "object_id" or "cio_object_id"');
        }
        const query = (0, utils_1.buildQueryString)({ id_type: idType });
        return this.request.get(`${this.apiRoot}/objects/${encodeURIComponent(objectTypeId)}/${encodeURIComponent(objectId)}/attributes${query}`);
    }
    /**
     * Get an object's relationships to people.
     *
     * @param objectTypeId The object type's numeric id.
     * @param objectId The object's identifier value.
     * @param options Optional identifier kind and pagination. See {@link ObjectRelationshipsOptions}.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `objectTypeId` or `objectId` is empty.
     */
    getObjectRelationships(objectTypeId, objectId, options = {}) {
        if ((0, utils_1.isEmpty)(objectTypeId)) {
            throw new utils_1.MissingParamError('objectTypeId');
        }
        if ((0, utils_1.isEmpty)(objectId)) {
            throw new utils_1.MissingParamError('objectId');
        }
        if (options.idType !== undefined && !(0, utils_1.isObjectIdType)(options.idType)) {
            throw new Error('idType must be one of "object_id" or "cio_object_id"');
        }
        const query = (0, utils_1.buildQueryString)({ id_type: options.idType, start: options.start, limit: options.limit });
        return this.request.get(`${this.apiRoot}/objects/${encodeURIComponent(objectTypeId)}/${encodeURIComponent(objectId)}/relationships${query}`);
    }
    /**
     * Find objects of a given type matching a filter.
     *
     * @param objectTypeId The object type's numeric id.
     * @param filter An {@link ObjectFilter} expression — `object_attribute` leaf
     *   conditions composed with `and` / `or` / `not`.
     * @param options Optional pagination. See {@link PaginationOptions}.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `objectTypeId` is empty or `filter` is `null`/`undefined`.
     */
    findObjects(objectTypeId, filter, options = {}) {
        if ((0, utils_1.isEmpty)(objectTypeId)) {
            throw new utils_1.MissingParamError('objectTypeId');
        }
        if (filter == null) {
            throw new utils_1.MissingParamError('filter');
        }
        const query = (0, utils_1.buildQueryString)({ start: options.start, limit: options.limit });
        return this.request.post(`${this.apiRoot}/objects${query}`, { object_type_id: objectTypeId, filter });
    }
    /**
     * List the object types defined in your workspace.
     *
     * @returns The parsed JSON response body (`{ types: [...] }`).
     */
    listObjectTypes() {
        return this.request.get(`${this.apiRoot}/object_types`);
    }
    /**
     * List activities across your workspace.
     *
     * @param options Optional filters/pagination. See {@link ListActivitiesOptions}.
     * @returns The parsed JSON response body (`{ activities: [...], next }`).
     * @throws {Error} If `options.idType` is provided and is not a valid {@link IdentifierType}.
     */
    listActivities(options = {}) {
        if (options.idType !== undefined && !(0, utils_1.isIdentifierType)(options.idType)) {
            throw new Error('idType must be one of "id", "cio_id", or "email"');
        }
        const query = (0, utils_1.buildQueryString)({
            start: options.start,
            limit: options.limit,
            type: options.type,
            name: options.name,
            deleted: options.deleted,
            customer_id: options.customerId,
            id_type: options.idType,
        });
        return this.request.get(`${this.apiRoot}/activities${query}`);
    }
    /**
     * List the segments in your workspace.
     *
     * @returns The parsed JSON response body (`{ segments: [...] }`).
     */
    listSegments() {
        return this.request.get(`${this.apiRoot}/segments`);
    }
    /**
     * Create a manual segment.
     *
     * @param segment The segment definition. `name` is required. See {@link SegmentInput}.
     * @returns The parsed JSON response body (`{ segment: {...} }`).
     * @throws {MissingParamError} If `segment` is missing/not an object, or `segment.name` is empty.
     */
    createSegment(segment) {
        if (segment == null || typeof segment !== 'object') {
            throw new utils_1.MissingParamError('segment');
        }
        if ((0, utils_1.isEmpty)(segment.name)) {
            throw new utils_1.MissingParamError('segment.name');
        }
        return this.request.post(`${this.apiRoot}/segments`, { segment });
    }
    /**
     * Get a single segment's metadata.
     *
     * @param segmentId The segment's numeric id.
     * @returns The parsed JSON response body (`{ segment: {...} }`).
     * @throws {MissingParamError} If `segmentId` is empty.
     */
    getSegment(segmentId) {
        if ((0, utils_1.isEmpty)(segmentId)) {
            throw new utils_1.MissingParamError('segmentId');
        }
        return this.request.get(`${this.apiRoot}/segments/${encodeURIComponent(segmentId)}`);
    }
    /**
     * Delete a manual segment.
     *
     * @param segmentId The segment's numeric id.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `segmentId` is empty.
     */
    deleteSegment(segmentId) {
        if ((0, utils_1.isEmpty)(segmentId)) {
            throw new utils_1.MissingParamError('segmentId');
        }
        return this.request.destroy(`${this.apiRoot}/segments/${encodeURIComponent(segmentId)}`);
    }
    /**
     * Get the number of people in a segment.
     *
     * @param segmentId The segment's numeric id.
     * @returns The parsed JSON response body (`{ count, ... }`).
     * @throws {MissingParamError} If `segmentId` is empty.
     */
    getSegmentCustomerCount(segmentId) {
        if ((0, utils_1.isEmpty)(segmentId)) {
            throw new utils_1.MissingParamError('segmentId');
        }
        return this.request.get(`${this.apiRoot}/segments/${encodeURIComponent(segmentId)}/customer_count`);
    }
    /**
     * List the people who belong to a segment.
     *
     * @param segmentId The segment's numeric id.
     * @param options Optional pagination. See {@link PaginationOptions}.
     * @returns The parsed JSON response body (`{ ids: [...], identifiers: [...], next }`).
     * @throws {MissingParamError} If `segmentId` is empty.
     */
    getSegmentMembership(segmentId, options = {}) {
        if ((0, utils_1.isEmpty)(segmentId)) {
            throw new utils_1.MissingParamError('segmentId');
        }
        const query = (0, utils_1.buildQueryString)({ start: options.start, limit: options.limit });
        return this.request.get(`${this.apiRoot}/segments/${encodeURIComponent(segmentId)}/membership${query}`);
    }
    /**
     * Get the campaigns, newsletters, and other resources that use a segment.
     *
     * @param segmentId The segment's numeric id.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `segmentId` is empty.
     */
    getSegmentUsedBy(segmentId) {
        if ((0, utils_1.isEmpty)(segmentId)) {
            throw new utils_1.MissingParamError('segmentId');
        }
        return this.request.get(`${this.apiRoot}/segments/${encodeURIComponent(segmentId)}/used_by`);
    }
    /**
     * List the subscription topics defined in your workspace.
     *
     * @returns The parsed JSON response body (`{ topics: [...] }`).
     */
    listSubscriptionTopics() {
        return this.request.get(`${this.apiRoot}/subscription_topics`);
    }
    /**
     * List the subscription channels configured in your workspace.
     *
     * @returns The parsed JSON response body.
     */
    listSubscriptionChannels() {
        return this.request.get(`${this.apiRoot}/subscription_channels`);
    }
    /**
     * Generate a subscription center token for a person. The token authenticates
     * a hosted subscription-center link so the person can manage their preferences.
     *
     * @param customerId The person's identifier value.
     * @returns The parsed JSON response body (`{ token }`).
     * @throws {MissingParamError} If `customerId` is empty.
     */
    getSubscriptionCenterToken(customerId) {
        if ((0, utils_1.isEmpty)(customerId)) {
            throw new utils_1.MissingParamError('customerId');
        }
        return this.request.get(`${this.apiRoot}/subscription_center/${encodeURIComponent(customerId)}/token`);
    }
    /**
     * List the transactional messages in your workspace.
     *
     * @returns The parsed JSON response body.
     */
    listTransactionalMessages() {
        return this.request.get(`${this.apiRoot}/transactional`);
    }
    /**
     * Get a single transactional message's metadata.
     *
     * @param transactionalId The transactional message's numeric id.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `transactionalId` is empty.
     */
    getTransactionalMessage(transactionalId) {
        if ((0, utils_1.isEmpty)(transactionalId)) {
            throw new utils_1.MissingParamError('transactionalId');
        }
        return this.request.get(`${this.apiRoot}/transactional/${encodeURIComponent(transactionalId)}`);
    }
    /**
     * List all content variants of a transactional message.
     *
     * @param transactionalId The transactional message's numeric id.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `transactionalId` is empty.
     */
    getTransactionalMessageContents(transactionalId) {
        if ((0, utils_1.isEmpty)(transactionalId)) {
            throw new utils_1.MissingParamError('transactionalId');
        }
        return this.request.get(`${this.apiRoot}/transactional/${encodeURIComponent(transactionalId)}/contents`);
    }
    /**
     * Get a single-language translation of a transactional message.
     *
     * @param transactionalId The transactional message's numeric id.
     * @param language The IETF language tag of the translation.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `transactionalId` or `language` is empty.
     */
    getTransactionalMessageLanguage(transactionalId, language) {
        if ((0, utils_1.isEmpty)(transactionalId)) {
            throw new utils_1.MissingParamError('transactionalId');
        }
        if ((0, utils_1.isEmpty)(language)) {
            throw new utils_1.MissingParamError('language');
        }
        return this.request.get(`${this.apiRoot}/transactional/${encodeURIComponent(transactionalId)}/language/${encodeURIComponent(language)}`);
    }
    /**
     * Update a single-language translation of a transactional message.
     *
     * @param transactionalId The transactional message's numeric id.
     * @param language The IETF language tag of the translation.
     * @param data The translation fields to update.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `transactionalId` or `language` is empty.
     */
    updateTransactionalMessageLanguage(transactionalId, language, data = {}) {
        if ((0, utils_1.isEmpty)(transactionalId)) {
            throw new utils_1.MissingParamError('transactionalId');
        }
        if ((0, utils_1.isEmpty)(language)) {
            throw new utils_1.MissingParamError('language');
        }
        return this.request.put(`${this.apiRoot}/transactional/${encodeURIComponent(transactionalId)}/language/${encodeURIComponent(language)}`, data);
    }
    /**
     * Get the individual deliveries (sends) of a transactional message.
     *
     * @param transactionalId The transactional message's numeric id.
     * @param options Optional filters/pagination. See {@link TransactionalDeliveriesOptions}.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `transactionalId` is empty.
     */
    getTransactionalMessageDeliveries(transactionalId, options = {}) {
        if ((0, utils_1.isEmpty)(transactionalId)) {
            throw new utils_1.MissingParamError('transactionalId');
        }
        const query = (0, utils_1.buildQueryString)({
            start: options.start,
            limit: options.limit,
            metric: options.metric,
            start_ts: options.start_ts,
            end_ts: options.end_ts,
            get_tracked_responses: options.get_tracked_responses,
        });
        return this.request.get(`${this.apiRoot}/transactional/${encodeURIComponent(transactionalId)}/messages${query}`);
    }
    /**
     * Get delivery metrics for a transactional message over time.
     *
     * @param transactionalId The transactional message's numeric id.
     * @param options Optional reporting window. See {@link TransactionalMetricsOptions}.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `transactionalId` is empty.
     */
    getTransactionalMessageMetrics(transactionalId, options = {}) {
        if ((0, utils_1.isEmpty)(transactionalId)) {
            throw new utils_1.MissingParamError('transactionalId');
        }
        const query = (0, utils_1.buildQueryString)({ period: options.period, steps: options.steps });
        return this.request.get(`${this.apiRoot}/transactional/${encodeURIComponent(transactionalId)}/metrics${query}`);
    }
    /**
     * Get link (click) metrics for a transactional message over time.
     *
     * @param transactionalId The transactional message's numeric id.
     * @param options Optional reporting window. See {@link TransactionalLinkMetricsOptions}.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `transactionalId` is empty.
     */
    getTransactionalMessageLinkMetrics(transactionalId, options = {}) {
        if ((0, utils_1.isEmpty)(transactionalId)) {
            throw new utils_1.MissingParamError('transactionalId');
        }
        const query = (0, utils_1.buildQueryString)({ period: options.period, steps: options.steps, unique: options.unique });
        return this.request.get(`${this.apiRoot}/transactional/${encodeURIComponent(transactionalId)}/metrics/links${query}`);
    }
    /**
     * Update a transactional message's content variant.
     *
     * @param transactionalId The transactional message's numeric id.
     * @param contentId The content variant's numeric id.
     * @param data The content fields to update.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `transactionalId` or `contentId` is empty.
     */
    updateTransactionalMessageContent(transactionalId, contentId, data = {}) {
        if ((0, utils_1.isEmpty)(transactionalId)) {
            throw new utils_1.MissingParamError('transactionalId');
        }
        if ((0, utils_1.isEmpty)(contentId)) {
            throw new utils_1.MissingParamError('contentId');
        }
        return this.request.put(`${this.apiRoot}/transactional/${encodeURIComponent(transactionalId)}/content/${encodeURIComponent(contentId)}`, data);
    }
    /**
     * Build the base URL for a campaign/broadcast resource. Shared by the
     * campaign and broadcast methods, which have identical sub-resource paths.
     */
    resourceBase(resource, id) {
        return `${this.apiRoot}/${resource}/${encodeURIComponent(id)}`;
    }
    /**
     * List the campaigns in your workspace.
     *
     * @returns The parsed JSON response body (`{ campaigns: [...] }`).
     */
    listCampaigns() {
        return this.request.get(`${this.apiRoot}/campaigns`);
    }
    /**
     * Get a single campaign's metadata.
     *
     * @param campaignId The campaign's numeric id.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `campaignId` is empty.
     */
    getCampaign(campaignId) {
        if ((0, utils_1.isEmpty)(campaignId)) {
            throw new utils_1.MissingParamError('campaignId');
        }
        return this.request.get(this.resourceBase('campaigns', campaignId));
    }
    /**
     * List a campaign's actions.
     *
     * @param campaignId The campaign's numeric id.
     * @param options Optional pagination cursor (`start`).
     * @returns The parsed JSON response body (`{ actions: [...], next }`).
     * @throws {MissingParamError} If `campaignId` is empty.
     */
    getCampaignActions(campaignId, options = {}) {
        if ((0, utils_1.isEmpty)(campaignId)) {
            throw new utils_1.MissingParamError('campaignId');
        }
        const query = (0, utils_1.buildQueryString)({ start: options.start });
        return this.request.get(`${this.resourceBase('campaigns', campaignId)}/actions${query}`);
    }
    /**
     * Get a single action of a campaign.
     *
     * @param campaignId The campaign's numeric id.
     * @param actionId The action's numeric id.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `campaignId` or `actionId` is empty.
     */
    getCampaignAction(campaignId, actionId) {
        if ((0, utils_1.isEmpty)(campaignId)) {
            throw new utils_1.MissingParamError('campaignId');
        }
        if ((0, utils_1.isEmpty)(actionId)) {
            throw new utils_1.MissingParamError('actionId');
        }
        return this.request.get(`${this.resourceBase('campaigns', campaignId)}/actions/${encodeURIComponent(actionId)}`);
    }
    /**
     * Update an action of a campaign (e.g. its message content).
     *
     * @param campaignId The campaign's numeric id.
     * @param actionId The action's numeric id.
     * @param data The action fields to update.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `campaignId` or `actionId` is empty.
     */
    updateCampaignAction(campaignId, actionId, data = {}) {
        if ((0, utils_1.isEmpty)(campaignId)) {
            throw new utils_1.MissingParamError('campaignId');
        }
        if ((0, utils_1.isEmpty)(actionId)) {
            throw new utils_1.MissingParamError('actionId');
        }
        return this.request.put(`${this.resourceBase('campaigns', campaignId)}/actions/${encodeURIComponent(actionId)}`, data);
    }
    /**
     * Get a single-language translation of a campaign action.
     *
     * @param campaignId The campaign's numeric id.
     * @param actionId The action's numeric id.
     * @param language The IETF language tag.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `campaignId`, `actionId`, or `language` is empty.
     */
    getCampaignActionLanguage(campaignId, actionId, language) {
        if ((0, utils_1.isEmpty)(campaignId)) {
            throw new utils_1.MissingParamError('campaignId');
        }
        if ((0, utils_1.isEmpty)(actionId)) {
            throw new utils_1.MissingParamError('actionId');
        }
        if ((0, utils_1.isEmpty)(language)) {
            throw new utils_1.MissingParamError('language');
        }
        return this.request.get(`${this.resourceBase('campaigns', campaignId)}/actions/${encodeURIComponent(actionId)}/language/${encodeURIComponent(language)}`);
    }
    /**
     * Update a single-language translation of a campaign action.
     *
     * @param campaignId The campaign's numeric id.
     * @param actionId The action's numeric id.
     * @param language The IETF language tag.
     * @param data The translation fields to update.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `campaignId`, `actionId`, or `language` is empty.
     */
    updateCampaignActionLanguage(campaignId, actionId, language, data = {}) {
        if ((0, utils_1.isEmpty)(campaignId)) {
            throw new utils_1.MissingParamError('campaignId');
        }
        if ((0, utils_1.isEmpty)(actionId)) {
            throw new utils_1.MissingParamError('actionId');
        }
        if ((0, utils_1.isEmpty)(language)) {
            throw new utils_1.MissingParamError('language');
        }
        return this.request.put(`${this.resourceBase('campaigns', campaignId)}/actions/${encodeURIComponent(actionId)}/language/${encodeURIComponent(language)}`, data);
    }
    /**
     * Get metrics for a single campaign action over time. Aggregated across all
     * channels (the API does not accept a `type` filter here).
     *
     * @param campaignId The campaign's numeric id.
     * @param actionId The action's numeric id.
     * @param options Optional reporting window/version. See {@link CampaignActionMetricsOptions}.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `campaignId` or `actionId` is empty.
     */
    getCampaignActionMetrics(campaignId, actionId, options = {}) {
        if ((0, utils_1.isEmpty)(campaignId)) {
            throw new utils_1.MissingParamError('campaignId');
        }
        if ((0, utils_1.isEmpty)(actionId)) {
            throw new utils_1.MissingParamError('actionId');
        }
        const query = (0, utils_1.buildQueryString)({
            version: options.version,
            res: options.res,
            tz: options.tz,
            start: options.start,
            end: options.end,
            period: options.period,
            steps: options.steps,
        });
        return this.request.get(`${this.resourceBase('campaigns', campaignId)}/actions/${encodeURIComponent(actionId)}/metrics${query}`);
    }
    /**
     * Get link (click) metrics for a single campaign action over time.
     *
     * @param campaignId The campaign's numeric id.
     * @param actionId The action's numeric id.
     * @param options Optional reporting window. See {@link LinkMetricsOptions}.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `campaignId` or `actionId` is empty.
     */
    getCampaignActionMetricsLinks(campaignId, actionId, options = {}) {
        if ((0, utils_1.isEmpty)(campaignId)) {
            throw new utils_1.MissingParamError('campaignId');
        }
        if ((0, utils_1.isEmpty)(actionId)) {
            throw new utils_1.MissingParamError('actionId');
        }
        const query = (0, utils_1.buildQueryString)({ period: options.period, steps: options.steps, unique: options.unique });
        return this.request.get(`${this.resourceBase('campaigns', campaignId)}/actions/${encodeURIComponent(actionId)}/metrics/links${query}`);
    }
    /**
     * Get delivery metrics for a campaign over time.
     *
     * @param campaignId The campaign's numeric id.
     * @param options Optional reporting window/filters. See {@link CampaignMetricsOptions}.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `campaignId` is empty.
     */
    getCampaignMetrics(campaignId, options = {}) {
        if ((0, utils_1.isEmpty)(campaignId)) {
            throw new utils_1.MissingParamError('campaignId');
        }
        const query = (0, utils_1.buildQueryString)({
            version: options.version,
            type: options.type,
            res: options.res,
            tz: options.tz,
            start: options.start,
            end: options.end,
            period: options.period,
            steps: options.steps,
        });
        return this.request.get(`${this.resourceBase('campaigns', campaignId)}/metrics${query}`);
    }
    /**
     * Get link (click) metrics for a campaign over time.
     *
     * @param campaignId The campaign's numeric id.
     * @param options Optional reporting window. See {@link LinkMetricsOptions}.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `campaignId` is empty.
     */
    getCampaignMetricsLinks(campaignId, options = {}) {
        if ((0, utils_1.isEmpty)(campaignId)) {
            throw new utils_1.MissingParamError('campaignId');
        }
        const query = (0, utils_1.buildQueryString)({ period: options.period, steps: options.steps, unique: options.unique });
        return this.request.get(`${this.resourceBase('campaigns', campaignId)}/metrics/links${query}`);
    }
    /**
     * Get a campaign's journey metrics (per-step conversion funnel) over a window.
     *
     * @param campaignId The campaign's numeric id.
     * @param options Required window and resolution. See {@link JourneyMetricsOptions}.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `campaignId`, `options.start`, `options.end`, or `options.res` is empty.
     */
    getCampaignJourneyMetrics(campaignId, options) {
        if ((0, utils_1.isEmpty)(campaignId)) {
            throw new utils_1.MissingParamError('campaignId');
        }
        if (options == null || (0, utils_1.isEmpty)(options.start)) {
            throw new utils_1.MissingParamError('options.start');
        }
        if ((0, utils_1.isEmpty)(options.end)) {
            throw new utils_1.MissingParamError('options.end');
        }
        if ((0, utils_1.isEmpty)(options.res)) {
            throw new utils_1.MissingParamError('options.res');
        }
        const query = (0, utils_1.buildQueryString)({ start: options.start, end: options.end, resolution: options.res });
        return this.request.get(`${this.resourceBase('campaigns', campaignId)}/journey_metrics${query}`);
    }
    /**
     * Get the individual messages (deliveries) sent by a campaign.
     *
     * @param campaignId The campaign's numeric id.
     * @param options Optional filters/pagination. See {@link CampaignMessagesOptions}.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `campaignId` is empty.
     */
    getCampaignMessages(campaignId, options = {}) {
        if ((0, utils_1.isEmpty)(campaignId)) {
            throw new utils_1.MissingParamError('campaignId');
        }
        const query = (0, utils_1.buildQueryString)({
            start: options.start,
            limit: options.limit,
            type: options.type,
            metric: options.metric,
            drafts: options.drafts,
            start_ts: options.start_ts,
            end_ts: options.end_ts,
            get_tracked_responses: options.get_tracked_responses,
        });
        return this.request.get(`${this.resourceBase('campaigns', campaignId)}/messages${query}`);
    }
    /**
     * Get the status of an API-triggered broadcast run. Pairs with {@link APIClient.triggerBroadcast}.
     *
     * @param broadcastId The broadcast (campaign) id.
     * @param triggerId The trigger id returned by `triggerBroadcast`.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `broadcastId` or `triggerId` is empty.
     */
    getBroadcastTriggerStatus(broadcastId, triggerId) {
        if ((0, utils_1.isEmpty)(broadcastId)) {
            throw new utils_1.MissingParamError('broadcastId');
        }
        if ((0, utils_1.isEmpty)(triggerId)) {
            throw new utils_1.MissingParamError('triggerId');
        }
        return this.request.get(`${this.apiRoot}/campaigns/${encodeURIComponent(broadcastId)}/triggers/${encodeURIComponent(triggerId)}`);
    }
    /**
     * Get the per-recipient errors for an API-triggered broadcast run.
     *
     * @param broadcastId The broadcast (campaign) id.
     * @param triggerId The trigger id returned by `triggerBroadcast`.
     * @param options Optional pagination. See {@link PaginationOptions}.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `broadcastId` or `triggerId` is empty.
     */
    getBroadcastTriggerErrors(broadcastId, triggerId, options = {}) {
        if ((0, utils_1.isEmpty)(broadcastId)) {
            throw new utils_1.MissingParamError('broadcastId');
        }
        if ((0, utils_1.isEmpty)(triggerId)) {
            throw new utils_1.MissingParamError('triggerId');
        }
        const query = (0, utils_1.buildQueryString)({ start: options.start, limit: options.limit });
        return this.request.get(`${this.apiRoot}/campaigns/${encodeURIComponent(broadcastId)}/triggers/${encodeURIComponent(triggerId)}/errors${query}`);
    }
    /**
     * List the broadcasts in your workspace.
     *
     * @returns The parsed JSON response body (`{ broadcasts: [...] }`).
     */
    listBroadcasts() {
        return this.request.get(`${this.apiRoot}/broadcasts`);
    }
    /**
     * Get a single broadcast's metadata.
     *
     * @param broadcastId The broadcast's numeric id.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `broadcastId` is empty.
     */
    getBroadcast(broadcastId) {
        if ((0, utils_1.isEmpty)(broadcastId)) {
            throw new utils_1.MissingParamError('broadcastId');
        }
        return this.request.get(this.resourceBase('broadcasts', broadcastId));
    }
    /**
     * List a broadcast's actions.
     *
     * @param broadcastId The broadcast's numeric id.
     * @returns The parsed JSON response body (`{ actions: [...] }`).
     * @throws {MissingParamError} If `broadcastId` is empty.
     */
    getBroadcastActions(broadcastId) {
        if ((0, utils_1.isEmpty)(broadcastId)) {
            throw new utils_1.MissingParamError('broadcastId');
        }
        return this.request.get(`${this.resourceBase('broadcasts', broadcastId)}/actions`);
    }
    /**
     * Get a single action of a broadcast.
     *
     * @param broadcastId The broadcast's numeric id.
     * @param actionId The action's numeric id.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `broadcastId` or `actionId` is empty.
     */
    getBroadcastAction(broadcastId, actionId) {
        if ((0, utils_1.isEmpty)(broadcastId)) {
            throw new utils_1.MissingParamError('broadcastId');
        }
        if ((0, utils_1.isEmpty)(actionId)) {
            throw new utils_1.MissingParamError('actionId');
        }
        return this.request.get(`${this.resourceBase('broadcasts', broadcastId)}/actions/${encodeURIComponent(actionId)}`);
    }
    /**
     * Update an action of a broadcast (e.g. its message content).
     *
     * @param broadcastId The broadcast's numeric id.
     * @param actionId The action's numeric id.
     * @param data The action fields to update.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `broadcastId` or `actionId` is empty.
     */
    updateBroadcastAction(broadcastId, actionId, data = {}) {
        if ((0, utils_1.isEmpty)(broadcastId)) {
            throw new utils_1.MissingParamError('broadcastId');
        }
        if ((0, utils_1.isEmpty)(actionId)) {
            throw new utils_1.MissingParamError('actionId');
        }
        return this.request.put(`${this.resourceBase('broadcasts', broadcastId)}/actions/${encodeURIComponent(actionId)}`, data);
    }
    /**
     * Get a single-language translation of a broadcast action.
     *
     * @param broadcastId The broadcast's numeric id.
     * @param actionId The action's numeric id.
     * @param language The IETF language tag.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `broadcastId`, `actionId`, or `language` is empty.
     */
    getBroadcastActionLanguage(broadcastId, actionId, language) {
        if ((0, utils_1.isEmpty)(broadcastId)) {
            throw new utils_1.MissingParamError('broadcastId');
        }
        if ((0, utils_1.isEmpty)(actionId)) {
            throw new utils_1.MissingParamError('actionId');
        }
        if ((0, utils_1.isEmpty)(language)) {
            throw new utils_1.MissingParamError('language');
        }
        return this.request.get(`${this.resourceBase('broadcasts', broadcastId)}/actions/${encodeURIComponent(actionId)}/language/${encodeURIComponent(language)}`);
    }
    /**
     * Update a single-language translation of a broadcast action.
     *
     * @param broadcastId The broadcast's numeric id.
     * @param actionId The action's numeric id.
     * @param language The IETF language tag.
     * @param data The translation fields to update.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `broadcastId`, `actionId`, or `language` is empty.
     */
    updateBroadcastActionLanguage(broadcastId, actionId, language, data = {}) {
        if ((0, utils_1.isEmpty)(broadcastId)) {
            throw new utils_1.MissingParamError('broadcastId');
        }
        if ((0, utils_1.isEmpty)(actionId)) {
            throw new utils_1.MissingParamError('actionId');
        }
        if ((0, utils_1.isEmpty)(language)) {
            throw new utils_1.MissingParamError('language');
        }
        return this.request.put(`${this.resourceBase('broadcasts', broadcastId)}/actions/${encodeURIComponent(actionId)}/language/${encodeURIComponent(language)}`, data);
    }
    /**
     * Get metrics for a single broadcast action over time. Aggregated across all
     * channels (the API does not accept a `type` filter here).
     *
     * @param broadcastId The broadcast's numeric id.
     * @param actionId The action's numeric id.
     * @param options Optional reporting window. See {@link MetricsWindowOptions}.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `broadcastId` or `actionId` is empty.
     */
    getBroadcastActionMetrics(broadcastId, actionId, options = {}) {
        if ((0, utils_1.isEmpty)(broadcastId)) {
            throw new utils_1.MissingParamError('broadcastId');
        }
        if ((0, utils_1.isEmpty)(actionId)) {
            throw new utils_1.MissingParamError('actionId');
        }
        const query = (0, utils_1.buildQueryString)({ period: options.period, steps: options.steps });
        return this.request.get(`${this.resourceBase('broadcasts', broadcastId)}/actions/${encodeURIComponent(actionId)}/metrics${query}`);
    }
    /**
     * Get link (click) metrics for a single broadcast action over time.
     *
     * @param broadcastId The broadcast's numeric id.
     * @param actionId The action's numeric id.
     * @param options Optional reporting window. See {@link LinkMetricsOptions}.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `broadcastId` or `actionId` is empty.
     */
    getBroadcastActionMetricsLinks(broadcastId, actionId, options = {}) {
        if ((0, utils_1.isEmpty)(broadcastId)) {
            throw new utils_1.MissingParamError('broadcastId');
        }
        if ((0, utils_1.isEmpty)(actionId)) {
            throw new utils_1.MissingParamError('actionId');
        }
        const query = (0, utils_1.buildQueryString)({ period: options.period, steps: options.steps, unique: options.unique });
        return this.request.get(`${this.resourceBase('broadcasts', broadcastId)}/actions/${encodeURIComponent(actionId)}/metrics/links${query}`);
    }
    /**
     * Get delivery metrics for a broadcast over time.
     *
     * @param broadcastId The broadcast's numeric id.
     * @param options Optional reporting window/filters. See {@link MetricsOptions}.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `broadcastId` is empty.
     */
    getBroadcastMetrics(broadcastId, options = {}) {
        if ((0, utils_1.isEmpty)(broadcastId)) {
            throw new utils_1.MissingParamError('broadcastId');
        }
        const query = (0, utils_1.buildQueryString)({ period: options.period, steps: options.steps, type: options.type });
        return this.request.get(`${this.resourceBase('broadcasts', broadcastId)}/metrics${query}`);
    }
    /**
     * Get link (click) metrics for a broadcast over time.
     *
     * @param broadcastId The broadcast's numeric id.
     * @param options Optional reporting window. See {@link LinkMetricsOptions}.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `broadcastId` is empty.
     */
    getBroadcastMetricsLinks(broadcastId, options = {}) {
        if ((0, utils_1.isEmpty)(broadcastId)) {
            throw new utils_1.MissingParamError('broadcastId');
        }
        const query = (0, utils_1.buildQueryString)({ period: options.period, steps: options.steps, unique: options.unique });
        return this.request.get(`${this.resourceBase('broadcasts', broadcastId)}/metrics/links${query}`);
    }
    /**
     * Get the individual messages (deliveries) sent by a broadcast.
     *
     * @param broadcastId The broadcast's numeric id.
     * @param options Optional filters/pagination. See {@link BroadcastMessagesOptions}.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `broadcastId` is empty.
     */
    getBroadcastMessages(broadcastId, options = {}) {
        if ((0, utils_1.isEmpty)(broadcastId)) {
            throw new utils_1.MissingParamError('broadcastId');
        }
        const query = (0, utils_1.buildQueryString)({
            start: options.start,
            limit: options.limit,
            metric: options.metric,
            type: options.type,
            start_ts: options.start_ts,
            end_ts: options.end_ts,
            get_tracked_responses: options.get_tracked_responses,
        });
        return this.request.get(`${this.resourceBase('broadcasts', broadcastId)}/messages${query}`);
    }
    /**
     * List the API triggers fired for a broadcast.
     *
     * @param broadcastId The broadcast's numeric id.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `broadcastId` is empty.
     */
    getBroadcastTriggers(broadcastId) {
        if ((0, utils_1.isEmpty)(broadcastId)) {
            throw new utils_1.MissingParamError('broadcastId');
        }
        return this.request.get(`${this.resourceBase('broadcasts', broadcastId)}/triggers`);
    }
    /**
     * List the newsletters in your workspace.
     *
     * @param options Optional pagination and sort. See {@link ListNewslettersOptions}.
     * @returns The parsed JSON response body (`{ newsletters: [...], next }`).
     */
    listNewsletters(options = {}) {
        const query = (0, utils_1.buildQueryString)({ start: options.start, limit: options.limit, sort: options.sort });
        return this.request.get(`${this.apiRoot}/newsletters${query}`);
    }
    /**
     * Create a newsletter.
     *
     * @param data The newsletter definition.
     * @returns The parsed JSON response body.
     */
    createNewsletter(data = {}) {
        return this.request.post(`${this.apiRoot}/newsletters`, data);
    }
    /**
     * Get a single newsletter's metadata.
     *
     * @param newsletterId The newsletter's numeric id.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `newsletterId` is empty.
     */
    getNewsletter(newsletterId) {
        if ((0, utils_1.isEmpty)(newsletterId)) {
            throw new utils_1.MissingParamError('newsletterId');
        }
        return this.request.get(this.resourceBase('newsletters', newsletterId));
    }
    /**
     * Delete a newsletter.
     *
     * @param newsletterId The newsletter's numeric id.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `newsletterId` is empty.
     */
    deleteNewsletter(newsletterId) {
        if ((0, utils_1.isEmpty)(newsletterId)) {
            throw new utils_1.MissingParamError('newsletterId');
        }
        return this.request.destroy(this.resourceBase('newsletters', newsletterId));
    }
    /**
     * List all content variants of a newsletter.
     *
     * @param newsletterId The newsletter's numeric id.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `newsletterId` is empty.
     */
    getNewsletterContents(newsletterId) {
        if ((0, utils_1.isEmpty)(newsletterId)) {
            throw new utils_1.MissingParamError('newsletterId');
        }
        return this.request.get(`${this.resourceBase('newsletters', newsletterId)}/contents`);
    }
    /**
     * Get a single content variant of a newsletter.
     *
     * @param newsletterId The newsletter's numeric id.
     * @param contentId The content variant's numeric id.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `newsletterId` or `contentId` is empty.
     */
    getNewsletterContent(newsletterId, contentId) {
        if ((0, utils_1.isEmpty)(newsletterId)) {
            throw new utils_1.MissingParamError('newsletterId');
        }
        if ((0, utils_1.isEmpty)(contentId)) {
            throw new utils_1.MissingParamError('contentId');
        }
        return this.request.get(`${this.resourceBase('newsletters', newsletterId)}/contents/${encodeURIComponent(contentId)}`);
    }
    /**
     * Update a content variant of a newsletter.
     *
     * @param newsletterId The newsletter's numeric id.
     * @param contentId The content variant's numeric id.
     * @param data The content fields to update.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `newsletterId` or `contentId` is empty.
     */
    updateNewsletterContent(newsletterId, contentId, data = {}) {
        if ((0, utils_1.isEmpty)(newsletterId)) {
            throw new utils_1.MissingParamError('newsletterId');
        }
        if ((0, utils_1.isEmpty)(contentId)) {
            throw new utils_1.MissingParamError('contentId');
        }
        return this.request.put(`${this.resourceBase('newsletters', newsletterId)}/contents/${encodeURIComponent(contentId)}`, data);
    }
    /**
     * Get metrics for a single newsletter content variant over time.
     *
     * @param newsletterId The newsletter's numeric id.
     * @param contentId The content variant's numeric id.
     * @param options Optional reporting window/filters. See {@link NewsletterMetricsOptions}.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `newsletterId` or `contentId` is empty.
     */
    getNewsletterContentMetrics(newsletterId, contentId, options = {}) {
        if ((0, utils_1.isEmpty)(newsletterId)) {
            throw new utils_1.MissingParamError('newsletterId');
        }
        if ((0, utils_1.isEmpty)(contentId)) {
            throw new utils_1.MissingParamError('contentId');
        }
        const query = (0, utils_1.buildQueryString)({ period: options.period, steps: options.steps });
        return this.request.get(`${this.resourceBase('newsletters', newsletterId)}/contents/${encodeURIComponent(contentId)}/metrics${query}`);
    }
    /**
     * Get link (click) metrics for a single newsletter content variant over time.
     *
     * @param newsletterId The newsletter's numeric id.
     * @param contentId The content variant's numeric id.
     * @param options Optional reporting window. See {@link LinkMetricsOptions}.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `newsletterId` or `contentId` is empty.
     */
    getNewsletterContentMetricsLinks(newsletterId, contentId, options = {}) {
        if ((0, utils_1.isEmpty)(newsletterId)) {
            throw new utils_1.MissingParamError('newsletterId');
        }
        if ((0, utils_1.isEmpty)(contentId)) {
            throw new utils_1.MissingParamError('contentId');
        }
        const query = (0, utils_1.buildQueryString)({ period: options.period, steps: options.steps, unique: options.unique });
        return this.request.get(`${this.resourceBase('newsletters', newsletterId)}/contents/${encodeURIComponent(contentId)}/metrics/links${query}`);
    }
    /**
     * Get delivery metrics for a newsletter over time.
     *
     * @param newsletterId The newsletter's numeric id.
     * @param options Optional reporting window/filters. See {@link NewsletterMetricsOptions}.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `newsletterId` is empty.
     */
    getNewsletterMetrics(newsletterId, options = {}) {
        if ((0, utils_1.isEmpty)(newsletterId)) {
            throw new utils_1.MissingParamError('newsletterId');
        }
        const query = (0, utils_1.buildQueryString)({ period: options.period, steps: options.steps });
        return this.request.get(`${this.resourceBase('newsletters', newsletterId)}/metrics${query}`);
    }
    /**
     * Get link (click) metrics for a newsletter over time.
     *
     * @param newsletterId The newsletter's numeric id.
     * @param options Optional reporting window. See {@link LinkMetricsOptions}.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `newsletterId` is empty.
     */
    getNewsletterMetricsLinks(newsletterId, options = {}) {
        if ((0, utils_1.isEmpty)(newsletterId)) {
            throw new utils_1.MissingParamError('newsletterId');
        }
        const query = (0, utils_1.buildQueryString)({ period: options.period, steps: options.steps, unique: options.unique });
        return this.request.get(`${this.resourceBase('newsletters', newsletterId)}/metrics/links${query}`);
    }
    /**
     * Get the individual messages (deliveries) sent by a newsletter.
     *
     * @param newsletterId The newsletter's numeric id.
     * @param options Optional filters/pagination. See {@link NewsletterMessagesOptions}.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `newsletterId` is empty.
     */
    getNewsletterMessages(newsletterId, options = {}) {
        if ((0, utils_1.isEmpty)(newsletterId)) {
            throw new utils_1.MissingParamError('newsletterId');
        }
        const query = (0, utils_1.buildQueryString)({
            start: options.start,
            limit: options.limit,
            metric: options.metric,
            type: options.type,
            start_ts: options.start_ts,
            end_ts: options.end_ts,
            get_tracked_responses: options.get_tracked_responses,
        });
        return this.request.get(`${this.resourceBase('newsletters', newsletterId)}/messages${query}`);
    }
    /**
     * Send a newsletter.
     *
     * @param newsletterId The newsletter's numeric id.
     * @param data Optional send settings — e.g. `rate_limit_email_rate`,
     *   `rate_limit_time_period`, `rate_limit_spread`.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `newsletterId` is empty.
     */
    sendNewsletter(newsletterId, data = {}) {
        if ((0, utils_1.isEmpty)(newsletterId)) {
            throw new utils_1.MissingParamError('newsletterId');
        }
        return this.request.post(`${this.resourceBase('newsletters', newsletterId)}/send`, data);
    }
    /**
     * Schedule a newsletter to send later.
     *
     * @param newsletterId The newsletter's numeric id.
     * @param data The schedule settings.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `newsletterId` is empty.
     */
    scheduleNewsletter(newsletterId, data = {}) {
        if ((0, utils_1.isEmpty)(newsletterId)) {
            throw new utils_1.MissingParamError('newsletterId');
        }
        return this.request.post(`${this.resourceBase('newsletters', newsletterId)}/schedule`, data);
    }
    /**
     * Add a language (translation) to a newsletter.
     *
     * @param newsletterId The newsletter's numeric id.
     * @param data The translation content, including its `language` tag.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `newsletterId` is empty.
     */
    createNewsletterLanguage(newsletterId, data = {}) {
        if ((0, utils_1.isEmpty)(newsletterId)) {
            throw new utils_1.MissingParamError('newsletterId');
        }
        return this.request.post(`${this.resourceBase('newsletters', newsletterId)}/language`, data);
    }
    /**
     * Get a single-language translation of a newsletter.
     *
     * @param newsletterId The newsletter's numeric id.
     * @param language The IETF language tag.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `newsletterId` or `language` is empty.
     */
    getNewsletterLanguage(newsletterId, language) {
        if ((0, utils_1.isEmpty)(newsletterId)) {
            throw new utils_1.MissingParamError('newsletterId');
        }
        if ((0, utils_1.isEmpty)(language)) {
            throw new utils_1.MissingParamError('language');
        }
        return this.request.get(`${this.resourceBase('newsletters', newsletterId)}/language/${encodeURIComponent(language)}`);
    }
    /**
     * Update a single-language translation of a newsletter.
     *
     * @param newsletterId The newsletter's numeric id.
     * @param language The IETF language tag.
     * @param data The translation fields to update.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `newsletterId` or `language` is empty.
     */
    updateNewsletterLanguage(newsletterId, language, data = {}) {
        if ((0, utils_1.isEmpty)(newsletterId)) {
            throw new utils_1.MissingParamError('newsletterId');
        }
        if ((0, utils_1.isEmpty)(language)) {
            throw new utils_1.MissingParamError('language');
        }
        return this.request.put(`${this.resourceBase('newsletters', newsletterId)}/language/${encodeURIComponent(language)}`, data);
    }
    /**
     * Delete a single-language translation of a newsletter.
     *
     * @param newsletterId The newsletter's numeric id.
     * @param language The IETF language tag.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `newsletterId` or `language` is empty.
     */
    deleteNewsletterLanguage(newsletterId, language) {
        if ((0, utils_1.isEmpty)(newsletterId)) {
            throw new utils_1.MissingParamError('newsletterId');
        }
        if ((0, utils_1.isEmpty)(language)) {
            throw new utils_1.MissingParamError('language');
        }
        return this.request.destroy(`${this.resourceBase('newsletters', newsletterId)}/language/${encodeURIComponent(language)}`);
    }
    /**
     * List a newsletter's A/B test groups.
     *
     * @param newsletterId The newsletter's numeric id.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `newsletterId` is empty.
     */
    getNewsletterTestGroups(newsletterId) {
        if ((0, utils_1.isEmpty)(newsletterId)) {
            throw new utils_1.MissingParamError('newsletterId');
        }
        return this.request.get(`${this.resourceBase('newsletters', newsletterId)}/test_groups`);
    }
    /**
     * Create an A/B test group on a newsletter. The API takes no request body —
     * a new empty test group is created and returned.
     *
     * @param newsletterId The newsletter's numeric id.
     * @returns The parsed JSON response body (the updated newsletter).
     * @throws {MissingParamError} If `newsletterId` is empty.
     */
    createNewsletterTestGroup(newsletterId) {
        if ((0, utils_1.isEmpty)(newsletterId)) {
            throw new utils_1.MissingParamError('newsletterId');
        }
        return this.request.post(`${this.resourceBase('newsletters', newsletterId)}/test_groups`);
    }
    /**
     * Add a language (translation) to a newsletter test group.
     *
     * @param newsletterId The newsletter's numeric id.
     * @param testGroupId The test group's id.
     * @param data The translation content, including its `language` tag.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `newsletterId` or `testGroupId` is empty.
     */
    createNewsletterTestGroupLanguage(newsletterId, testGroupId, data = {}) {
        if ((0, utils_1.isEmpty)(newsletterId)) {
            throw new utils_1.MissingParamError('newsletterId');
        }
        if ((0, utils_1.isEmpty)(testGroupId)) {
            throw new utils_1.MissingParamError('testGroupId');
        }
        return this.request.post(`${this.resourceBase('newsletters', newsletterId)}/test_group/${encodeURIComponent(testGroupId)}/language`, data);
    }
    /**
     * Get a single-language translation of a newsletter test group.
     *
     * @param newsletterId The newsletter's numeric id.
     * @param testGroupId The test group's id.
     * @param language The IETF language tag.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `newsletterId`, `testGroupId`, or `language` is empty.
     */
    getNewsletterTestGroupLanguage(newsletterId, testGroupId, language) {
        if ((0, utils_1.isEmpty)(newsletterId)) {
            throw new utils_1.MissingParamError('newsletterId');
        }
        if ((0, utils_1.isEmpty)(testGroupId)) {
            throw new utils_1.MissingParamError('testGroupId');
        }
        if ((0, utils_1.isEmpty)(language)) {
            throw new utils_1.MissingParamError('language');
        }
        return this.request.get(`${this.resourceBase('newsletters', newsletterId)}/test_group/${encodeURIComponent(testGroupId)}/language/${encodeURIComponent(language)}`);
    }
    /**
     * Update a single-language translation of a newsletter test group.
     *
     * @param newsletterId The newsletter's numeric id.
     * @param testGroupId The test group's id.
     * @param language The IETF language tag.
     * @param data The translation fields to update.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `newsletterId`, `testGroupId`, or `language` is empty.
     */
    updateNewsletterTestGroupLanguage(newsletterId, testGroupId, language, data = {}) {
        if ((0, utils_1.isEmpty)(newsletterId)) {
            throw new utils_1.MissingParamError('newsletterId');
        }
        if ((0, utils_1.isEmpty)(testGroupId)) {
            throw new utils_1.MissingParamError('testGroupId');
        }
        if ((0, utils_1.isEmpty)(language)) {
            throw new utils_1.MissingParamError('language');
        }
        return this.request.put(`${this.resourceBase('newsletters', newsletterId)}/test_group/${encodeURIComponent(testGroupId)}/language/${encodeURIComponent(language)}`, data);
    }
    /**
     * Delete a single-language translation of a newsletter test group.
     *
     * @param newsletterId The newsletter's numeric id.
     * @param testGroupId The test group's id.
     * @param language The IETF language tag.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `newsletterId`, `testGroupId`, or `language` is empty.
     */
    deleteNewsletterTestGroupLanguage(newsletterId, testGroupId, language) {
        if ((0, utils_1.isEmpty)(newsletterId)) {
            throw new utils_1.MissingParamError('newsletterId');
        }
        if ((0, utils_1.isEmpty)(testGroupId)) {
            throw new utils_1.MissingParamError('testGroupId');
        }
        if ((0, utils_1.isEmpty)(language)) {
            throw new utils_1.MissingParamError('language');
        }
        return this.request.destroy(`${this.resourceBase('newsletters', newsletterId)}/test_group/${encodeURIComponent(testGroupId)}/language/${encodeURIComponent(language)}`);
    }
    /**
     * List Design Studio folders.
     *
     * @param options Optional filters, sorting, and pagination. See {@link DesignStudioListOptions}.
     * @returns The parsed JSON response body (`{ folders: [...], meta }`).
     */
    listDesignStudioFolders(options = {}) {
        const query = (0, utils_1.buildQueryString)({
            parent_folder_id: options.parentFolderId,
            direct_descendants_only: options.directDescendantsOnly,
            sort_by: options.sortBy,
            sort_order: options.sortOrder,
            created_before: options.createdBefore,
            created_after: options.createdAfter,
            updated_before: options.updatedBefore,
            updated_after: options.updatedAfter,
            page: options.page,
            limit: options.limit,
        });
        return this.request.get(`${this.apiRoot}/design_studio/folders${query}`);
    }
    /**
     * Create a Design Studio folder.
     *
     * @param folder The folder definition. `name` is required. See {@link DesignStudioFolderInput}.
     * @returns The parsed JSON response body (`{ folder: {...} }`).
     * @throws {MissingParamError} If `folder` is missing/not an object, or `folder.name` is empty.
     */
    createDesignStudioFolder(folder) {
        if (folder == null || typeof folder !== 'object') {
            throw new utils_1.MissingParamError('folder');
        }
        if ((0, utils_1.isEmpty)(folder.name)) {
            throw new utils_1.MissingParamError('folder.name');
        }
        return this.request.post(`${this.apiRoot}/design_studio/folders`, folder);
    }
    /**
     * Get a single Design Studio folder.
     *
     * @param folderId The folder's UUID.
     * @returns The parsed JSON response body (`{ folder: {...} }`).
     * @throws {MissingParamError} If `folderId` is empty.
     */
    getDesignStudioFolder(folderId) {
        if ((0, utils_1.isEmpty)(folderId)) {
            throw new utils_1.MissingParamError('folderId');
        }
        return this.request.get(`${this.apiRoot}/design_studio/folders/${encodeURIComponent(folderId)}`);
    }
    /**
     * Update a Design Studio folder. At least one field must be provided.
     *
     * @param folderId The folder's UUID.
     * @param updates The fields to change. See {@link DesignStudioFolderUpdate}.
     * @returns The parsed JSON response body (empty on success — the API returns 204).
     * @throws {MissingParamError} If `folderId` is empty or `updates` is missing/not an object.
     */
    updateDesignStudioFolder(folderId, updates) {
        if ((0, utils_1.isEmpty)(folderId)) {
            throw new utils_1.MissingParamError('folderId');
        }
        if (updates == null || typeof updates !== 'object') {
            throw new utils_1.MissingParamError('updates');
        }
        return this.request.put(`${this.apiRoot}/design_studio/folders/${encodeURIComponent(folderId)}`, updates);
    }
    /**
     * Delete a Design Studio folder.
     *
     * @param folderId The folder's UUID.
     * @returns The parsed JSON response body (empty on success — the API returns 204).
     * @throws {MissingParamError} If `folderId` is empty.
     */
    deleteDesignStudioFolder(folderId) {
        if ((0, utils_1.isEmpty)(folderId)) {
            throw new utils_1.MissingParamError('folderId');
        }
        return this.request.destroy(`${this.apiRoot}/design_studio/folders/${encodeURIComponent(folderId)}`);
    }
    /**
     * List Design Studio emails.
     *
     * @param options Optional filters, sorting, and pagination. See {@link ListDesignStudioEmailsOptions}.
     * @returns The parsed JSON response body (`{ emails: [...], folders: [...], meta }`).
     */
    listDesignStudioEmails(options = {}) {
        const query = (0, utils_1.buildQueryString)({
            parent_folder_id: options.parentFolderId,
            direct_descendants_only: options.directDescendantsOnly,
            sort_by: options.sortBy,
            sort_order: options.sortOrder,
            created_before: options.createdBefore,
            created_after: options.createdAfter,
            updated_before: options.updatedBefore,
            updated_after: options.updatedAfter,
            page: options.page,
            limit: options.limit,
            is_template: options.isTemplate,
            has_translations: options.hasTranslations,
            is_linked: options.isLinked,
        });
        return this.request.get(`${this.apiRoot}/design_studio/emails${query}`);
    }
    /**
     * Create a Design Studio email.
     *
     * @param email The email definition. `name` is required. See {@link DesignStudioEmailInput}.
     * @returns The parsed JSON response body (`{ email: {...} }`).
     * @throws {MissingParamError} If `email` is missing/not an object, or `email.name` is empty.
     */
    createDesignStudioEmail(email) {
        if (email == null || typeof email !== 'object') {
            throw new utils_1.MissingParamError('email');
        }
        if ((0, utils_1.isEmpty)(email.name)) {
            throw new utils_1.MissingParamError('email.name');
        }
        return this.request.post(`${this.apiRoot}/design_studio/emails`, email);
    }
    /**
     * Get a single Design Studio email.
     *
     * @param emailId The email's UUID.
     * @returns The parsed JSON response body (`{ email: {...} }`).
     * @throws {MissingParamError} If `emailId` is empty.
     */
    getDesignStudioEmail(emailId) {
        if ((0, utils_1.isEmpty)(emailId)) {
            throw new utils_1.MissingParamError('emailId');
        }
        return this.request.get(`${this.apiRoot}/design_studio/emails/${encodeURIComponent(emailId)}`);
    }
    /**
     * Update a Design Studio email. At least one field must be provided.
     *
     * @param emailId The email's UUID.
     * @param updates The fields to change. See {@link DesignStudioEmailUpdate}.
     * @returns The parsed JSON response body (empty on success — the API returns 204).
     * @throws {MissingParamError} If `emailId` is empty or `updates` is missing/not an object.
     */
    updateDesignStudioEmail(emailId, updates) {
        if ((0, utils_1.isEmpty)(emailId)) {
            throw new utils_1.MissingParamError('emailId');
        }
        if (updates == null || typeof updates !== 'object') {
            throw new utils_1.MissingParamError('updates');
        }
        return this.request.put(`${this.apiRoot}/design_studio/emails/${encodeURIComponent(emailId)}`, updates);
    }
    /**
     * Delete a Design Studio email.
     *
     * @param emailId The email's UUID.
     * @returns The parsed JSON response body (empty on success — the API returns 204).
     * @throws {MissingParamError} If `emailId` is empty.
     */
    deleteDesignStudioEmail(emailId) {
        if ((0, utils_1.isEmpty)(emailId)) {
            throw new utils_1.MissingParamError('emailId');
        }
        return this.request.destroy(`${this.apiRoot}/design_studio/emails/${encodeURIComponent(emailId)}`);
    }
    /**
     * List the translations (languages) of a Design Studio email.
     *
     * @param emailId The email's UUID.
     * @returns The parsed JSON response body (`{ email_translations: [...] }`).
     * @throws {MissingParamError} If `emailId` is empty.
     */
    listDesignStudioEmailLanguages(emailId) {
        if ((0, utils_1.isEmpty)(emailId)) {
            throw new utils_1.MissingParamError('emailId');
        }
        return this.request.get(`${this.apiRoot}/design_studio/emails/${encodeURIComponent(emailId)}/languages`);
    }
    /**
     * Create a translation of a Design Studio email. Content blocks you omit are
     * inherited from the default-language email.
     *
     * @param emailId The email's UUID.
     * @param translation The translation definition. `language` is required. See {@link DesignStudioEmailTranslationInput}.
     * @returns The parsed JSON response body (`{ email_translation: {...} }`).
     * @throws {MissingParamError} If `emailId` is empty, `translation` is missing/not an object, or `translation.language` is empty.
     */
    createDesignStudioEmailLanguage(emailId, translation) {
        if ((0, utils_1.isEmpty)(emailId)) {
            throw new utils_1.MissingParamError('emailId');
        }
        if (translation == null || typeof translation !== 'object') {
            throw new utils_1.MissingParamError('translation');
        }
        if ((0, utils_1.isEmpty)(translation.language)) {
            throw new utils_1.MissingParamError('translation.language');
        }
        return this.request.post(`${this.apiRoot}/design_studio/emails/${encodeURIComponent(emailId)}/languages`, translation);
    }
    /**
     * Get a single-language translation of a Design Studio email.
     *
     * @param emailId The email's UUID.
     * @param language The IETF language tag.
     * @returns The parsed JSON response body (`{ email_translation: {...} }`).
     * @throws {MissingParamError} If `emailId` or `language` is empty.
     */
    getDesignStudioEmailLanguage(emailId, language) {
        if ((0, utils_1.isEmpty)(emailId)) {
            throw new utils_1.MissingParamError('emailId');
        }
        if ((0, utils_1.isEmpty)(language)) {
            throw new utils_1.MissingParamError('language');
        }
        return this.request.get(`${this.apiRoot}/design_studio/emails/${encodeURIComponent(emailId)}/languages/${encodeURIComponent(language)}`);
    }
    /**
     * Update a single-language translation of a Design Studio email. At least one field must be provided.
     *
     * @param emailId The email's UUID.
     * @param language The IETF language tag.
     * @param updates The fields to change. See {@link DesignStudioEmailTranslationUpdate}.
     * @returns The parsed JSON response body (empty on success — the API returns 204).
     * @throws {MissingParamError} If `emailId` or `language` is empty, or `updates` is missing/not an object.
     */
    updateDesignStudioEmailLanguage(emailId, language, updates) {
        if ((0, utils_1.isEmpty)(emailId)) {
            throw new utils_1.MissingParamError('emailId');
        }
        if ((0, utils_1.isEmpty)(language)) {
            throw new utils_1.MissingParamError('language');
        }
        if (updates == null || typeof updates !== 'object') {
            throw new utils_1.MissingParamError('updates');
        }
        return this.request.put(`${this.apiRoot}/design_studio/emails/${encodeURIComponent(emailId)}/languages/${encodeURIComponent(language)}`, updates);
    }
    /**
     * Delete a single-language translation of a Design Studio email.
     *
     * @param emailId The email's UUID.
     * @param language The IETF language tag.
     * @returns The parsed JSON response body (empty on success — the API returns 204).
     * @throws {MissingParamError} If `emailId` or `language` is empty.
     */
    deleteDesignStudioEmailLanguage(emailId, language) {
        if ((0, utils_1.isEmpty)(emailId)) {
            throw new utils_1.MissingParamError('emailId');
        }
        if ((0, utils_1.isEmpty)(language)) {
            throw new utils_1.MissingParamError('language');
        }
        return this.request.destroy(`${this.apiRoot}/design_studio/emails/${encodeURIComponent(emailId)}/languages/${encodeURIComponent(language)}`);
    }
    /**
     * List Design Studio components.
     *
     * @param options Optional filters, sorting, and pagination. See {@link ListDesignStudioComponentsOptions}.
     * @returns The parsed JSON response body (`{ components: [...], folders: [...], meta }`).
     */
    listDesignStudioComponents(options = {}) {
        const query = (0, utils_1.buildQueryString)({
            parent_folder_id: options.parentFolderId,
            direct_descendants_only: options.directDescendantsOnly,
            sort_by: options.sortBy,
            sort_order: options.sortOrder,
            created_before: options.createdBefore,
            created_after: options.createdAfter,
            updated_before: options.updatedBefore,
            updated_after: options.updatedAfter,
            page: options.page,
            limit: options.limit,
            tag: options.tag,
        });
        return this.request.get(`${this.apiRoot}/design_studio/components${query}`);
    }
    /**
     * Create a Design Studio component.
     *
     * @param component The component definition. `name` and `tag` are required. See {@link DesignStudioComponentInput}.
     * @returns The parsed JSON response body (`{ component: {...} }`).
     * @throws {MissingParamError} If `component` is missing/not an object, or `component.name`/`component.tag` is empty.
     */
    createDesignStudioComponent(component) {
        if (component == null || typeof component !== 'object') {
            throw new utils_1.MissingParamError('component');
        }
        if ((0, utils_1.isEmpty)(component.name)) {
            throw new utils_1.MissingParamError('component.name');
        }
        if ((0, utils_1.isEmpty)(component.tag)) {
            throw new utils_1.MissingParamError('component.tag');
        }
        return this.request.post(`${this.apiRoot}/design_studio/components`, component);
    }
    /**
     * Get a single Design Studio component.
     *
     * @param componentId The component's UUID.
     * @returns The parsed JSON response body (`{ component: {...} }`).
     * @throws {MissingParamError} If `componentId` is empty.
     */
    getDesignStudioComponent(componentId) {
        if ((0, utils_1.isEmpty)(componentId)) {
            throw new utils_1.MissingParamError('componentId');
        }
        return this.request.get(`${this.apiRoot}/design_studio/components/${encodeURIComponent(componentId)}`);
    }
    /**
     * Update a Design Studio component. At least one field must be provided.
     *
     * @param componentId The component's UUID.
     * @param updates The fields to change. See {@link DesignStudioComponentUpdate}.
     * @returns The parsed JSON response body (empty on success — the API returns 204).
     * @throws {MissingParamError} If `componentId` is empty or `updates` is missing/not an object.
     */
    updateDesignStudioComponent(componentId, updates) {
        if ((0, utils_1.isEmpty)(componentId)) {
            throw new utils_1.MissingParamError('componentId');
        }
        if (updates == null || typeof updates !== 'object') {
            throw new utils_1.MissingParamError('updates');
        }
        return this.request.put(`${this.apiRoot}/design_studio/components/${encodeURIComponent(componentId)}`, updates);
    }
    /**
     * Delete a Design Studio component.
     *
     * @param componentId The component's UUID.
     * @returns The parsed JSON response body (empty on success — the API returns 204).
     * @throws {MissingParamError} If `componentId` is empty.
     */
    deleteDesignStudioComponent(componentId) {
        if ((0, utils_1.isEmpty)(componentId)) {
            throw new utils_1.MissingParamError('componentId');
        }
        return this.request.destroy(`${this.apiRoot}/design_studio/components/${encodeURIComponent(componentId)}`);
    }
    /**
     * List uploaded assets (files).
     *
     * @param options Optional folder filter and pagination. See {@link AssetListOptions}.
     * @returns The parsed JSON response body (`{ assets: [...], meta }`).
     */
    listAssets(options = {}) {
        const query = (0, utils_1.buildQueryString)({
            parent_folder_id: options.parentFolderId,
            direct_descendants_only: options.directDescendantsOnly,
            page: options.page,
            limit: options.limit,
        });
        return this.request.get(`${this.apiRoot}/assets${query}`);
    }
    /**
     * Upload a file asset (`multipart/form-data`).
     *
     * @param file The file to upload. `data` and `filename` are required. See {@link CreateAssetInput}.
     * @returns The parsed JSON response body (`{ asset: {...} }`).
     * @throws {MissingParamError} If `file` is missing/not an object, or `file.data`/`file.filename` is missing.
     */
    createAsset(file) {
        if (file == null || typeof file !== 'object') {
            throw new utils_1.MissingParamError('file');
        }
        if (file.data == null) {
            throw new utils_1.MissingParamError('file.data');
        }
        if ((0, utils_1.isEmpty)(file.filename)) {
            throw new utils_1.MissingParamError('file.filename');
        }
        // Set the part's Content-Type explicitly. An untyped Blob is serialized as
        // `application/octet-stream`, which the API rejects, so resolve a type from
        // (in order) an explicit `contentType`, the filename extension, or the type
        // already on `data` when it is a Blob. An empty-string `contentType` is
        // treated as absent so it still falls through to derivation.
        const explicitType = (0, utils_1.isEmpty)(file.contentType) ? undefined : file.contentType;
        const contentType = explicitType ??
            assetContentTypeForFilename(file.filename) ??
            (file.data instanceof Blob && file.data.type ? file.data.type : undefined);
        const form = new FormData();
        const blob = contentType ? new Blob([file.data], { type: contentType }) : new Blob([file.data]);
        form.append('file', blob, file.filename);
        if (file.name !== undefined) {
            form.append('name', file.name);
        }
        if (file.parentFolderId !== undefined) {
            form.append('parent_folder_id', String(file.parentFolderId));
        }
        return this.request.postForm(`${this.apiRoot}/assets/files`, form);
    }
    /**
     * Get a single asset (file).
     *
     * @param assetId The asset's numeric id.
     * @returns The parsed JSON response body (`{ asset: {...} }`).
     * @throws {MissingParamError} If `assetId` is empty.
     */
    getAsset(assetId) {
        if ((0, utils_1.isEmpty)(assetId)) {
            throw new utils_1.MissingParamError('assetId');
        }
        return this.request.get(`${this.apiRoot}/assets/files/${encodeURIComponent(assetId)}`);
    }
    /**
     * Update an asset's name and/or parent folder. At least one field must be provided;
     * the file bytes cannot be changed.
     *
     * @param assetId The asset's numeric id.
     * @param updates The fields to change. See {@link AssetUpdate}.
     * @returns The parsed JSON response body (empty on success — the API returns 204).
     * @throws {MissingParamError} If `assetId` is empty or `updates` is missing/not an object.
     */
    updateAsset(assetId, updates) {
        if ((0, utils_1.isEmpty)(assetId)) {
            throw new utils_1.MissingParamError('assetId');
        }
        if (updates == null || typeof updates !== 'object') {
            throw new utils_1.MissingParamError('updates');
        }
        return this.request.put(`${this.apiRoot}/assets/files/${encodeURIComponent(assetId)}`, updates);
    }
    /**
     * Delete an asset (file).
     *
     * @param assetId The asset's numeric id.
     * @returns The parsed JSON response body (empty on success — the API returns 204).
     * @throws {MissingParamError} If `assetId` is empty.
     */
    deleteAsset(assetId) {
        if ((0, utils_1.isEmpty)(assetId)) {
            throw new utils_1.MissingParamError('assetId');
        }
        return this.request.destroy(`${this.apiRoot}/assets/files/${encodeURIComponent(assetId)}`);
    }
    /**
     * List asset folders.
     *
     * @param options Optional folder filter and pagination. See {@link AssetListOptions}.
     * @returns The parsed JSON response body (`{ folders: [...], meta }`).
     */
    listAssetFolders(options = {}) {
        const query = (0, utils_1.buildQueryString)({
            parent_folder_id: options.parentFolderId,
            direct_descendants_only: options.directDescendantsOnly,
            page: options.page,
            limit: options.limit,
        });
        return this.request.get(`${this.apiRoot}/assets/folders${query}`);
    }
    /**
     * Create an asset folder.
     *
     * @param folder The folder definition. `name` is required. See {@link AssetFolderInput}.
     * @returns The parsed JSON response body (`{ folder: {...} }`).
     * @throws {MissingParamError} If `folder` is missing/not an object, or `folder.name` is empty.
     */
    createAssetFolder(folder) {
        if (folder == null || typeof folder !== 'object') {
            throw new utils_1.MissingParamError('folder');
        }
        if ((0, utils_1.isEmpty)(folder.name)) {
            throw new utils_1.MissingParamError('folder.name');
        }
        return this.request.post(`${this.apiRoot}/assets/folders`, folder);
    }
    /**
     * Get a single asset folder.
     *
     * @param folderId The folder's numeric id.
     * @returns The parsed JSON response body (`{ folder: {...} }`).
     * @throws {MissingParamError} If `folderId` is empty.
     */
    getAssetFolder(folderId) {
        if ((0, utils_1.isEmpty)(folderId)) {
            throw new utils_1.MissingParamError('folderId');
        }
        return this.request.get(`${this.apiRoot}/assets/folders/${encodeURIComponent(folderId)}`);
    }
    /**
     * Update an asset folder. At least one field must be provided.
     *
     * @param folderId The folder's numeric id.
     * @param updates The fields to change. See {@link AssetFolderUpdate}.
     * @returns The parsed JSON response body (empty on success — the API returns 204).
     * @throws {MissingParamError} If `folderId` is empty or `updates` is missing/not an object.
     */
    updateAssetFolder(folderId, updates) {
        if ((0, utils_1.isEmpty)(folderId)) {
            throw new utils_1.MissingParamError('folderId');
        }
        if (updates == null || typeof updates !== 'object') {
            throw new utils_1.MissingParamError('updates');
        }
        return this.request.put(`${this.apiRoot}/assets/folders/${encodeURIComponent(folderId)}`, updates);
    }
    /**
     * Delete an asset folder. The folder must be empty.
     *
     * @param folderId The folder's numeric id.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `folderId` is empty.
     */
    deleteAssetFolder(folderId) {
        if ((0, utils_1.isEmpty)(folderId)) {
            throw new utils_1.MissingParamError('folderId');
        }
        return this.request.destroy(`${this.apiRoot}/assets/folders/${encodeURIComponent(folderId)}`);
    }
    /**
     * List the collections in your workspace.
     *
     * @returns The parsed JSON response body (`{ collections: [...] }`).
     */
    listCollections() {
        return this.request.get(`${this.apiRoot}/collections`);
    }
    /**
     * Create a collection. Provide inline `data` or a source `url`, not both.
     *
     * @param collection The collection definition. `name` is required. See {@link CollectionInput}.
     * @returns The parsed JSON response body (`{ collection: {...} }`).
     * @throws {MissingParamError} If `collection` is missing/not an object, or `collection.name` is empty.
     */
    createCollection(collection) {
        if (collection == null || typeof collection !== 'object') {
            throw new utils_1.MissingParamError('collection');
        }
        if ((0, utils_1.isEmpty)(collection.name)) {
            throw new utils_1.MissingParamError('collection.name');
        }
        return this.request.post(`${this.apiRoot}/collections`, collection);
    }
    /**
     * Get a single collection's metadata (name, schema, row count, size).
     *
     * @param collectionId The collection's numeric id.
     * @returns The parsed JSON response body (`{ collection: {...} }`).
     * @throws {MissingParamError} If `collectionId` is empty.
     */
    getCollection(collectionId) {
        if ((0, utils_1.isEmpty)(collectionId)) {
            throw new utils_1.MissingParamError('collectionId');
        }
        return this.request.get(`${this.apiRoot}/collections/${encodeURIComponent(collectionId)}`);
    }
    /**
     * Update a collection. Any subset of fields may be provided; `data` and `url` are mutually exclusive.
     *
     * @param collectionId The collection's numeric id.
     * @param updates The fields to change. See {@link CollectionUpdate}.
     * @returns The parsed JSON response body (`{ collection: {...} }`).
     * @throws {MissingParamError} If `collectionId` is empty or `updates` is missing/not an object.
     */
    updateCollection(collectionId, updates) {
        if ((0, utils_1.isEmpty)(collectionId)) {
            throw new utils_1.MissingParamError('collectionId');
        }
        if (updates == null || typeof updates !== 'object') {
            throw new utils_1.MissingParamError('updates');
        }
        return this.request.put(`${this.apiRoot}/collections/${encodeURIComponent(collectionId)}`, updates);
    }
    /**
     * Delete a collection. Fails if the collection is still referenced by a campaign.
     *
     * @param collectionId The collection's numeric id.
     * @returns The parsed JSON response body (empty on success — the API returns 204).
     * @throws {MissingParamError} If `collectionId` is empty.
     */
    deleteCollection(collectionId) {
        if ((0, utils_1.isEmpty)(collectionId)) {
            throw new utils_1.MissingParamError('collectionId');
        }
        return this.request.destroy(`${this.apiRoot}/collections/${encodeURIComponent(collectionId)}`);
    }
    /**
     * Get a collection's content — the full array of data rows.
     *
     * @param collectionId The collection's numeric id.
     * @returns The parsed JSON response body: an array of row objects.
     * @throws {MissingParamError} If `collectionId` is empty.
     */
    getCollectionContent(collectionId) {
        if ((0, utils_1.isEmpty)(collectionId)) {
            throw new utils_1.MissingParamError('collectionId');
        }
        return this.request.get(`${this.apiRoot}/collections/${encodeURIComponent(collectionId)}/content`);
    }
    /**
     * Replace a collection's content with a new array of data rows.
     *
     * @param collectionId The collection's numeric id.
     * @param content The full array of row objects to store.
     * @returns The parsed JSON response body (`{ collection: {...} }`).
     * @throws {MissingParamError} If `collectionId` is empty or `content` is not an array.
     */
    updateCollectionContent(collectionId, content) {
        if ((0, utils_1.isEmpty)(collectionId)) {
            throw new utils_1.MissingParamError('collectionId');
        }
        if (!Array.isArray(content)) {
            throw new utils_1.MissingParamError('content');
        }
        // This endpoint takes a top-level JSON array; the transport serializes it
        // as-is. The cast keeps `RequestData` object-shaped for its other callers.
        return this.request.put(`${this.apiRoot}/collections/${encodeURIComponent(collectionId)}/content`, content);
    }
    /**
     * Search every suppression category for an email address.
     *
     * @param email The email address to search for.
     * @returns The parsed JSON response body (`{ category, suppressions: [...] }`).
     * @throws {MissingParamError} If `email` is empty.
     */
    searchSuppression(email) {
        if ((0, utils_1.isEmpty)(email)) {
            throw new utils_1.MissingParamError('email');
        }
        return this.request.get(`${this.apiRoot}/esp/search_suppression/${encodeURIComponent(email)}`);
    }
    /**
     * List suppressions in a category (offset-based pagination).
     *
     * @param suppressionType One of `blocks`, `bounces`, `spam_reports`, `invalid_emails`.
     * @param options Optional filters and pagination. See {@link SuppressionsOptions}.
     * @returns The parsed JSON response body (`{ category, suppressions: [...] }`).
     * @throws {MissingParamError} If `suppressionType` is empty.
     */
    getSuppressions(suppressionType, options = {}) {
        if ((0, utils_1.isEmpty)(suppressionType)) {
            throw new utils_1.MissingParamError('suppressionType');
        }
        const query = (0, utils_1.buildQueryString)({
            limit: options.limit,
            offset: options.offset,
            email: options.email,
            domain: options.domain,
        });
        return this.request.get(`${this.apiRoot}/esp/suppression/${encodeURIComponent(suppressionType)}${query}`);
    }
    /**
     * List suppressions in a category for a single sending domain (cursor-based pagination).
     *
     * @param domainName The sending domain.
     * @param suppressionType One of `blocks`, `bounces`, `spam_reports`, `invalid_emails`.
     * @param options Optional filter and pagination. See {@link DomainSuppressionsOptions}.
     * @returns The parsed JSON response body (`{ category, suppressions: [...], next }`).
     * @throws {MissingParamError} If `domainName` or `suppressionType` is empty.
     */
    getDomainSuppressions(domainName, suppressionType, options = {}) {
        if ((0, utils_1.isEmpty)(domainName)) {
            throw new utils_1.MissingParamError('domainName');
        }
        if ((0, utils_1.isEmpty)(suppressionType)) {
            throw new utils_1.MissingParamError('suppressionType');
        }
        const query = (0, utils_1.buildQueryString)({
            limit: options.limit,
            email: options.email,
            start: options.start,
        });
        return this.request.get(`${this.apiRoot}/esp/domains/${encodeURIComponent(domainName)}/suppression/${encodeURIComponent(suppressionType)}${query}`);
    }
    /**
     * Add an email address to a suppression category.
     *
     * @param suppressionType One of `blocks`, `bounces`, `spam_reports`, `invalid_emails`.
     * @param email The email address to suppress.
     * @returns The parsed JSON response body.
     * @throws {MissingParamError} If `suppressionType` or `email` is empty.
     */
    createSuppression(suppressionType, email) {
        if ((0, utils_1.isEmpty)(suppressionType)) {
            throw new utils_1.MissingParamError('suppressionType');
        }
        if ((0, utils_1.isEmpty)(email)) {
            throw new utils_1.MissingParamError('email');
        }
        return this.request.post(`${this.apiRoot}/esp/suppression/${encodeURIComponent(suppressionType)}/${encodeURIComponent(email)}`);
    }
    /**
     * Remove an email address from a suppression category.
     *
     * @param suppressionType One of `blocks`, `bounces`, `spam_reports`, `invalid_emails`.
     * @param email The email address to unsuppress.
     * @returns The parsed JSON response body (empty on success — the API returns 204).
     * @throws {MissingParamError} If `suppressionType` or `email` is empty.
     */
    deleteSuppression(suppressionType, email) {
        if ((0, utils_1.isEmpty)(suppressionType)) {
            throw new utils_1.MissingParamError('suppressionType');
        }
        if ((0, utils_1.isEmpty)(email)) {
            throw new utils_1.MissingParamError('email');
        }
        return this.request.destroy(`${this.apiRoot}/esp/suppression/${encodeURIComponent(suppressionType)}/${encodeURIComponent(email)}`);
    }
    /**
     * List the reporting webhooks in your workspace.
     *
     * @returns The parsed JSON response body (`{ reporting_webhooks: [...] }`).
     */
    listReportingWebhooks() {
        return this.request.get(`${this.apiRoot}/reporting_webhooks`);
    }
    /**
     * Create a reporting webhook.
     *
     * @param webhook The webhook definition. `endpoint` is required. See {@link ReportingWebhookInput}.
     * @returns The parsed JSON response body (the created webhook).
     * @throws {MissingParamError} If `webhook` is missing/not an object, or `webhook.endpoint` is empty.
     */
    createReportingWebhook(webhook) {
        if (webhook == null || typeof webhook !== 'object') {
            throw new utils_1.MissingParamError('webhook');
        }
        if ((0, utils_1.isEmpty)(webhook.endpoint)) {
            throw new utils_1.MissingParamError('webhook.endpoint');
        }
        return this.request.post(`${this.apiRoot}/reporting_webhooks`, webhook);
    }
    /**
     * Get a single reporting webhook.
     *
     * @param webhookId The webhook's numeric id.
     * @returns The parsed JSON response body (the webhook).
     * @throws {MissingParamError} If `webhookId` is empty.
     */
    getReportingWebhook(webhookId) {
        if ((0, utils_1.isEmpty)(webhookId)) {
            throw new utils_1.MissingParamError('webhookId');
        }
        return this.request.get(`${this.apiRoot}/reporting_webhooks/${encodeURIComponent(webhookId)}`);
    }
    /**
     * Update a reporting webhook. Any subset of fields may be provided.
     *
     * @param webhookId The webhook's numeric id.
     * @param updates The fields to change. See {@link ReportingWebhookUpdate}.
     * @returns The parsed JSON response body (the updated webhook).
     * @throws {MissingParamError} If `webhookId` is empty or `updates` is missing/not an object.
     */
    updateReportingWebhook(webhookId, updates) {
        if ((0, utils_1.isEmpty)(webhookId)) {
            throw new utils_1.MissingParamError('webhookId');
        }
        if (updates == null || typeof updates !== 'object') {
            throw new utils_1.MissingParamError('updates');
        }
        return this.request.put(`${this.apiRoot}/reporting_webhooks/${encodeURIComponent(webhookId)}`, updates);
    }
    /**
     * Delete a reporting webhook.
     *
     * @param webhookId The webhook's numeric id.
     * @returns The parsed JSON response body (empty on success — the API returns 204).
     * @throws {MissingParamError} If `webhookId` is empty.
     */
    deleteReportingWebhook(webhookId) {
        if ((0, utils_1.isEmpty)(webhookId)) {
            throw new utils_1.MissingParamError('webhookId');
        }
        return this.request.destroy(`${this.apiRoot}/reporting_webhooks/${encodeURIComponent(webhookId)}`);
    }
    /**
     * List the snippets in your workspace.
     *
     * @returns The parsed JSON response body (`{ snippets: [...] }`).
     */
    getSnippets() {
        return this.request.get(`${this.apiRoot}/snippets`);
    }
    /**
     * Create a snippet.
     *
     * @param snippet The snippet definition. `name` and `value` are required. See {@link SnippetInput}.
     * @returns The parsed JSON response body (`{ snippet: {...} }`).
     * @throws {MissingParamError} If `snippet` is missing/not an object, or `snippet.name`/`snippet.value` is empty.
     */
    createSnippet(snippet) {
        if (snippet == null || typeof snippet !== 'object') {
            throw new utils_1.MissingParamError('snippet');
        }
        if ((0, utils_1.isEmpty)(snippet.name)) {
            throw new utils_1.MissingParamError('snippet.name');
        }
        if ((0, utils_1.isEmpty)(snippet.value)) {
            throw new utils_1.MissingParamError('snippet.value');
        }
        return this.request.post(`${this.apiRoot}/snippets`, snippet);
    }
    /**
     * Create or update a snippet (upsert by name).
     *
     * @param snippet The snippet definition. `name` and `value` are required. See {@link SnippetInput}.
     * @returns The parsed JSON response body (`{ snippet: {...} }`).
     * @throws {MissingParamError} If `snippet` is missing/not an object, or `snippet.name`/`snippet.value` is empty.
     */
    updateSnippet(snippet) {
        if (snippet == null || typeof snippet !== 'object') {
            throw new utils_1.MissingParamError('snippet');
        }
        if ((0, utils_1.isEmpty)(snippet.name)) {
            throw new utils_1.MissingParamError('snippet.name');
        }
        if ((0, utils_1.isEmpty)(snippet.value)) {
            throw new utils_1.MissingParamError('snippet.value');
        }
        return this.request.put(`${this.apiRoot}/snippets`, snippet);
    }
    /**
     * Delete a snippet by name. Fails if the snippet is still in use.
     *
     * @param name The snippet's name.
     * @returns The parsed JSON response body (empty on success — the API returns 204).
     * @throws {MissingParamError} If `name` is empty.
     */
    deleteSnippet(name) {
        if ((0, utils_1.isEmpty)(name)) {
            throw new utils_1.MissingParamError('name');
        }
        return this.request.destroy(`${this.apiRoot}/snippets/${encodeURIComponent(name)}`);
    }
    /**
     * List the sender identities in your workspace.
     *
     * @param options Optional sort, pagination, and hidden filter. See {@link SenderIdentitiesOptions}.
     * @returns The parsed JSON response body (`{ sender_identities: [...], next }`).
     */
    getSenderIdentities(options = {}) {
        const query = (0, utils_1.buildQueryString)({
            start: options.start,
            limit: options.limit,
            sort: options.sort,
            hidden: options.hidden,
        });
        return this.request.get(`${this.apiRoot}/sender_identities${query}`);
    }
    /**
     * Get a single sender identity.
     *
     * @param senderId The sender identity's numeric id.
     * @returns The parsed JSON response body (`{ sender_identity: {...} }`).
     * @throws {MissingParamError} If `senderId` is empty.
     */
    getSenderIdentity(senderId) {
        if ((0, utils_1.isEmpty)(senderId)) {
            throw new utils_1.MissingParamError('senderId');
        }
        return this.request.get(`${this.apiRoot}/sender_identities/${encodeURIComponent(senderId)}`);
    }
    /**
     * List the campaigns and newsletters that use a sender identity.
     *
     * @param senderId The sender identity's numeric id.
     * @returns The parsed JSON response body (`{ campaigns, sent_newsletters, draft_newsletters }`).
     * @throws {MissingParamError} If `senderId` is empty.
     */
    getSenderIdentityUsedBy(senderId) {
        if ((0, utils_1.isEmpty)(senderId)) {
            throw new utils_1.MissingParamError('senderId');
        }
        return this.request.get(`${this.apiRoot}/sender_identities/${encodeURIComponent(senderId)}/used_by`);
    }
    /**
     * List sent messages (deliveries) across your workspace.
     *
     * @param options Optional filters and pagination. See {@link MessagesOptions}.
     * @returns The parsed JSON response body (`{ messages: [...], next, ... }`).
     */
    getMessages(options = {}) {
        const query = (0, utils_1.buildQueryString)({
            start: options.start,
            limit: options.limit,
            drafts: options.drafts,
            metric: options.metric,
            type: options.type,
            campaign_id: options.campaign_id,
            action_id: options.action_id,
            newsletter_id: options.newsletter_id,
            transactional_id: options.transactional_id,
            trigger_id: options.trigger_id,
            template_id: options.template_id,
            content_id: options.content_id,
            start_ts: options.start_ts,
            end_ts: options.end_ts,
            associations: options.associations,
            get_tracked_responses: options.get_tracked_responses,
        });
        return this.request.get(`${this.apiRoot}/messages${query}`);
    }
    /**
     * Get a single sent message (delivery).
     *
     * @param messageId The delivery id (`CIO-Delivery-ID`).
     * @param options Optional response expansions. See {@link MessageOptions}.
     * @returns The parsed JSON response body (`{ message: {...}, ... }`).
     * @throws {MissingParamError} If `messageId` is empty.
     */
    getMessage(messageId, options = {}) {
        if ((0, utils_1.isEmpty)(messageId)) {
            throw new utils_1.MissingParamError('messageId');
        }
        const query = (0, utils_1.buildQueryString)({
            archived_message: options.archived_message,
            associations: options.associations,
            get_tracked_responses: options.get_tracked_responses,
        });
        return this.request.get(`${this.apiRoot}/messages/${encodeURIComponent(messageId)}${query}`);
    }
    /**
     * Get the archived content of a single sent message.
     *
     * @param messageId The delivery id (`CIO-Delivery-ID`).
     * @returns The parsed JSON response body (`{ archived_message: {...} }`).
     * @throws {MissingParamError} If `messageId` is empty.
     */
    getArchivedMessage(messageId) {
        if ((0, utils_1.isEmpty)(messageId)) {
            throw new utils_1.MissingParamError('messageId');
        }
        return this.request.get(`${this.apiRoot}/messages/${encodeURIComponent(messageId)}/archived_message`);
    }
    /**
     * Start a CSV import.
     *
     * @param importData The import definition. `data_file_url` and `type` are required. See {@link ImportInput}.
     * @returns The parsed JSON response body (`{ import: {...} }`).
     * @throws {MissingParamError} If `importData` is missing/not an object, or `data_file_url`/`type` is empty.
     */
    createImport(importData) {
        if (importData == null || typeof importData !== 'object') {
            throw new utils_1.MissingParamError('importData');
        }
        if ((0, utils_1.isEmpty)(importData.data_file_url)) {
            throw new utils_1.MissingParamError('importData.data_file_url');
        }
        if ((0, utils_1.isEmpty)(importData.type)) {
            throw new utils_1.MissingParamError('importData.type');
        }
        return this.request.post(`${this.apiRoot}/imports`, { import: importData });
    }
    /**
     * Get the status of an import.
     *
     * @param importId The import's numeric id.
     * @returns The parsed JSON response body (`{ import: {...} }`).
     * @throws {MissingParamError} If `importId` is empty.
     */
    getImport(importId) {
        if ((0, utils_1.isEmpty)(importId)) {
            throw new utils_1.MissingParamError('importId');
        }
        return this.request.get(`${this.apiRoot}/imports/${encodeURIComponent(importId)}`);
    }
    /**
     * Batch-update attribute metadata (up to 100 at a time).
     *
     * @param attributes The attribute updates. Each requires a `name`. See {@link DataIndexAttribute}.
     * @returns The parsed JSON response body (empty on success — the API returns 204).
     * @throws {MissingParamError} If `attributes` is not a non-empty array.
     */
    batchUpdateAttributes(attributes) {
        if (!Array.isArray(attributes) || attributes.length === 0) {
            throw new utils_1.MissingParamError('attributes');
        }
        return this.request.post(`${this.apiRoot}/data_index/attributes`, { attributes });
    }
    /**
     * Batch-update event metadata (up to 100 at a time).
     *
     * @param events The event updates. Each requires a `name`. See {@link DataIndexEvent}.
     * @returns The parsed JSON response body (empty on success — the API returns 204).
     * @throws {MissingParamError} If `events` is not a non-empty array.
     */
    batchUpdateEvents(events) {
        if (!Array.isArray(events) || events.length === 0) {
            throw new utils_1.MissingParamError('events');
        }
        return this.request.post(`${this.apiRoot}/data_index/events`, { events });
    }
    /**
     * List the workspaces (environments) in your account, with usage counts.
     *
     * @returns The parsed JSON response body (`{ workspaces: [...] }`).
     */
    listWorkspaces() {
        return this.request.get(`${this.apiRoot}/workspaces`);
    }
    /**
     * List the Customer.io egress IP addresses (for allowlisting).
     *
     * @returns The parsed JSON response body (`{ ip_addresses: [...] }`).
     */
    getIpAddresses() {
        return this.request.get(`${this.apiRoot}/info/ip_addresses`);
    }
}
exports.APIClient = APIClient;
var requests_2 = require("./api/requests");
Object.defineProperty(exports, "SendEmailRequest", { enumerable: true, get: function () { return requests_2.SendEmailRequest; } });
Object.defineProperty(exports, "SendPushRequest", { enumerable: true, get: function () { return requests_2.SendPushRequest; } });
Object.defineProperty(exports, "SendSMSRequest", { enumerable: true, get: function () { return requests_2.SendSMSRequest; } });
Object.defineProperty(exports, "SendWhatsAppRequest", { enumerable: true, get: function () { return requests_2.SendWhatsAppRequest; } });
Object.defineProperty(exports, "SendInboxMessageRequest", { enumerable: true, get: function () { return requests_2.SendInboxMessageRequest; } });
Object.defineProperty(exports, "SendInAppRequest", { enumerable: true, get: function () { return requests_2.SendInAppRequest; } });