import {z} from 'zod' export type RangeFilterFloat = {eq?: number | undefined, gt?: number | undefined, gte?: number | undefined, lt?: number | undefined, lte?: number | undefined}; export type RangeFilterDateTime = {eq?: string | undefined, gt?: string | undefined, gte?: string | undefined, lt?: string | undefined, lte?: string | undefined}; export type GetApiApiV4ReportingCdrsArgs = {limit?: number | undefined, offset?: number | undefined, cursor?: string | undefined, type?: ('call' | 'conversation' | 'hlr_lookup' | 'sms' | Array<'call' | 'conversation' | 'hlr_lookup' | 'sms'>) | undefined, account_id?: (string | Array) | undefined, subtype?: ('inbound' | 'outbound' | 'click_to_call' | 'dialer' | 'scheduled_callback' | 'queue_callback' | 'chat' | 'facebook' | 'instagram' | 'telegram' | 'viber' | 'whatsapp' | 'hlr_lookup' | 'outbound_sms' | Array<'inbound' | 'outbound' | 'click_to_call' | 'dialer' | 'scheduled_callback' | 'queue_callback' | 'chat' | 'facebook' | 'instagram' | 'telegram' | 'viber' | 'whatsapp' | 'hlr_lookup' | 'outbound_sms'>) | undefined, disposition?: ('busy' | 'answered' | 'vm_answered' | 'machine_answered' | 'abandoned' | 'dialer_abandoned' | 'system_abandoned' | 'rejected' | 'system_rejected' | 'no_answer' | 'missed' | 'failed' | 'active' | 'archived' | 'initiated' | 'waiting' | 'available' | 'unavailable' | 'unknown' | 'sent' | 'delivered' | 'not_sent' | 'not_delivered' | 'route_unavailable' | Array<'busy' | 'answered' | 'vm_answered' | 'machine_answered' | 'abandoned' | 'dialer_abandoned' | 'system_abandoned' | 'rejected' | 'system_rejected' | 'no_answer' | 'missed' | 'failed' | 'active' | 'archived' | 'initiated' | 'waiting' | 'available' | 'unavailable' | 'unknown' | 'sent' | 'delivered' | 'not_sent' | 'not_delivered' | 'route_unavailable'>) | undefined, wrapup_code?: (string | Array) | undefined, end_reason?: ('agent' | 'busy' | 'customer' | 'dncr_failure' | 'dncr_restricted' | 'failed' | 'machine' | 'machine_answered' | 'manager' | 'no_answer' | 'rejected' | 'remote_party' | 'system_reject' | 'timeout' | Array<'agent' | 'busy' | 'customer' | 'dncr_failure' | 'dncr_restricted' | 'failed' | 'machine' | 'machine_answered' | 'manager' | 'no_answer' | 'rejected' | 'remote_party' | 'system_reject' | 'timeout'>) | undefined, tag?: ('monitor' | 'whisper' | 'barge_in' | 'intercept' | 'terminate' | 'forward' | 'consult_transfer' | 'blind_transfer' | 'all_transfers' | 'conference' | 'call_score' | Array<'monitor' | 'whisper' | 'barge_in' | 'intercept' | 'terminate' | 'forward' | 'consult_transfer' | 'blind_transfer' | 'all_transfers' | 'conference' | 'call_score'>) | undefined, from_phone_number?: (unknown | Array) | undefined, to_phone_number?: (unknown | Array) | undefined, from_phone_number_like?: string | undefined, to_phone_number_like?: string | undefined, from_channel_account_id?: (string | Array) | undefined, to_channel_account_id?: (string | Array) | undefined, from_channel_profile_id?: (number | Array) | undefined, to_channel_profile_id?: (number | Array) | undefined, user_id?: (number | Array) | undefined, team_id?: (number | Array) | undefined, queue_id?: (number | Array) | undefined, campaign_id?: (number | Array) | undefined, related_call_id?: (string | Array) | undefined, destination_country?: (string | Array) | undefined, conversation_score?: (number | RangeFilterFloat) | undefined, duration_overall?: (number | RangeFilterFloat) | undefined, duration_dialing?: (number | RangeFilterFloat) | undefined, duration_ivr?: (number | RangeFilterFloat) | undefined, duration_queue?: (number | RangeFilterFloat) | undefined, duration_talk?: (number | RangeFilterFloat) | undefined, duration_hold?: (number | RangeFilterFloat) | undefined, started_at?: (string | RangeFilterDateTime) | undefined, ended_at?: (string | RangeFilterDateTime) | undefined}; export type GetApiApiV4ReportingCdrsOptions = {baseUrl: string, StaticKey?: string | undefined, fetch?: typeof fetch | undefined}; export const cdrCallDisposition = z.enum(["busy", "answered", "vm_answered", "machine_answered", "abandoned", "dialer_abandoned", "system_abandoned", "rejected", "system_rejected", "no_answer", "missed", "failed"]); export const cdrPartyPhoneNumber = z.object({type: z.literal("phone_number").optional(), phone_number: z.string().optional()}); export const wrapupCodeSnapshot = z.object({code_snapshot: z.string().optional()}); export const cdrRecordCall = z.object({id: z.string().optional(), type: z.literal("call").optional(), subtype: z.enum(["inbound", "outbound", "click_to_call", "dialer", "scheduled_callback", "queue_callback"]).optional(), disposition: cdrCallDisposition.optional(), from: cdrPartyPhoneNumber.nullable().optional(), to: cdrPartyPhoneNumber.nullable().optional(), account_id: z.string().nullable().optional(), user: z.object({id: z.number().int().optional(), name: z.string().optional(), email: z.string().optional(), name_snapshot: z.string().nullable().optional(), deleted: z.boolean().optional()}).nullable().optional(), cost: z.string().nullable().optional(), tags: z.array(z.enum(["barge_in", "blind_transfer", "call_score", "conference", "consult_transfer", "forward", "intercept", "monitor", "terminate", "whisper"])).nullable().optional(), started_at: z.string().optional(), ended_at: z.string().nullable().optional(), data: z.object({queue: z.object({id: z.number().int().optional(), name: z.string().optional(), name_snapshot: z.string().nullable().optional(), deleted: z.boolean().optional()}).nullable().optional(), campaign: z.object({id: z.number().int().optional(), name: z.string().optional(), name_snapshot: z.string().nullable().optional(), deleted: z.boolean().optional()}).nullable().optional(), campaign_lead_id: z.number().int().nullable().optional(), destination_country: z.string().nullable().optional(), durations: z.object({overall: z.number().optional(), dialing: z.number().optional(), ivr: z.number().optional(), queue: z.number().optional(), talk: z.number().optional(), hold: z.number().optional()}).optional(), end_reason: z.enum(["agent", "busy", "customer", "dncr_failure", "dncr_restricted", "failed", "machine", "machine_answered", "manager", "no_answer", "rejected", "remote_party", "system_reject", "timeout"]).nullable().optional(), wrapup_code: wrapupCodeSnapshot.nullable().optional(), summary: z.string().nullable().optional(), conversation_score: z.number().nullable().optional(), mos: z.number().nullable().optional(), mos_tc: z.number().nullable().optional()}).optional()}); export const cdrPartyChannelAccount = z.object({type: z.literal("channel_account").optional(), channel_account: z.object({id: z.string().optional(), channel: z.enum(["chat", "facebook", "instagram", "telegram", "viber", "whatsapp"]).optional(), identifier: z.string().optional(), data: z.object({name: z.string().nullable(), username: z.string().nullable().optional()}).optional(), name_snapshot: z.string().nullable().optional(), deleted: z.boolean().optional()}).optional()}); export const cdrPartyChannelProfile = z.object({type: z.literal("channel_profile").optional(), channel_profile: z.object({id: z.number().int().optional(), channel: z.enum(["chat", "facebook", "instagram", "telegram", "viber", "whatsapp"]).optional(), identifier: z.string().nullable().optional(), name: z.string().optional(), name_snapshot: z.string().nullable().optional(), deleted: z.boolean().optional()}).optional()}); export const cdrServiceLevelAgreement = z.object({value: z.number().optional(), status: z.enum(["ok", "strained", "failed"]).optional()}); export const cdrRecordConversation = z.object({id: z.string().optional(), type: z.literal("conversation").optional(), subtype: z.enum(["chat", "facebook", "instagram", "telegram", "viber", "whatsapp"]).optional(), disposition: z.enum(["active", "archived", "initiated", "waiting"]).optional(), from: z.discriminatedUnion("type", [cdrPartyChannelAccount, cdrPartyChannelProfile]).nullable().optional(), to: z.discriminatedUnion("type", [cdrPartyChannelAccount, cdrPartyChannelProfile]).nullable().optional(), account_id: z.string().nullable().optional(), user: z.object({id: z.number().int().optional(), name: z.string().optional(), email: z.string().optional(), name_snapshot: z.string().nullable().optional(), deleted: z.boolean().optional()}).nullable().optional(), cost: z.string().nullable().optional(), tags: z.array(z.enum(["barge_in", "blind_transfer", "call_score", "conference", "consult_transfer", "forward", "intercept", "monitor", "terminate", "whisper"])).nullable().optional(), started_at: z.string().optional(), ended_at: z.string().nullable().optional(), data: z.object({channel: z.string().optional(), flow: z.object({id: z.string().optional(), name: z.string().optional(), name_snapshot: z.string().nullable().optional(), deleted: z.boolean().optional()}).nullable().optional(), queue: z.object({id: z.number().int().optional(), name: z.string().optional(), name_snapshot: z.string().nullable().optional(), deleted: z.boolean().optional()}).nullable().optional(), wrapup_code: wrapupCodeSnapshot.nullable().optional(), durations: z.object({overall: z.number().optional(), queue: z.number().optional(), talk: z.number().optional()}).optional(), service_level_agreements: z.object({first_response: cdrServiceLevelAgreement.nullable().optional(), resolution: cdrServiceLevelAgreement.nullable().optional(), avg_reply: cdrServiceLevelAgreement.nullable().optional()}).optional()}).optional()}); export const callShort = z.object({id: z.string().optional()}); export const cdrRecordHlrLookup = z.object({id: z.string().optional(), type: z.literal("hlr_lookup").optional(), subtype: z.literal("hlr_lookup").optional(), disposition: z.enum(["available", "unavailable", "unknown"]).optional(), from: cdrPartyPhoneNumber.nullable().optional(), to: cdrPartyPhoneNumber.nullable().optional(), account_id: z.string().nullable().optional(), user: z.object({id: z.number().int().optional(), name: z.string().optional(), email: z.string().optional(), name_snapshot: z.string().nullable().optional(), deleted: z.boolean().optional()}).nullable().optional(), cost: z.string().nullable().optional(), tags: z.array(z.enum(["barge_in", "blind_transfer", "call_score", "conference", "consult_transfer", "forward", "intercept", "monitor", "terminate", "whisper"])).nullable().optional(), started_at: z.string().optional(), ended_at: z.string().nullable().optional(), data: z.object({reachable: z.boolean().optional(), roaming: z.boolean().nullable().optional(), related_call: callShort.nullable().optional()}).optional()}); export const cdrRecordSms = z.object({id: z.string().optional(), type: z.literal("sms").optional(), subtype: z.literal("outbound_sms").optional(), disposition: z.enum(["sent", "delivered", "not_sent", "not_delivered", "route_unavailable"]).optional(), from: cdrPartyPhoneNumber.nullable().optional(), to: cdrPartyPhoneNumber.nullable().optional(), account_id: z.string().nullable().optional(), user: z.object({id: z.number().int().optional(), name: z.string().optional(), email: z.string().optional(), name_snapshot: z.string().nullable().optional(), deleted: z.boolean().optional()}).nullable().optional(), cost: z.string().nullable().optional(), tags: z.array(z.enum(["barge_in", "blind_transfer", "call_score", "conference", "consult_transfer", "forward", "intercept", "monitor", "terminate", "whisper"])).nullable().optional(), started_at: z.string().optional(), ended_at: z.string().nullable().optional(), data: z.object({sent: z.boolean().optional(), segments_count: z.number().int().optional(), message: z.string().optional(), canned_message: z.object({id: z.number().int().optional(), name: z.string().optional(), name_snapshot: z.string().nullable().optional(), deleted: z.boolean().optional()}).nullable().optional(), related_call: callShort.nullable().optional()}).optional()}); export const cdr = z.discriminatedUnion("type", [cdrRecordCall, cdrRecordConversation, cdrRecordHlrLookup, cdrRecordSms]); export const getApiApiV4ReportingCdrsResponse = z.object({cdrs: z.array(cdr).optional(), metadata: z.object({total: z.number().int().optional(), next_cursor: z.string().nullable().optional()}).optional()}); 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 }; /** * Get list of CDRs * * GET /api/v4/reporting/cdrs */ export const getApiApiV4ReportingCdrs = async (args: GetApiApiV4ReportingCdrsArgs, options: GetApiApiV4ReportingCdrsOptions) => { const {baseUrl, fetch: fetchFn = fetch, StaticKey} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json") if (args.limit != null) query.append("limit", String(args.limit)) if (args.offset != null) query.append("offset", String(args.offset)) if (args.cursor != null) query.append("cursor", String(args.cursor)) if (args.type != null) query.append("type", String(args.type)) if (args.account_id != null) query.append("account_id", String(args.account_id)) if (args.subtype != null) query.append("subtype", String(args.subtype)) if (args.disposition != null) query.append("disposition", String(args.disposition)) if (args.wrapup_code != null) query.append("wrapup_code", String(args.wrapup_code)) if (args.end_reason != null) query.append("end_reason", String(args.end_reason)) if (args.tag != null) query.append("tag", String(args.tag)) if (args.from_phone_number != null) query.append("from_phone_number", String(args.from_phone_number)) if (args.to_phone_number != null) query.append("to_phone_number", String(args.to_phone_number)) if (args.from_phone_number_like != null) query.append("from_phone_number_like", String(args.from_phone_number_like)) if (args.to_phone_number_like != null) query.append("to_phone_number_like", String(args.to_phone_number_like)) if (args.from_channel_account_id != null) query.append("from_channel_account_id", String(args.from_channel_account_id)) if (args.to_channel_account_id != null) query.append("to_channel_account_id", String(args.to_channel_account_id)) if (args.from_channel_profile_id != null) query.append("from_channel_profile_id", String(args.from_channel_profile_id)) if (args.to_channel_profile_id != null) query.append("to_channel_profile_id", String(args.to_channel_profile_id)) if (args.user_id != null) query.append("user_id", String(args.user_id)) if (args.team_id != null) query.append("team_id", String(args.team_id)) if (args.queue_id != null) query.append("queue_id", String(args.queue_id)) if (args.campaign_id != null) query.append("campaign_id", String(args.campaign_id)) if (args.related_call_id != null) query.append("related_call_id", String(args.related_call_id)) if (args.destination_country != null) query.append("destination_country", String(args.destination_country)) if (args.conversation_score != null) query.append("conversation_score", String(args.conversation_score)) if (args.duration_overall != null) query.append("duration_overall", String(args.duration_overall)) if (args.duration_dialing != null) query.append("duration_dialing", String(args.duration_dialing)) if (args.duration_ivr != null) query.append("duration_ivr", String(args.duration_ivr)) if (args.duration_queue != null) query.append("duration_queue", String(args.duration_queue)) if (args.duration_talk != null) query.append("duration_talk", String(args.duration_talk)) if (args.duration_hold != null) query.append("duration_hold", String(args.duration_hold)) if (args.started_at != null) query.append("started_at", String(args.started_at)) if (args.ended_at != null) query.append("ended_at", String(args.ended_at)) if (StaticKey !== undefined) headers.set('authorization', `Bearer ${StaticKey}`) const url = `${baseUrl.replace(/\/+$/, '')}/api/v4/reporting/cdrs${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(getApiApiV4ReportingCdrsResponse, await res.json()) };