import qs from 'qs' import type { ApiClient } from '../../core/ApiClient' import { BaseCrudApi } from '../../core/BaseCrudApi' import type { ListQuery, Paginated } from '../../core/types' import type { Contact, CountContactsQuery, CountMediaResult, CreateContactPayload, ExistsContactsParams, ExportTemplatePayload, TransferTicketPayload, UpdateContactPayload, } from './types' export class ContactsApi extends BaseCrudApi { constructor(client: ApiClient) { super(client, '/contacts') } /** * Downloads a CSV template for importing contacts, with columns based on the service type. * @permissions contacts.view AND contacts.export */ exportTemplate(body: ExportTemplatePayload, headers?: Record): Promise { return this.client.post( `${this.basePath}/export-template`, body, headers, ) } /** * Returns the total number of contacts matching the given query filters. * @permissions contacts.view */ count(query?: CountContactsQuery, headers?: Record): Promise<{ count: number }> { const queryString = query ? `?${qs.stringify(query)}` : '' return this.client.get<{ count: number }>(`${this.basePath}/count${queryString}`, headers) } /** * Returns the total number of contacts matching the given filters (POST variant for complex queries). * @permissions contacts.view */ countPost( body: CountContactsQuery, headers?: Record, ): Promise<{ count: number }> { return this.client.post<{ count: number }, CountContactsQuery>( `${this.basePath}/count`, body, headers, ) } /** * Returns a paginated list of contacts available for message forwarding. * @permissions contacts.view */ forward( query?: ListQuery, headers?: Record, ): Promise> { const queryString = query ? `?${qs.stringify(query)}` : '' return this.client.get>(`${this.basePath}/forward${queryString}`, headers) } /** * Checks whether contacts exist for the given phone numbers on a specific service. Returns a map of number to boolean. * @permissions contacts.view */ exists( params: ExistsContactsParams, headers?: Record, ): Promise> { const queryString = `?${qs.stringify(params)}` return this.client.get>( `${this.basePath}/exists${queryString}`, headers, ) } /** * Returns media counts grouped by type (e.g. image, video, document, link) for a contact. * @permissions contacts.view */ countMedia(contactId: string, headers?: Record): Promise { return this.client.get(`${this.basePath}/${contactId}/media/count`, headers) } /** * Returns a paginated list of contacts using POST body for advanced filtering. * @permissions contacts.view */ list( body: Record, headers?: Record, ): Promise> { return this.client.post, Record>( `${this.basePath}/list`, body, headers, ) } /** * Opens (or transfers) the contact's ticket to a department, and optionally a user. * This is the real ticket-open flow used by the UI — there is no `POST /tickets`. * @permissions tickets.transfer.all OR tickets.transfer.me */ transferTicket( contactId: string, payload: TransferTicketPayload, headers?: Record, ): Promise { return this.client.post( `${this.basePath}/${contactId}/ticket/transfer`, payload, headers, ) } }