import { API_BASE_URL } from './apiBase' export type DashboardResponse = { metrics: { voice_calls: number messages_sent: number delivery_rate: number api_requests: number balance: number } activity: Array<{ id: string type: string title: string detail: string status: string occurred_at: string }> } export async function getDashboard(signal?: AbortSignal): Promise { const response = await fetch(`${API_BASE_URL}/api/v1/dashboard`, { signal, credentials: 'include', }) if (!response.ok) throw new Error(`Dashboard request failed: ${response.status}`) return response.json() as Promise } export type AuthUser = { id: string email: string name: string avatar_url: string | null email_verified: boolean has_password: boolean providers: string[] } async function authRequest(path: string, init?: RequestInit): Promise { const response = await fetch(`${API_BASE_URL}/api/v1/auth${path}`, { ...init, credentials: 'include', headers: { 'Content-Type': 'application/json', ...init?.headers }, }) if (!response.ok) { const body = await response.json().catch(() => null) throw new Error(body?.detail ?? 'Authentication request failed') } return response.json() as Promise } export const authApi = { me: () => authRequest('/me'), login: (email: string, password: string) => authRequest('/login', { method: 'POST', body: JSON.stringify({ email, password }) }), register: (name: string, email: string, password: string) => authRequest('/register', { method: 'POST', body: JSON.stringify({ name, email, password }) }), setPassword: (password: string, currentPassword?: string) => authRequest('/password', { method: 'POST', body: JSON.stringify({ password, current_password: currentPassword || null }), }), logout: () => fetch(`${API_BASE_URL}/api/v1/auth/logout`, { method: 'POST', credentials: 'include' }), googleUrl: `${API_BASE_URL}/api/v1/auth/google/start`, } export type KycDocument = { id: string document_type: string original_filename: string | null status: 'missing' | 'pending' | 'approved' | 'rejected' review_note: string | null uploaded_at: string | null reviewed_at: string | null } export type KycApplication = { id: string pan_number: string | null gst_number: string | null status: string completed: boolean submitted_at: string | null approved_at: string | null documents: KycDocument[] } export type DltDocument = { id: string document_type: string original_filename: string | null status: 'missing' | 'pending' | 'approved' | 'rejected' review_note: string | null uploaded_at: string | null reviewed_at: string | null } export type DltApplication = { id: string status: string completed: boolean submitted_at: string | null approved_at: string | null pe_tm_chain_binding_id: string documents: DltDocument[] } export type ComplianceStatus = { kyc_completed: boolean dlt_completed: boolean console_unlocked: boolean } async function jsonRequest(path: string, init?: RequestInit): Promise { const response = await fetch(`${API_BASE_URL}${path}`, { ...init, credentials: 'include', headers: init?.body instanceof FormData ? init.headers : { 'Content-Type': 'application/json', ...init?.headers }, }) if (!response.ok) { const body = await response.json().catch(() => null) throw new Error(typeof body?.detail === 'string' ? body.detail : 'Request failed') } if (response.status === 204) return undefined as T return response.json() as Promise } export const kycApi = { me: () => jsonRequest('/api/v1/kyc/me'), update: (pan_number: string, gst_number: string) => jsonRequest('/api/v1/kyc/me', { method: 'PUT', body: JSON.stringify({ pan_number, gst_number }) }), upload: (documentType: string, file: File) => { const body = new FormData() body.append('file', file) return jsonRequest(`/api/v1/kyc/documents/${documentType}`, { method: 'POST', body }) }, submit: () => jsonRequest('/api/v1/kyc/submit', { method: 'POST' }), templateUrl: `${API_BASE_URL}/api/v1/kyc/template`, } export const dltApi = { me: () => jsonRequest('/api/v1/dlt/me'), loaUrl: `${API_BASE_URL}/api/v1/dlt/loa`, upload: (documentType: string, file: File) => { const body = new FormData() body.append('file', file) return jsonRequest(`/api/v1/dlt/documents/${documentType}`, { method: 'POST', body }) }, confirmChainBinding: () => jsonRequest('/api/v1/dlt/chain-binding/confirm', { method: 'POST' }), submit: () => jsonRequest('/api/v1/dlt/submit', { method: 'POST' }), } export const complianceApi = { status: () => jsonRequest('/api/v1/compliance/status'), } export type AdminKycApplication = KycApplication & { user: { id: string; name: string; email: string } } export type AdminDltApplication = DltApplication & { user: { id: string; name: string; email: string } } export type SenderConfig = { id: string sender_id: string sender_type: 'promotional' | 'transactional' purpose: string dlt_entity_id: string status: 'pending' | 'approved' | 'rejected' review_note: string | null created_at: string reviewed_at: string | null } export type AdminSenderConfig = SenderConfig & { user: { id: string; name: string; email: string } } export type TemplateConfig = { id: string sender_config_id: string sender_id: string template_name: string template_text: string dlt_template_id: string status: 'pending' | 'approved' | 'rejected' review_note: string | null created_at: string reviewed_at: string | null } export const messagingApi = { listSenders: () => jsonRequest('/api/v1/messaging/senders'), createSender: (payload: { sender_id: string sender_type: string purpose: string dlt_entity_id: string }) => jsonRequest('/api/v1/messaging/senders', { method: 'POST', body: JSON.stringify(payload) }), listTemplates: () => jsonRequest('/api/v1/messaging/templates'), createTemplate: (payload: { sender_config_id: string template_name: string template_text: string dlt_template_id: string }) => jsonRequest('/api/v1/messaging/templates', { method: 'POST', body: JSON.stringify(payload) }), send: (payload: { sender_config_id: string template_id: string numbers: string message: string campaign_name?: string | null }) => jsonRequest('/api/v1/messaging/send', { method: 'POST', body: JSON.stringify(payload) }), } export type CampaignRunMode = 'manual' | 'on_upload' | 'scheduled' export type CampaignCsvUpload = { id: string original_filename: string total_rows: number status: 'validated' | 'running' | 'completed' | 'failed' run_started_at: string | null run_completed_at: string | null created_at: string } export type Campaign = { id: string name: string sender_config_id: string sender_id: string sender_type: 'promotional' | 'transactional' template_id: string template_name: string template_text: string number_column: string variable_columns: string[] run_mode: CampaignRunMode scheduled_at: string | null status: string created_at: string updated_at: string csv_upload_count: number csv_uploads: CampaignCsvUpload[] } export const campaignsApi = { list: () => jsonRequest('/api/v1/campaigns'), get: (campaignId: string) => jsonRequest(`/api/v1/campaigns/${campaignId}`), create: (payload: { name: string sender_config_id: string template_id: string number_column: string variable_columns: string[] run_mode: CampaignRunMode scheduled_at?: string | null }) => jsonRequest('/api/v1/campaigns', { method: 'POST', body: JSON.stringify(payload) }), listCsvUploads: (campaignId: string) => jsonRequest(`/api/v1/campaigns/${campaignId}/csv`), uploadCsv: (campaignId: string, file: File) => { const body = new FormData() body.append('file', file) return jsonRequest(`/api/v1/campaigns/${campaignId}/csv`, { method: 'POST', body }) }, runAutomation: (campaignId: string) => jsonRequest<{ processed_uploads: number; note: string }>(`/api/v1/campaigns/${campaignId}/run`, { method: 'POST' }), runCsv: (campaignId: string, uploadId: string) => jsonRequest( `/api/v1/campaigns/${campaignId}/csv/${uploadId}/run`, { method: 'POST' }, ), } export type ComposeSendResultItem = { to: string success: boolean job_id: string | null message_id: string | null error: string | null } export type ComposeSendResponse = { success: boolean campaign_name: string | null sent: number failed: number sender_id: string template_id: string template_name: string dlt_template_id: string results: ComposeSendResultItem[] } export type ApiKey = { id: string name: string key_prefix: string created_at: string last_used_at: string | null revoked_at: string | null status: 'active' | 'revoked' } export type ApiKeyWithToken = ApiKey & { token: string } export const apiKeysApi = { list: () => jsonRequest('/api/v1/api-keys'), create: (name: string) => jsonRequest('/api/v1/api-keys', { method: 'POST', body: JSON.stringify({ name }) }), regenerate: (keyId: string) => jsonRequest(`/api/v1/api-keys/${keyId}/regenerate`, { method: 'POST' }), revoke: (keyId: string) => fetch(`${API_BASE_URL}/api/v1/api-keys/${keyId}`, { method: 'DELETE', credentials: 'include' }).then((response) => { if (!response.ok) throw new Error('Unable to revoke API key') }), } export type MessageApiDoc = { id: string method: string path: string title: string summary: string authentication: { type: string header: string format: string alternate_header: string } request_body: Record responses: Record }> } export type SendMessageResponse = { success: boolean job_id: string | null message_id: string | null to: string | null sender_id: string template_id: string template_name: string dlt_template_id: string } export const messagesApi = { docs: () => jsonRequest('/api/v1/messages/docs/send'), send: (apiKey: string, payload: { sender_id: string template_id: string to: string message: string }) => fetch(`${API_BASE_URL}/api/v1/messages/send`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}`, }, body: JSON.stringify(payload), }).then(async (response) => { const body = await response.json().catch(() => null) if (!response.ok) { throw new Error(typeof body?.detail === 'string' ? body.detail : 'SMS send request failed') } return body as SendMessageResponse }), } export type UrmiOtpProject = { id: string project_name: string message_label: string app_type: 'web' | 'mobile' app_url: string purpose: string status: 'pending' | 'approved' | 'rejected' review_note: string | null terms_accepted_at: string created_at: string reviewed_at: string | null } export type OtpTemplateDefinition = { type: string name: string content: string sample: string uses: string[] variables: string[] } export type UrmiOtpDocumentation = { project: UrmiOtpProject templates: OtpTemplateDefinition[] api: { method: string path: string implemented: boolean summary: string parameters: Record example: Record } } export type AdminUrmiOtpProject = UrmiOtpProject & { user: { id: string; name: string; email: string } } export type AgentTemplateParameter = { name: string type: string required: boolean description: string example: string } export type AgentMessageTemplate = { id: string slug: string name: string description: string header_text: string | null body_preview: string footer_text: string | null meta_template_name: string language: string category: string parameters: AgentTemplateParameter[] button_label: string | null button_url_base: string | null is_active: boolean subscribed: boolean } export type AgentTemplateDocumentation = { template: AgentMessageTemplate api: { method: string path: string summary: string authentication: { type: string header: string format: string alternate_header?: string } parameters: Record example: Record response_example: Record } notes: string[] } export type AgentMessageApiDoc = { id: string method: string path: string title: string summary: string authentication: { type: string header: string format: string alternate_header?: string } request_body: Record responses: Record }> notes?: string[] available_templates?: Array<{ slug: string; name: string; meta_template_name: string }> } export type AgentMessageSendResponse = { success: boolean template: string to: string customer_name: string report_group: string report_id: string report_url: string | null wa_message_id: string | null dispatch_id: string } export const agentTemplatesApi = { list: () => jsonRequest('/api/v1/agent-templates'), subscribe: (slug: string) => jsonRequest(`/api/v1/agent-templates/${slug}/subscribe`, { method: 'POST' }), unsubscribe: (slug: string) => jsonRequest(`/api/v1/agent-templates/${slug}/subscribe`, { method: 'DELETE' }), documentation: (slug: string) => jsonRequest(`/api/v1/agent-templates/${slug}/docs`), testStatus: (slug: string) => jsonRequest<{ template: string; allowed: boolean; retry_after?: string | null; last_tested_at?: string | null }>( `/api/v1/agent-templates/${slug}/test-status`, ), test: (slug: string, payload: { to: string; customer_name: string; report_group: string; report_id?: string }) => jsonRequest(`/api/v1/agent-templates/${slug}/test`, { method: 'POST', body: JSON.stringify(payload), }), } export const agentMessagesApi = { docs: () => jsonRequest('/api/v1/agent/messages/docs/send'), send: ( apiKey: string, payload: { template: string to: string customer_name: string report_group: string report_id?: string }, ) => fetch(`${API_BASE_URL}/api/v1/agent/messages/send`, { method: 'POST', headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json', }, body: JSON.stringify(payload), }).then(async (response) => { if (!response.ok) { const body = await response.json().catch(() => null) throw new Error(typeof body?.detail === 'string' ? body.detail : 'Request failed') } return response.json() as Promise }), } export type UrmiportGroup = { id: string report_group: string display_name: string | null default_access: 'public' | 'password' password_protected: boolean id_mode: 'auto' | 'manual' report_count: number url_pattern: string created_at: string } export type UrmiportReport = { id: string report_group: string report_id: string access: 'public' | 'password' password_protected: boolean url: string updated_at: string created_at: string } export type UrmiportApiDoc = { id: string method: string path: string title: string summary: string authentication: { type: string header: string format: string alternate_header?: string } request_body: Record responses: Record }> notes?: string[] } export type UrmiportPublishResponse = UrmiportReport & { success: boolean } export const urmiportApi = { docs: () => jsonRequest('/api/v1/urmiport/docs'), listGroups: () => jsonRequest('/api/v1/urmiport/groups'), createGroup: (payload: { report_group: string display_name?: string default_access: 'public' | 'password' password?: string id_mode: 'auto' | 'manual' }) => jsonRequest('/api/v1/urmiport/groups', { method: 'POST', body: JSON.stringify(payload), }), listReports: (slug: string) => jsonRequest(`/api/v1/urmiport/groups/${slug}/reports`), viewReport: (group: string, reportId: string) => fetch(`${API_BASE_URL}/api/v1/urmiport/view/${group}/${reportId}`).then(async (response) => { if (!response.ok) { const body = await response.json().catch(() => null) throw new Error(typeof body?.detail === 'string' ? body.detail : 'Report not found') } return response.json() as Promise<{ report_group: string report_id: string access: string requires_password: boolean html: string | null }> }), unlockReport: (group: string, reportId: string, password: string) => fetch(`${API_BASE_URL}/api/v1/urmiport/view/${group}/${reportId}/unlock`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password }), }).then(async (response) => { if (!response.ok) { const body = await response.json().catch(() => null) throw new Error(typeof body?.detail === 'string' ? body.detail : 'Unable to unlock report') } return response.json() as Promise<{ html: string }> }), publish: ( apiKey: string, payload: { report_group: string report_id?: string body: string access?: 'public' | 'password' password?: string }, ) => fetch(`${API_BASE_URL}/api/v1/urmiport/reports`, { method: 'POST', headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json', }, body: JSON.stringify(payload), }).then(async (response) => { if (!response.ok) { const body = await response.json().catch(() => null) throw new Error(typeof body?.detail === 'string' ? body.detail : 'Request failed') } return response.json() as Promise }), } export const urmiOtpApi = { listProjects: () => jsonRequest('/api/v1/urmiotp/projects'), createProject: (payload: { project_name: string message_label: string app_type: 'web' | 'mobile' app_url: string purpose: string terms_accepted: boolean }) => jsonRequest('/api/v1/urmiotp/projects', { method: 'POST', body: JSON.stringify(payload), }), documentation: (projectId: string) => jsonRequest(`/api/v1/urmiotp/projects/${projectId}/documentation`), generate: (payload: { project_name: string template_type: string to: string variables: { otp: string; validity_minutes: string } }) => jsonRequest<{ success: boolean log_id: string project_name: string template_type: string to: string message_preview: string status: string }>('/api/v1/urmiotp/generate', { method: 'POST', body: JSON.stringify(payload), }), } export type PhoneNumber = { id: string phone_number: string monthly_number_price: number inbound_price: number outbound_price: number currency: string status: 'available' | 'assigned' acquisition_type: 'admin_assigned' | 'purchased' | null assigned_at: string | null created_at: string } export type IntegrationEndpoint = { id: string name: string phone_number_id: string phone_number: string url: string enabled: boolean created_at: string updated_at: string signing_secret?: string } export type VoiceSocketEndpoint = IntegrationEndpoint export type SmsDeliveryWebhook = IntegrationEndpoint & { description: string | null event: 'message.delivery.updated' } export type IntegrationsDocs = { title: string summary: string relay: { principle: string latency: string } socket: { title: string description: string events: string[] authentication: { headers: string[] signature: string replay_protection: string } } webhook: { title: string description: string event: string statuses: string[] authentication: { headers: string[] signature: string } example: Record } api_authentication: { header: string format: string note: string } endpoints: Array<{ method: string; path: string; description: string }> } type EndpointPayload = { name: string phone_number_id: string url: string description?: string | null enabled?: boolean } export const integrationsApi = { docs: () => jsonRequest('/api/v1/integrations/docs'), listSockets: () => jsonRequest('/api/v1/integrations/sockets'), createSocket: (payload: EndpointPayload) => jsonRequest('/api/v1/integrations/sockets', { method: 'POST', body: JSON.stringify(payload), }), updateSocket: (id: string, payload: Partial) => jsonRequest(`/api/v1/integrations/sockets/${id}`, { method: 'PATCH', body: JSON.stringify(payload), }), deleteSocket: (id: string) => jsonRequest(`/api/v1/integrations/sockets/${id}`, { method: 'DELETE' }), listWebhooks: () => jsonRequest('/api/v1/integrations/webhooks'), createWebhook: (payload: EndpointPayload) => jsonRequest('/api/v1/integrations/webhooks', { method: 'POST', body: JSON.stringify(payload), }), updateWebhook: (id: string, payload: Partial) => jsonRequest(`/api/v1/integrations/webhooks/${id}`, { method: 'PATCH', body: JSON.stringify(payload), }), deleteWebhook: (id: string) => jsonRequest(`/api/v1/integrations/webhooks/${id}`, { method: 'DELETE' }), testWebhook: (id: string) => jsonRequest<{ delivered: boolean; response_code: number }>( `/api/v1/integrations/webhooks/${id}/test`, { method: 'POST' }, ), } export type VoiceCall = { id: string direction: string from: string to: string status: string stream_id: string | null call_id: string | null bridge_mode: string upstream: string | null error_message: string | null answered_at: string | null ended_at: string | null created_at: string media_url?: string } export type VoiceApiDocs = { title: string summary: string method: string path: string authentication: { header: string format: string alternate_header?: string } request_body: Record response_example: Record relay: { principle: string latency: string } outbound_flow: string[] endpoints: Array<{ method: string; path: string; description: string }> } export const voiceApi = { docs: () => jsonRequest('/api/v1/voice/docs'), createCall: (payload: { to: string; from: string; bridge_mode?: 'console' | 'upstream'; upstream?: string }) => jsonRequest('/api/v1/voice/calls', { method: 'POST', body: JSON.stringify(payload) }), getCall: (callId: string) => jsonRequest(`/api/v1/voice/calls/${callId}`), createCallWithApiKey: ( apiKey: string, payload: { to: string; from: string; bridge_mode?: 'console' | 'upstream'; upstream?: string }, ) => fetch(`${API_BASE_URL}/api/v1/voice/calls`, { method: 'POST', credentials: 'include', headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json', }, body: JSON.stringify(payload), }).then(async (response) => { if (!response.ok) { const body = await response.json().catch(() => null) throw new Error(typeof body?.detail === 'string' ? body.detail : 'Request failed') } return response.json() as Promise }), } export type AdminPhoneNumber = PhoneNumber & { user: { id: string; name: string; email: string } | null } export const numbersApi = { catalog: () => jsonRequest('/api/v1/numbers/catalog'), mine: () => jsonRequest('/api/v1/numbers/mine'), purchase: (numberId: string) => jsonRequest(`/api/v1/numbers/${numberId}/purchase`, { method: 'POST' }), } export type AdminTemplateConfig = TemplateConfig & { user: { id: string; name: string; email: string } } export type AdminWhatsAppConfig = { business_phone: string phone_number_id: string | null business_account_id: string | null meta_app_id: string | null access_token_configured: boolean app_secret_configured: boolean webhook_url: string verify_token_configured: boolean ready_to_send: boolean supported_operations: string[] } export type AdminWhatsAppMessage = { id: string direction: 'inbound' | 'outbound' wa_message_id: string | null from: string to: string body: string | null message_type: string status: string media?: { id?: string mime_type?: string caption?: string filename?: string } | null created_at: string } export type AdminWhatsAppTemplate = { id?: string name: string language: string status?: string category?: string } export type AdminWhatsAppSendPayload = { to: string message_type: 'text' | 'template' | 'image' | 'document' | 'audio' | 'video' | 'interactive_buttons' | 'interactive_list' | 'reaction' text?: string reply_to?: string template_name?: string template_language?: string template_components?: Record[] media_link?: string media_caption?: string media_filename?: string interactive_body?: string interactive_header?: string interactive_footer?: string interactive_buttons?: Array<{ id: string; title: string }> interactive_list_button?: string interactive_list_sections?: Record[] reaction_message_id?: string reaction_emoji?: string } export type AdminConnectionTokens = { users: Array<{ id: string name: string email: string whatsapp_accounts: Array<{ id: string name: string status: string source: 'token' | 'signup' access_token: string waba_id: string phones: Array<{ id: string; display_phone: string; phone_number_id: string; status: string }> updated_at: string }> facebook: { status: string user_name: string access_token: string expires_at: string | null connected_at: string | null } | null }> } export const adminApi = { me: () => jsonRequest<{ username: string }>('/api/v1/admin/me'), login: (username: string, password: string) => jsonRequest<{ username: string }>('/api/v1/admin/login', { method: 'POST', body: JSON.stringify({ username, password }), }), logout: () => fetch(`${API_BASE_URL}/api/v1/admin/logout`, { method: 'POST', credentials: 'include' }), listKyc: () => jsonRequest('/api/v1/admin/compliance/kyc'), reviewDocument: (applicationId: string, documentId: string, status: 'approved' | 'rejected', review_note?: string) => jsonRequest(`/api/v1/admin/compliance/kyc/${applicationId}/documents/${documentId}`, { method: 'PATCH', body: JSON.stringify({ status, review_note: review_note || null }), }), listDlt: () => jsonRequest('/api/v1/admin/compliance/dlt'), reviewDltDocument: (applicationId: string, documentId: string, status: 'approved' | 'rejected', review_note?: string) => jsonRequest(`/api/v1/admin/compliance/dlt/${applicationId}/documents/${documentId}`, { method: 'PATCH', body: JSON.stringify({ status, review_note: review_note || null }), }), listSenders: () => jsonRequest('/api/v1/admin/compliance/senders'), reviewSender: (senderId: string, status: 'approved' | 'rejected', review_note?: string) => jsonRequest(`/api/v1/admin/compliance/senders/${senderId}`, { method: 'PATCH', body: JSON.stringify({ status, review_note: review_note || null }), }), listTemplates: () => jsonRequest('/api/v1/admin/compliance/templates'), reviewTemplate: (templateId: string, status: 'approved' | 'rejected', review_note?: string) => jsonRequest(`/api/v1/admin/compliance/templates/${templateId}`, { method: 'PATCH', body: JSON.stringify({ status, review_note: review_note || null }), }), listUrmiOtpProjects: () => jsonRequest('/api/v1/admin/compliance/urmiotp-projects'), reviewUrmiOtpProject: (projectId: string, status: 'approved' | 'rejected', review_note?: string) => jsonRequest(`/api/v1/admin/compliance/urmiotp-projects/${projectId}`, { method: 'PATCH', body: JSON.stringify({ status, review_note: review_note || null }), }), listPhoneNumbers: () => jsonRequest('/api/v1/admin/phone-numbers'), createPhoneNumber: (payload: { phone_number: string monthly_number_price: number inbound_price: number outbound_price: number user_email?: string | null }) => jsonRequest('/api/v1/admin/phone-numbers', { method: 'POST', body: JSON.stringify(payload), }), updatePhoneNumber: (numberId: string, payload: { monthly_number_price?: number inbound_price?: number outbound_price?: number user_email?: string | null release?: boolean }) => jsonRequest(`/api/v1/admin/phone-numbers/${numberId}`, { method: 'PATCH', body: JSON.stringify(payload), }), connectionTokens: () => jsonRequest('/api/v1/admin/connection-tokens'), whatsappConfig: () => jsonRequest('/api/v1/admin/whatsapp/config'), updateWhatsAppConfig: (payload: { phone_number_id?: string business_account_id?: string app_secret?: string }) => jsonRequest('/api/v1/admin/whatsapp/config', { method: 'PATCH', body: JSON.stringify(payload), }), listWhatsAppMessages: (limit = 50, peer?: string) => jsonRequest( `/api/v1/admin/whatsapp/messages?limit=${limit}${peer ? `&peer=${encodeURIComponent(peer)}` : ''}`, ), listWhatsAppTemplates: () => jsonRequest<{ data?: AdminWhatsAppTemplate[] }>('/api/v1/admin/whatsapp/templates'), markWhatsAppRead: (waMessageId: string) => jsonRequest<{ success?: boolean }>(`/api/v1/admin/whatsapp/messages/${encodeURIComponent(waMessageId)}/read`, { method: 'POST', }), sendWhatsAppMessage: (payload: AdminWhatsAppSendPayload) => jsonRequest('/api/v1/admin/whatsapp/messages/send', { method: 'POST', body: JSON.stringify(payload), }), } export type LogType = 'outbound' | 'inbound' | 'sms' | 'otp' export type LogEntry = { id: string from?: string to?: string template?: string otp_type?: string project?: string socket_name?: string status?: string time: string } export type LogsResponse = { type: LogType page: number page_size: number total: number total_pages: number from: string to: string items: LogEntry[] } export const logsApi = { list: (params: { type: LogType; from: string; to: string; page?: number }) => { const query = new URLSearchParams({ type: params.type, from: params.from, to: params.to, page: String(params.page ?? 1), }) return jsonRequest(`/api/v1/logs?${query.toString()}`) }, } export type AnalyticsPeriod = 'weekly' | 'monthly' | 'quarterly' export type AnalyticsService = 'sms' | 'inbound' | 'outbound' | 'otp' export type AnalyticsGroupBy = 'dlt_sender' | 'dlt_template' | 'did' | 'socket' | 'project' | 'otp_type' export type UsageAnalytics = { period: AnalyticsPeriod service: AnalyticsService group_by: AnalyticsGroupBy from: string to: string totals: { sms: number inbound: number outbound: number otp: number } timeline: Array<{ label: string period_start: string sms: number inbound: number outbound: number otp: number }> breakdown: Array<{ label: string detail: string count: number }> } export const analyticsApi = { usage: (params: { period: AnalyticsPeriod; service: AnalyticsService; group_by: AnalyticsGroupBy }) => { const query = new URLSearchParams({ period: params.period, service: params.service, group_by: params.group_by, }) return jsonRequest(`/api/v1/analytics/usage?${query.toString()}`) }, } export type WhatsAppSetup = { webhook_url: string verify_token_configured: boolean verify_token_visible: boolean api_version: string recommended_fields: string[] docs_url: string } export type WhatsAppSignupConfig = { app_id: string | null config_id: string | null api_version: string sdk_version?: string ready: boolean } export type WhatsAppPhoneAsset = { id: string phone_number_id: string display_phone: string verified_name: string | null quality: string | null status: string code_verification_status: string | null needs_otp?: boolean needs_registration?: boolean } export type WhatsAppAccount = { id: string waba_id_masked: string | null name: string business_name: string | null business_id_masked: string | null status: string usable: boolean messaging_status: string management_status: string templates_status: string webhooks_status: string template_count: number quality: string | null primary_phone: WhatsAppPhoneAsset | null phones: WhatsAppPhoneAsset[] last_error: string | null updated_at: string } export type WhatsAppSignupResult = { status: string message: string accounts: WhatsAppAccount[] } export type WhatsAppTemplateRow = { name: string | null category: string | null language: string | null status: string | null quality: string | null } export type WhatsAppDeveloperConfig = { webhook_url: string verify_token_configured: boolean verify_token_visible: boolean recommended_fields: string[] webhooks_status: string docs_url: string } export type WhatsAppAccountLogs = { audits: { id: string; event: string; detail: string; created_at: string }[] events: { id: string; field: string; event_type: string; from_wa_id: string | null; status: string | null; created_at: string }[] } export type CustomerWhatsAppMessage = { id: string direction: 'inbound' | 'outbound' wa_message_id: string | null from: string to: string body: string | null message_type: string status: string contact_name?: string | null media?: { id?: string; mime_type?: string; caption?: string; filename?: string } | null created_at: string } export type CustomerWhatsAppSend = { to: string message_type?: string text?: string reply_to?: string template_name?: string template_language?: string media_link?: string media_caption?: string media_filename?: string interactive_body?: string interactive_buttons?: { id: string; title: string }[] reaction_message_id?: string reaction_emoji?: string } export type WhatsAppWebhookEvent = { id: string waba_id: string | null phone_number_id: string | null field: string event_type: string from_wa_id: string | null message_id: string | null status: string | null created_at: string } export const whatsappApi = { setup: () => jsonRequest('/api/v1/whatsapp/setup'), events: () => jsonRequest('/api/v1/whatsapp/events'), signupConfig: () => jsonRequest('/api/v1/whatsapp/signup/config'), accounts: () => jsonRequest('/api/v1/whatsapp/accounts'), connectToken: (payload: { access_token: string; waba_id: string; phone_number_id?: string | null; app_secret?: string | null }) => jsonRequest('/api/v1/whatsapp/connections/token', { method: 'POST', body: JSON.stringify(payload), }), completeSignup: (payload: { event: string code?: string | null waba_id?: string | null phone_number_id?: string | null business_id?: string | null error_message?: string | null }) => jsonRequest('/api/v1/whatsapp/signup/complete', { method: 'POST', body: JSON.stringify(payload), }), account: (accountId: string) => jsonRequest(`/api/v1/whatsapp/accounts/${accountId}`), refreshAccount: (accountId: string) => jsonRequest(`/api/v1/whatsapp/accounts/${accountId}/refresh`, { method: 'POST' }), disconnectAccount: (accountId: string) => jsonRequest(`/api/v1/whatsapp/accounts/${accountId}/disconnect`, { method: 'POST' }), registerPhone: (accountId: string, phoneId: string, pin?: string) => jsonRequest(`/api/v1/whatsapp/accounts/${accountId}/phones/${phoneId}/register`, { method: 'POST', body: JSON.stringify({ pin: pin || null }), }), enableDelivery: (accountId: string, appSecret: string) => jsonRequest(`/api/v1/whatsapp/accounts/${accountId}/delivery`, { method: 'POST', body: JSON.stringify({ app_secret: appSecret }), }), messages: (accountId: string) => jsonRequest(`/api/v1/whatsapp/accounts/${accountId}/messages`), sendAccountMessage: (accountId: string, payload: CustomerWhatsAppSend) => jsonRequest(`/api/v1/whatsapp/accounts/${accountId}/messages/send`, { method: 'POST', body: JSON.stringify(payload), }), templates: (accountId: string) => jsonRequest<{ available: boolean; templates: WhatsAppTemplateRow[] }>(`/api/v1/whatsapp/accounts/${accountId}/templates`), createMarketingTemplate: (accountId: string, payload: { name: string; language: string; body: string }) => jsonRequest<{ id?: string | null; name: string; language: string; category: string; status: string }>( `/api/v1/whatsapp/accounts/${accountId}/templates`, { method: 'POST', body: JSON.stringify(payload) }, ), sendBroadcast: (accountId: string, payload: { template_name: string template_language: string recipients: string[] body_parameters?: string[] product_policy?: 'STRICT' | 'CLOUD_API_FALLBACK' use_sample_header?: boolean }) => jsonRequest<{ template_name: string template_language: string product_policy: string sent: number failed: number results: { to: string; status: string; error?: string; message_id?: string | null }[] }>(`/api/v1/whatsapp/accounts/${accountId}/broadcasts`, { method: 'POST', body: JSON.stringify(payload), }), logs: (accountId: string) => jsonRequest(`/api/v1/whatsapp/accounts/${accountId}/logs`), developer: (accountId: string) => jsonRequest(`/api/v1/whatsapp/accounts/${accountId}/developer`), sendText: (payload: { to: string; body: string; phone_number_id: string }) => jsonRequest>('/api/v1/whatsapp/messages/send', { method: 'POST', body: JSON.stringify(payload), }), } export type WaTemplateDraft = { account_id: string phone_number_id: string name: string language: string category: 'MARKETING' | 'UTILITY' | 'AUTHENTICATION' | '' subtype: string parameter_format: 'POSITIONAL' | 'NAMED' header: { type: 'NONE' | 'TEXT' | 'IMAGE' | 'VIDEO' | 'DOCUMENT' | 'LOCATION' text: string examples: string[] handle: string sample_id: string sample_name: string sample_type: string preview_name: string preview_url: string } body: string body_examples: string[] footer: string buttons: { type: 'URL' | 'PHONE_NUMBER' | 'QUICK_REPLY' | 'COPY_CODE' | 'CATALOG' | 'FLOW' text: string url: string phone: string example: string flow_id: string screen: string }[] auth: { otp_type: 'COPY_CODE' add_security_recommendation: boolean code_expiration_minutes: number copy_label: string } } export type WaManagedTemplate = { id: string account_id: string account_name: string waba_name: string connection_name: string connection_method: string phone_number_id: string | null display_phone: string phones: string[] meta_template_id: string | null name: string language: string category: string subtype: string status: string quality: string | null rejected_reason: string | null parameter_format: string header_format: string sample_stored: boolean sample_name: string definition: Partial components: unknown[] messages_sent: number messages_sent_source: string created_at: string updated_at: string analytics?: { source: string note: string sent: number by_status: Record } } export const waTemplateApi = { languages: () => jsonRequest<{ code: string; label: string }[]>('/api/v1/whatsapp/message-templates/languages'), list: (accountId?: string) => jsonRequest<{ templates: WaManagedTemplate[]; summary: { total: number; approved: number; pending: number; rejected: number; paused: number }; warnings: string[] }>( `/api/v1/whatsapp/message-templates${accountId ? `?account_id=${accountId}` : ''}`, ), get: (templateId: string) => jsonRequest(`/api/v1/whatsapp/message-templates/${templateId}`), create: (payload: WaTemplateDraft) => jsonRequest('/api/v1/whatsapp/message-templates', { method: 'POST', body: JSON.stringify(payload) }), resubmit: (templateId: string, payload: WaTemplateDraft) => jsonRequest(`/api/v1/whatsapp/message-templates/${templateId}/resubmit`, { method: 'POST', body: JSON.stringify(payload) }), remove: (templateId: string) => jsonRequest<{ status: string }>(`/api/v1/whatsapp/message-templates/${templateId}`, { method: 'DELETE' }), uploadHeader: (accountId: string, file: File) => { const body = new FormData() body.append('account_id', accountId) body.append('file', file) return jsonRequest<{ handle: string; format: string; sample_id: string; sample_name: string; sample_type: string }>('/api/v1/whatsapp/message-templates/media', { method: 'POST', body }) }, saveSample: (templateId: string, file: File) => { const body = new FormData() body.append('file', file) return jsonRequest(`/api/v1/whatsapp/message-templates/${templateId}/sample`, { method: 'POST', body }) }, sampleBlob: async (templateId: string) => { const response = await fetch(`${API_BASE_URL}/api/v1/whatsapp/message-templates/${templateId}/sample`, { credentials: 'include' }) if (!response.ok) throw new Error('The sample file could not be opened') return response.blob() }, }