// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. import { APIResource } from '../../core/resource'; import * as ContactsAPI from './contacts'; import { ContactAddParams, ContactAddResponse, ContactListParams, ContactRemoveParams, Contacts, } from './contacts'; import { APIPromise } from '../../core/api-promise'; import { Cursor, type CursorParams, PagePromise } from '../../core/pagination'; import { buildHeaders } from '../../internal/headers'; import { RequestOptions } from '../../internal/request-options'; import { path } from '../../internal/utils/path'; export class Broadcasts extends APIResource { contacts: ContactsAPI.Contacts = new ContactsAPI.Contacts(this._client); /** * Create a new broadcast campaign. Add contacts after creation, then send. * * @example * ```ts * const broadcast = await client.broadcasts.create({ * channel: 'sms', * name: 'Black Friday Sale', * text: 'Hi {{name}}, check out our Black Friday deals! Use code FRIDAY20 for 20% off.', * }); * ``` */ create(body: BroadcastCreateParams, options?: RequestOptions): APIPromise { return this._client.post('/v1/broadcasts', { body, ...options }); } /** * Get broadcast * * @example * ```ts * const broadcast = await client.broadcasts.retrieve( * 'broadcastId', * ); * ``` */ retrieve(broadcastID: string, options?: RequestOptions): APIPromise { return this._client.get(path`/v1/broadcasts/${broadcastID}`, options); } /** * Update a broadcast in draft status. * * @example * ```ts * const broadcast = await client.broadcasts.update( * 'broadcastId', * ); * ``` */ update( broadcastID: string, body: BroadcastUpdateParams, options?: RequestOptions, ): APIPromise { return this._client.patch(path`/v1/broadcasts/${broadcastID}`, { body, ...options }); } /** * List broadcasts for this project. * * @example * ```ts * // Automatically fetches more pages as needed. * for await (const broadcast of client.broadcasts.list()) { * // ... * } * ``` */ list( query: BroadcastListParams | null | undefined = {}, options?: RequestOptions, ): PagePromise { return this._client.getAPIList('/v1/broadcasts', Cursor, { query, ...options }); } /** * Delete a broadcast in draft status. * * @example * ```ts * await client.broadcasts.delete('broadcastId'); * ``` */ delete(broadcastID: string, options?: RequestOptions): APIPromise { return this._client.delete(path`/v1/broadcasts/${broadcastID}`, { ...options, headers: buildHeaders([{ Accept: '*/*' }, options?.headers]), }); } /** * Cancel a broadcast. Pending contacts will be skipped, but already queued * messages may still be delivered. * * @example * ```ts * const response = await client.broadcasts.cancel( * 'broadcastId', * ); * ``` */ cancel(broadcastID: string, options?: RequestOptions): APIPromise { return this._client.post(path`/v1/broadcasts/${broadcastID}/cancel`, options); } /** * Request manual review by the Zavu team for a rejected broadcast. Use this after * automated review rejection if you believe the content is legitimate. * * @example * ```ts * const response = await client.broadcasts.escalateReview( * 'broadcastId', * ); * ``` */ escalateReview(broadcastID: string, options?: RequestOptions): APIPromise { return this._client.post(path`/v1/broadcasts/${broadcastID}/escalate`, options); } /** * Get real-time progress of a broadcast including delivery counts and estimated * completion time. * * @example * ```ts * const broadcastProgress = await client.broadcasts.progress( * 'broadcastId', * ); * ``` */ progress(broadcastID: string, options?: RequestOptions): APIPromise { return this._client.get(path`/v1/broadcasts/${broadcastID}/progress`, options); } /** * Update the scheduled time for a broadcast. The broadcast must be in scheduled * status. * * @example * ```ts * const response = await client.broadcasts.reschedule( * 'broadcastId', * { scheduledAt: '2024-01-15T14:00:00Z' }, * ); * ``` */ reschedule( broadcastID: string, body: BroadcastRescheduleParams, options?: RequestOptions, ): APIPromise { return this._client.patch(path`/v1/broadcasts/${broadcastID}/schedule`, { body, ...options }); } /** * Resubmit a rejected broadcast for AI review after editing content. Maximum 3 * review attempts allowed per broadcast. * * @example * ```ts * const response = await client.broadcasts.retryReview( * 'broadcastId', * ); * ``` */ retryReview(broadcastID: string, options?: RequestOptions): APIPromise { return this._client.post(path`/v1/broadcasts/${broadcastID}/retry-review`, options); } /** * Start sending the broadcast immediately or schedule for later. * * **The account must be past the unverified level to send, except on WhatsApp.** * An account that has verified nothing is refused with `403` and code * `kyc_required` on every channel other than `whatsapp`. Any one of these lifts * it: identity verification (KYC), a saved payment method, a settled deposit, or a * paid plan. Business verification (KYB) is not required to broadcast; it gates * 10DLC registration only. A `whatsapp` broadcast is exempt: it can only be built * on a template, and Meta vets the business and the content when it approves that * template, so an unapproved template is refused instead. `smart` is not exempt, * since it can route a contact to SMS or email. Drafts can be created, edited and * kept without any check. Every send path (dashboard, API and CLI) enforces the * same rule. * * **Daily ceilings apply per recipient.** Each message a broadcast sends counts * against the channel's daily ceiling (see `POST /v1/messages`). Once the ceiling * is reached, the remaining recipients are marked `failed` with `errorCode` * `DAILY_LIMIT_EXCEEDED`; they are not retried the next day. * * **Review depends on the channel, and cannot be bypassed.** A draft is submitted * to automated content review here; it does not go straight out. A WhatsApp * broadcast built on a Meta-approved template skips review (Meta already vetted * the content) and begins sending. An email broadcast sends as soon as the * automated review passes. Every other channel moves to `pending_admin_review` and * waits for a person. If the review rejects it, use PATCH to edit the content then * call POST /retry-review. * * Calling this on a broadcast that is already `approved` or `scheduled` sends or * reschedules it directly, since it has already been reviewed. Reserves the * estimated cost from your balance. * * @example * ```ts * const response = await client.broadcasts.send( * 'broadcastId', * ); * ``` */ send( broadcastID: string, body: BroadcastSendParams | null | undefined = {}, options?: RequestOptions, ): APIPromise { return this._client.post(path`/v1/broadcasts/${broadcastID}/send`, { body, ...options }); } } export type BroadcastsCursor = Cursor; export type BroadcastContactsCursor = Cursor; export interface Broadcast { id: string; /** * Broadcast delivery channel. Use 'smart' for per-contact intelligent routing. */ channel: BroadcastChannel; createdAt: string; /** * Type of message for broadcast. */ messageType: BroadcastMessageType; name: string; /** * Current status of the broadcast. */ status: BroadcastStatus; /** * Total number of contacts in the broadcast. */ totalContacts: number; /** * Actual cost so far in USD. */ actualCost?: number | null; completedAt?: string; /** * Content for non-text broadcast message types. */ content?: BroadcastContent; deliveredCount?: number; emailSubject?: string; /** * Estimated total cost in USD. */ estimatedCost?: number | null; failedCount?: number; metadata?: { [key: string]: string }; pendingCount?: number; /** * Amount reserved from balance in USD. */ reservedAmount?: number | null; /** * Number of review attempts (max 3). */ reviewAttempts?: number | null; /** * AI content review result. */ reviewResult?: Broadcast.ReviewResult | null; scheduledAt?: string; senderId?: string; sendingCount?: number; startedAt?: string; text?: string; updatedAt?: string; } export namespace Broadcast { /** * AI content review result. */ export interface ReviewResult { /** * Policy categories violated, if any. */ categories?: Array; /** * Problematic text fragments, if any. */ flaggedContent?: Array | null; /** * Explanation of the review decision. */ reasoning?: string; reviewedAt?: string; /** * Content safety score from 0.0 to 1.0, where 1.0 is completely safe. */ score?: number; } } /** * Broadcast delivery channel. Use 'smart' for per-contact intelligent routing. */ export type BroadcastChannel = 'smart' | 'sms' | 'sms_oneway' | 'whatsapp' | 'telegram' | 'email'; export interface BroadcastContact { id: string; createdAt: string; recipient: string; recipientType: 'phone' | 'email'; /** * Status of a contact within a broadcast. */ status: BroadcastContactStatus; cost?: number | null; errorCode?: string; errorMessage?: string; /** * Associated message ID after processing. */ messageId?: string; processedAt?: string; templateButtonVariables?: { [key: string]: string }; templateHeaderVariables?: { [key: string]: string }; templateVariables?: { [key: string]: string }; } /** * Status of a contact within a broadcast. */ export type BroadcastContactStatus = 'pending' | 'queued' | 'sending' | 'delivered' | 'failed' | 'skipped'; /** * Content for non-text broadcast message types. */ export interface BroadcastContent { /** * Filename for documents. */ filename?: string; /** * Media ID if already uploaded. */ mediaId?: string; /** * URL of the media file. */ mediaUrl?: string; /** * MIME type of the media. */ mimeType?: string; /** * Default button variables for dynamic URL/OTP buttons. Keys are the button index * (0, 1, 2). Per-contact values override these. */ templateButtonVariables?: { [key: string]: string }; /** * Default value for a text-header variable, keyed by `1` (can be overridden per * contact). If omitted, Zavu resolves the header from `templateVariables` by the * header placeholder's name. */ templateHeaderVariables?: { [key: string]: string }; /** * Template ID for template messages. */ templateId?: string; /** * Default body variables (can be overridden per contact). Key them to match the * template body: by position (`1`, `2`, ...) for positional templates, or by name * (e.g. `customer_name`) for named templates. Zavu detects the template's format * and sends the correct payload to Meta. Do not mix positional and named keys. */ templateVariables?: { [key: string]: string }; } /** * Type of message for broadcast. */ export type BroadcastMessageType = 'text' | 'image' | 'video' | 'audio' | 'document' | 'template'; export interface BroadcastProgress { broadcastId: string; /** * Successfully delivered. */ delivered: number; /** * Failed to deliver. */ failed: number; /** * Not yet queued for sending. */ pending: number; /** * Percentage complete (0-100). */ percentComplete: number; /** * Currently being sent. */ sending: number; /** * Skipped (broadcast cancelled). */ skipped: number; /** * Current status of the broadcast. */ status: BroadcastStatus; /** * Total contacts in broadcast. */ total: number; /** * Actual cost so far in USD. */ actualCost?: number | null; estimatedCompletionAt?: string; /** * Estimated total cost in USD. */ estimatedCost?: number | null; /** * Amount reserved from balance in USD. */ reservedAmount?: number | null; startedAt?: string; } /** * Current status of the broadcast. */ export type BroadcastStatus = | 'draft' | 'pending_review' | 'approved' | 'rejected' | 'escalated' | 'rejected_final' | 'scheduled' | 'sending' | 'paused' | 'completed' | 'cancelled' | 'failed'; export interface BroadcastCreateResponse { broadcast: Broadcast; } export interface BroadcastRetrieveResponse { broadcast: Broadcast; } export interface BroadcastUpdateResponse { broadcast: Broadcast; } export interface BroadcastCancelResponse { broadcast: Broadcast; } export interface BroadcastEscalateReviewResponse { broadcast: Broadcast; } export interface BroadcastRescheduleResponse { broadcast: Broadcast; } export interface BroadcastRetryReviewResponse { broadcast: Broadcast; } export interface BroadcastSendResponse { broadcast: Broadcast; } export interface BroadcastCreateParams { /** * Broadcast delivery channel. Use 'smart' for per-contact intelligent routing. */ channel: BroadcastChannel; /** * Name of the broadcast campaign. */ name: string; /** * Content for non-text broadcast message types. */ content?: BroadcastContent; /** * HTML body for email broadcasts. */ emailHtmlBody?: string; /** * Email subject line. Required for email broadcasts. */ emailSubject?: string; /** * Idempotency key to prevent duplicate broadcasts. */ idempotencyKey?: string; /** * Type of message for broadcast. */ messageType?: BroadcastMessageType; metadata?: { [key: string]: string }; /** * Schedule the broadcast for future delivery. */ scheduledAt?: string; /** * Sender profile ID. Uses default sender if omitted. */ senderId?: string; /** * Text content or caption. Supports template variables: {{name}}, {{1}}, etc. */ text?: string; } export interface BroadcastUpdateParams { /** * Content for non-text broadcast message types. */ content?: BroadcastContent; emailHtmlBody?: string; emailSubject?: string; metadata?: { [key: string]: string }; name?: string; text?: string; } export interface BroadcastListParams extends CursorParams { /** * Current status of the broadcast. */ status?: BroadcastStatus; } export interface BroadcastRescheduleParams { /** * New scheduled time for the broadcast. */ scheduledAt: string; } export interface BroadcastSendParams { /** * Schedule for future delivery. Omit to send immediately. */ scheduledAt?: string; } Broadcasts.Contacts = Contacts; export declare namespace Broadcasts { export { type Broadcast as Broadcast, type BroadcastChannel as BroadcastChannel, type BroadcastContact as BroadcastContact, type BroadcastContactStatus as BroadcastContactStatus, type BroadcastContent as BroadcastContent, type BroadcastMessageType as BroadcastMessageType, type BroadcastProgress as BroadcastProgress, type BroadcastStatus as BroadcastStatus, type BroadcastCreateResponse as BroadcastCreateResponse, type BroadcastRetrieveResponse as BroadcastRetrieveResponse, type BroadcastUpdateResponse as BroadcastUpdateResponse, type BroadcastCancelResponse as BroadcastCancelResponse, type BroadcastEscalateReviewResponse as BroadcastEscalateReviewResponse, type BroadcastRescheduleResponse as BroadcastRescheduleResponse, type BroadcastRetryReviewResponse as BroadcastRetryReviewResponse, type BroadcastSendResponse as BroadcastSendResponse, type BroadcastsCursor as BroadcastsCursor, type BroadcastCreateParams as BroadcastCreateParams, type BroadcastUpdateParams as BroadcastUpdateParams, type BroadcastListParams as BroadcastListParams, type BroadcastRescheduleParams as BroadcastRescheduleParams, type BroadcastSendParams as BroadcastSendParams, }; export { Contacts as Contacts, type ContactAddResponse as ContactAddResponse, type ContactListParams as ContactListParams, type ContactAddParams as ContactAddParams, type ContactRemoveParams as ContactRemoveParams, }; }