import {z} from 'zod' export type CampaignStatus = 'active' | 'paused' | 'stopping' | 'stopped' | 'completed' | 'failed' | 'attention'; export type OutboundChannel = 'phone' | 'whatsapp'; export type GetApiV1ConvaiOutboundCampaignsCampaignsArgs = {page_size?: number | undefined, status?: (Array | null) | undefined, agent_id?: (string | null) | undefined, channel?: (OutboundChannel | null) | undefined, mode?: ('voice' | 'message' | null) | undefined, search?: (string | null) | undefined, cursor?: (string | null) | undefined, 'xi-api-key'?: (string | null) | undefined}; export type GetApiV1ConvaiOutboundCampaignsCampaignsOptions = {baseUrl: string, fetch?: typeof fetch | undefined}; export const twilioMachineDetectionMode = z.enum(["enable", "detect_message_end"]); export const twilioMachineDetectionAction = z.literal("hang_up"); export const twilioAnsweredBy = z.enum(["human", "fax", "unknown", "machine_start", "machine_end_beep", "machine_end_silence", "machine_end_other"]); export const twilioMachineDetectionRule = z.object({action: twilioMachineDetectionAction, answered_by: z.array(twilioAnsweredBy)}); export const twilioMachineDetectionConfig = z.object({mode: twilioMachineDetectionMode.optional(), actions: z.array(twilioMachineDetectionRule).optional()}); export const phoneVoiceConfigOutput = z.object({ringing_timeout_secs: z.number().int().gte(1).lte(999).optional(), twilio_call_recording_enabled: z.boolean().optional(), twilio_machine_detection: twilioMachineDetectionConfig.nullable().optional(), mode: z.literal("voice").optional()}); export const phoneChannelConfigOutput = z.object({channel: z.literal("phone").optional(), agent_phone_number_id: z.string().min(1).max(256), mode_config: phoneVoiceConfigOutput.optional()}); export const whatsAppVoiceConfig = z.object({mode: z.literal("voice").optional(), whatsapp_call_permission_request_template_name: z.string(), whatsapp_call_permission_request_template_language_code: z.string()}); export const whatsAppTemplateComponentButton = z.object({type: z.string(), text: z.string().nullable().optional(), url: z.string().nullable().optional()}); export const whatsAppTemplateComponent = z.object({type: z.string(), format: z.string().nullable().optional(), text: z.string().nullable().optional(), buttons: z.array(whatsAppTemplateComponentButton).nullable().optional()}); export const whatsAppTemplate = z.object({name: z.string(), language: z.string(), parameter_format: z.enum(["POSITIONAL", "NAMED"]).optional(), components: z.array(whatsAppTemplateComponent), category: z.string().nullable().optional()}); export const whatsAppMessageConfig = z.object({mode: z.literal("message").optional(), template: whatsAppTemplate}); export const whatsAppChannelConfig = z.object({channel: z.literal("whatsapp").optional(), whatsapp_phone_number_id: z.string().min(1), mode_config: z.discriminatedUnion("mode", [whatsAppVoiceConfig, whatsAppMessageConfig])}); export const campaignStatus = z.enum(["active", "paused", "stopping", "stopped", "completed", "failed", "attention"]); export const intervalPlacement = z.enum(["start", "random", "rotate"]); export const retryPolicy = z.object({retry_delay_seconds: z.number().int().gt(0).optional(), max_short_retries: z.number().int().gte(0).lte(30).optional(), max_total_calls: z.number().int().gte(1).lte(30).optional(), max_callbacks: z.number().int().gte(0).lte(30).optional(), max_redirects: z.number().int().gte(0).lte(30).optional(), interval_placement: intervalPlacement.optional(), rotate_step_seconds: z.number().int().gt(0).optional(), max_consecutive_failures_per_number: z.number().int().gte(1).lte(30).nullable().optional()}); export const callingIntervalPolicy = z.object({start: z.string(), end: z.string()}); export const callingPeriodPolicy = z.object({weekdays: z.array(z.number().int()), intervals: z.array(callingIntervalPolicy)}); export const dataCollectionPolicy = z.object({callback_time: z.string().nullable().optional(), redirect_phone_number: z.string().nullable().optional(), stop: z.string().nullable().optional(), mappings: z.record(z.string(), z.string()).optional()}); export const followupContextPolicy = z.object({call_type: z.boolean().optional(), previous_conversation_id: z.boolean().optional(), previous_summary: z.boolean().optional(), previous_transcript: z.boolean().optional()}); export const campaignPolicySnapshot = z.object({timezone: z.string(), retry: retryPolicy, calling_periods: z.array(callingPeriodPolicy), data_collection: dataCollectionPolicy, followup_context: followupContextPolicy}); export const campaignVariant = z.enum(["simple", "advanced"]); export const campaignSource = z.enum(["ui", "api"]); export const outboundChannel = z.enum(["phone", "whatsapp"]); export const outboundMode = z.enum(["voice", "message"]); export const recipientDeliveryCounts = z.object({sent: z.number().int().optional(), delivered: z.number().int().optional(), read: z.number().int().optional(), replied: z.number().int().optional(), failed: z.number().int().optional()}); export const campaignSummaryCounts = z.object({leads: z.number().int(), terminal_leads: z.number().int(), open_attention: z.number().int(), delivery: recipientDeliveryCounts.nullable().optional()}); export const campaignResponse = z.object({campaign_id: z.string(), call_name: z.string(), agent_id: z.string(), channel_config: z.discriminatedUnion("channel", [phoneChannelConfigOutput, whatsAppChannelConfig]), status: campaignStatus, policy: campaignPolicySnapshot, variant: campaignVariant, source: campaignSource.nullable(), created_at_unix: z.number().int(), scheduled_time_unix: z.number().int().nullable(), updated_at_unix: z.number().int(), branch_id: z.string().nullable(), environment: z.string().nullable(), target_concurrency_limit: z.number().int().nullable(), channel: outboundChannel, mode: outboundMode, counts: campaignSummaryCounts, agent_name: z.string()}); export const campaignPageResponse = z.object({campaigns: z.array(campaignResponse), next_cursor: z.string().nullable(), has_more: z.boolean()}); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * List Outbound Campaigns * * GET /v1/convai/outbound-campaigns/campaigns */ export const getApiV1ConvaiOutboundCampaignsCampaigns = async (args: GetApiV1ConvaiOutboundCampaignsCampaignsArgs, options: GetApiV1ConvaiOutboundCampaignsCampaignsOptions) => { const {baseUrl, fetch: fetchFn = fetch} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json") if (args.page_size != null) query.append("page_size", String(args.page_size)) for (const item of args.status ?? []) query.append("status", String(item)) if (args.agent_id != null) query.append("agent_id", String(args.agent_id)) if (args.channel != null) query.append("channel", String(args.channel)) if (args.mode != null) query.append("mode", String(args.mode)) if (args.search != null) query.append("search", String(args.search)) if (args.cursor != null) query.append("cursor", String(args.cursor)) if (args['xi-api-key'] != null) headers.set("xi-api-key", String(args['xi-api-key'])) const url = `${baseUrl.replace(/\/+$/, '')}/v1/convai/outbound-campaigns/campaigns${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "GET", headers}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(campaignPageResponse, await res.json()) };