Skip to Content
CampanhaLer dados

Usando a API para ler dados de uma campanha

Para exportar dados de ligações usando a API é preciso utilizar a interface do sistema para gerar um token de acesso da API e fazer ligações.

Cumprindo esses requisitos, siga os passos descritos abaixo:

1. Obter ligações completadas incluindo dados do contato

Um caso de uso comum é obter as ligações completadas de um dia, trazendo junto com elas os dados preenchidos dos seus contatos. Para isso é necessário usar o parâmetro include e a entidade campaign_contact como o dado a ser incluído. Isso fará com que o fim do JSON de ligações tenha um JSON aninhado com todos os dados de contato.

Exemplo de chamada

GET
/api/v1/campaign_completed_calls
Params
Authorization
KeyValue
var axios = require("axios").default;

var options = {
  method: 'GET',
  url: 'https://exemplo.callix.com.br/api/v1/campaign_completed_calls',
  headers: {
    'Content-Type': 'application/json',
    Authorization: 'Bearer {seu_token
  }'}
};

axios.request(options).then(function (response) {
  console.log(response.data);
}).catch(function (error) {
  console.error(error);
});

Para testar, configure na página de Autenticação.

Exemplo de resposta

{ "meta": { "count": 1 }, "links": { "first": "/api/v1/campaign_completed_calls?filter%5Bstarted_at%5D=2021-08-30T03%3A00%3A00.000Z%2C2021-08-31T02%3A59%3A59.000Z&include=campaign_contact&page%5Blimit%5D=100&page%5Boffset%5D=0", "last": "/api/v1/campaign_completed_calls?filter%5Bstarted_at%5D=2021-08-30T03%3A00%3A00.000Z%2C2021-08-31T02%3A59%3A59.000Z&include=campaign_contact&page%5Blimit%5D=100&page%5Boffset%5D=0" }, "data": [ { "type": "campaign_completed_calls", "id": "82518", "attributes": { "started_at": "2021-08-31T02:23:57.013000Z", "ended_at": "2021-08-31T02:24:16.453000Z", "duration": 19, "destination_phone": "+5511993198168", "protocol": 3038, "note": null, "answered_at": "2021-08-31T02:24:08.713000Z", "hangup_cause": 2, "service_duration": 8, "has_audio": true, "audio_link": "/api/v1/campaign_completed_calls/82518/audio" }, "relationships": { "campaign": { "data": { "type": "campaigns", "id": "84" }, "links": { "self": "/api/v1/campaign_completed_calls/82518/relationships/campaign", "related": "/api/v1/campaign_completed_calls/82518/campaign" } }, "campaign_list": { "data": { "type": "campaign_lists", "id": "168" }, "links": { "self": "/api/v1/campaign_completed_calls/82518/relationships/campaign_list", "related": "/api/v1/campaign_completed_calls/82518/campaign_list" } }, "campaign_contact": { "data": { "type": "campaign_contacts", "id": "663468" }, "links": { "self": "/api/v1/campaign_completed_calls/82518/relationships/campaign_contact", "related": "/api/v1/campaign_completed_calls/82518/campaign_contact" } }, "qualification": { "data": { "type": "qualifications", "id": "49" }, "links": { "self": "/api/v1/campaign_completed_calls/82518/relationships/qualification", "related": "/api/v1/campaign_completed_calls/82518/qualification" } }, "agent": { "data": { "type": "users", "id": "25" }, "links": { "self": "/api/v1/campaign_completed_calls/82518/relationships/agent", "related": "/api/v1/campaign_completed_calls/82518/agent" } } }, "links": { "self": "/api/v1/campaign_completed_calls/82518" } } ], "included": [ { "type": "campaign_contacts", "id": "663468", "attributes": { "codigo": "1522", "telefone": "+5511993198168" }, "links": { "self": "/api/v1/campaign_contacts/663468" } } ] }
Formato de data

Os valores de data e hora são formatados de acordo com o padrão ISO-8601 , ou seja, YYYY-MM-DDThh:mm:ss.sssZ. As datas retornadas nas respostas estão convertidas no fuso horário UTC+0, indicado pela letra Z. As datas enviadas também devem seguir esse padrão.

No exemplo acima, estão sendo solicitadas todas as ligações completadas de campanha que foram feitas no mês de outubro/2025. Ou seja, ligações feitas no seguinte intervalo:

  • 01/10/2025 - 00:00:00 UTC (2025-10-01T00:00:00.000Z)
  • 31/10/2025 - 23:59:59 UTC (2025-10-31T23:59:59.999Z)
Dados do contato

Usando a chamada acima, com o parâmetro include=campaign_contact, os dados do cliente vem no atributo included da resposta:

"included": [ { "type": "campaign_contacts", "id": "663468", "attributes": { "codigo": "1522", "telefone": "+5511993198168" }, "links": { "self": "/api/v1/campaign_contacts/663468" } } ]

2. Paginação de resultados

Exemplo de chamada

GET
/api/v1/campaign_completed_calls
Params
Authorization
KeyValue
var axios = require("axios").default;

var options = {
  method: 'GET',
  url: 'https://exemplo.callix.com.br/api/v1/campaign_completed_calls',
  headers: {
    'Content-Type': 'application/json',
    Authorization: 'Bearer {seu_token
  }'}
};

axios.request(options).then(function (response) {
  console.log(response.data);
}).catch(function (error) {
  console.error(error);
});

Para testar, configure na página de Autenticação.

Exemplo de resposta

{ "meta": { "count": 245 }, "links": { "first": "/api/v1/campaign_completed_calls?filter%5Bstarted_at%5D=2021-01-05T03%3A00%3A00.000Z%2C2021-09-07T02%3A59%3A59.000Z&page%5Blimit%5D=4&page%5Boffset%5D=0", "last": "/api/v1/campaign_completed_calls?filter%5Bstarted_at%5D=2021-01-05T03%3A00%3A00.000Z%2C2021-09-07T02%3A59%3A59.000Z&page%5Blimit%5D=4&page%5Boffset%5D=244", "next": "/api/v1/campaign_completed_calls?filter%5Bstarted_at%5D=2021-01-05T03%3A00%3A00.000Z%2C2021-09-07T02%3A59%3A59.000Z&page%5Blimit%5D=4&page%5Boffset%5D=4" }, "data": [ { "type": "campaign_completed_calls", "id": "6153", "attributes": { "started_at": "2021-01-06T20:57:50.997000Z", "ended_at": "2021-01-06T21:02:14.157000Z", "duration": 263, "destination_phone": "+5537999743290", "protocol": 555, "note": null, "answered_at": "2021-01-06T20:57:58.377000Z", "hangup_cause": 1, "service_duration": 256, "has_audio": true, "audio_link": "/api/v1/campaign_completed_calls/6153/audio" }, "relationships": { "campaign": { "data": { "type": "campaigns", "id": "6" }, "links": { "self": "/api/v1/campaign_completed_calls/6153/relationships/campaign", "related": "/api/v1/campaign_completed_calls/6153/campaign" } }, "campaign_list": { "data": { "type": "campaign_lists", "id": "9" }, "links": { "self": "/api/v1/campaign_completed_calls/6153/relationships/campaign_list", "related": "/api/v1/campaign_completed_calls/6153/campaign_list" } }, "campaign_contact": { "data": { "type": "campaign_contacts", "id": "560643" }, "links": { "self": "/api/v1/campaign_completed_calls/6153/relationships/campaign_contact", "related": "/api/v1/campaign_completed_calls/6153/campaign_contact" } }, "qualification": { "links": { "self": "/api/v1/campaign_completed_calls/6153/relationships/qualification", "related": "/api/v1/campaign_completed_calls/6153/qualification" } }, "agent": { "data": { "type": "users", "id": "5" }, "links": { "self": "/api/v1/campaign_completed_calls/6153/relationships/agent", "related": "/api/v1/campaign_completed_calls/6153/agent" } } }, "links": { "self": "/api/v1/campaign_completed_calls/6153" } }, { "type": "campaign_completed_calls", "id": "6169", "attributes": { "started_at": "2021-01-07T15:06:07.809000Z", "ended_at": "2021-01-07T15:06:35.009000Z", "duration": 27, "destination_phone": "+5511946825709", "protocol": 567, "note": null, "answered_at": "2021-01-07T15:06:25.069000Z", "hangup_cause": 2, "service_duration": 10, "has_audio": true, "audio_link": "/api/v1/campaign_completed_calls/6169/audio" }, "relationships": { "campaign": { "data": { "type": "campaigns", "id": "6" }, "links": { "self": "/api/v1/campaign_completed_calls/6169/relationships/campaign", "related": "/api/v1/campaign_completed_calls/6169/campaign" } }, "campaign_list": { "data": { "type": "campaign_lists", "id": "9" }, "links": { "self": "/api/v1/campaign_completed_calls/6169/relationships/campaign_list", "related": "/api/v1/campaign_completed_calls/6169/campaign_list" } }, "campaign_contact": { "data": { "type": "campaign_contacts", "id": "560641" }, "links": { "self": "/api/v1/campaign_completed_calls/6169/relationships/campaign_contact", "related": "/api/v1/campaign_completed_calls/6169/campaign_contact" } }, "qualification": { "data": { "type": "qualifications", "id": "5" }, "links": { "self": "/api/v1/campaign_completed_calls/6169/relationships/qualification", "related": "/api/v1/campaign_completed_calls/6169/qualification" } }, "agent": { "data": { "type": "users", "id": "5" }, "links": { "self": "/api/v1/campaign_completed_calls/6169/relationships/agent", "related": "/api/v1/campaign_completed_calls/6169/agent" } } }, "links": { "self": "/api/v1/campaign_completed_calls/6169" } }, { "type": "campaign_completed_calls", "id": "6180", "attributes": { "started_at": "2021-01-07T19:13:46.469000Z", "ended_at": "2021-01-07T19:14:00.429000Z", "duration": 13, "destination_phone": "+5514999011006", "protocol": 574, "note": null, "answered_at": "2021-01-07T19:13:52.789000Z", "hangup_cause": 1, "service_duration": 8, "has_audio": true, "audio_link": "/api/v1/campaign_completed_calls/6180/audio" }, "relationships": { "campaign": { "data": { "type": "campaigns", "id": "6" }, "links": { "self": "/api/v1/campaign_completed_calls/6180/relationships/campaign", "related": "/api/v1/campaign_completed_calls/6180/campaign" } }, "campaign_list": { "data": { "type": "campaign_lists", "id": "9" }, "links": { "self": "/api/v1/campaign_completed_calls/6180/relationships/campaign_list", "related": "/api/v1/campaign_completed_calls/6180/campaign_list" } }, "campaign_contact": { "data": { "type": "campaign_contacts", "id": "560644" }, "links": { "self": "/api/v1/campaign_completed_calls/6180/relationships/campaign_contact", "related": "/api/v1/campaign_completed_calls/6180/campaign_contact" } }, "qualification": { "data": { "type": "qualifications", "id": "5" }, "links": { "self": "/api/v1/campaign_completed_calls/6180/relationships/qualification", "related": "/api/v1/campaign_completed_calls/6180/qualification" } }, "agent": { "data": { "type": "users", "id": "5" }, "links": { "self": "/api/v1/campaign_completed_calls/6180/relationships/agent", "related": "/api/v1/campaign_completed_calls/6180/agent" } } }, "links": { "self": "/api/v1/campaign_completed_calls/6180" } }, { "type": "campaign_completed_calls", "id": "6181", "attributes": { "started_at": "2021-01-07T19:24:19.012000Z", "ended_at": "2021-01-07T19:25:46.452000Z", "duration": 87, "destination_phone": "+5582991781462", "protocol": 575, "note": null, "answered_at": "2021-01-07T19:24:29.152000Z", "hangup_cause": 1, "service_duration": 78, "has_audio": true, "audio_link": "/api/v1/campaign_completed_calls/6181/audio" }, "relationships": { "campaign": { "data": { "type": "campaigns", "id": "6" }, "links": { "self": "/api/v1/campaign_completed_calls/6181/relationships/campaign", "related": "/api/v1/campaign_completed_calls/6181/campaign" } }, "campaign_list": { "data": { "type": "campaign_lists", "id": "9" }, "links": { "self": "/api/v1/campaign_completed_calls/6181/relationships/campaign_list", "related": "/api/v1/campaign_completed_calls/6181/campaign_list" } }, "campaign_contact": { "data": { "type": "campaign_contacts", "id": "560645" }, "links": { "self": "/api/v1/campaign_completed_calls/6181/relationships/campaign_contact", "related": "/api/v1/campaign_completed_calls/6181/campaign_contact" } }, "qualification": { "data": { "type": "qualifications", "id": "6" }, "links": { "self": "/api/v1/campaign_completed_calls/6181/relationships/qualification", "related": "/api/v1/campaign_completed_calls/6181/qualification" } }, "agent": { "data": { "type": "users", "id": "5" }, "links": { "self": "/api/v1/campaign_completed_calls/6181/relationships/agent", "related": "/api/v1/campaign_completed_calls/6181/agent" } } }, "links": { "self": "/api/v1/campaign_completed_calls/6181" } } ] }
Parâmetros

Para paginar a requisição deve-se adicionar dois parâmetros, são eles:

  • page[limit] que é um valor inteiro maior que 0 e representa a quantidade de registros a ser recuperada em cada requisição. Na ausência desse parâmetro, seu valor padrão é 100.
  • page[offset] que é um valor inteiro maior ou igual a 0 e representa o deslocamento de registros. Na ausência desse parâmetro seu valor padrão é 0.

A resposta de requisição trás a contagem do total de registros de acordo com os filtros aplicados (meta.count). Com esse valor, é possível calcular a paginação.

"meta": { "count": 245 },
Exemplos
  • Primeira página com paginação de tamanho 10.

    • page[limit]=10
    • page[offset]=0
  • Terceira página com paginação de tamanho 100.

    • page[limit]=100
    • page[offset]=200

3. Ler Áudio da Chamada

Retorna o áudio da chamada em formato base64.

Exemplo de chamada

GET
/api/v1/campaign_completed_calls/1/audio
Params
Authorization
KeyValue
var axios = require("axios").default;

var options = {
  method: 'GET',
  url: 'https://exemplo.callix.com.br/api/v1/campaign_completed_calls/1/audio',
  headers: {
    'Content-Type': 'application/json',
    Authorization: 'Bearer {seu_token
  }'}
};

axios.request(options).then(function (response) {
  console.log(response.data);
}).catch(function (error) {
  console.error(error);
});

Para testar, configure na página de Autenticação.

Parâmetros

Para obter o áudio de uma chamada, deve-se adicionar apenas o ID da chamada. O link obtido através do atributo audio_link na resposta das chamadas completadas já contém o ID da chamada, então basta utilizá-lo.

A resposta dessa requisição trás o audio da chamada em formato base64. Com esse formato, é possível criar um arquivo de áudio de extensão .mp3.

4. Programa de Exemplo

O script Python abaixo contém uma implementação de exemplo usando Python para consumir as APIs de obter ligações completadas e não completadas de campanha, bem como a API de download dos audios. Ele foi construído ao redor de dois casos de uso comuns:

a) Baixar as ligações do dia anterior: agenda-se a execução do script para a madrugada (por exemplo 03:00 AM) do dia corrente, baixando as ligações do dia anterior para fazer algum relatório

b) Baixar as ligações de uma faixa de dias: deseja-se baixar um histórico de todas as ligações, por exemplo mensalmente, para fins de armazenamento local. Então por exemplo no dia primeiro de cada mês agenda-se a execução do script para baixar as ligações do mês anterior.

Para facilitar a operação, o script aceita alguns parâmetros, explicados abaixo:

uso:

python export_calls_v2.py [-h] [-y] [-d] [-m] -t TENANT -b BEARER [start_date] [end_date]

Busca e escreve dados de chamadas de campanha usando a API Callix.

O script cria um subdiretório abaixo do diretório corrente chamado \call_audios, onde cria dois arquivos CSV, um para chamadas completadas (completed_calls.csv) e outro para chamadas não completadas (missed_calls.csv), e caso o parâmetro -d seja especificado, baixa o audio em formato mp3 de cada chamada completada.

parâmetros posicionais:

  • start_date : Data de início no formato dd/mm/aaaa (requerido se o parâmetro -y não for usado)
  • end_date : Data de fim no formato dd/mm/aaaa (requerido se o parâmetro -y não for usado)

outros parâmetros:

-h, —help : mostra ajuda para os comandos

-y, —yesterday : Use a data de ontem como data de início e fim, para facilitar o uso de baixar ligações do dia anterior

-d, —download : Parâmetro que indica se é desejado fazer o download dos audios das ligações completadas (default: False)

-m, —missed : Parâmetro que indica se é desejado fazer o download das ligações não completadas (default: False)

-t TENANT, —tenant TENANT : Qual o prefixo do tenant (Cliente) para a URL de base da API (exemplo: ‘globo’ para https://globo.callix.com.br )

-b BEARER, —bearer BEARER : Bearer token da Autorização (crie o Token em Configurações>APIs>Acesso à API e conecte-o a um Perfil de Acesso adequado) (por exemplo, ‘14ae293d-f1f0-4f0c-a220-c4bc9438a855’)

O programa é fornecido “as-is”, como um exemplo de implementação de consumo de API da Callix.

Campos de campanha precisam ser adaptados no script

A Callix não se propõe a dar suporte neste script e fornece-o simplesmente para dar um “ponta pé inicial” em esforços de integração com a Callix.

Instruções para executar o script

Para executar o script você precisa: Ter a última versão do Python instalado. Você pode baixar a última versão navegando para https://www.python.org/downloads/ 

Uma vez instalado o Python, instale os módulos requests e pytz usando o pip. Na linha de comando:

pip install requests pip install pytz

Para rodar o script copie-o para algum diretório e chame-o da linha de comando, por exemplo:

python export_calls_v2.py -y -t globo -b 14ae293d-f1f0-4f0c-a220-c4bc9438a855 -d

(faz o download das ligações completadas, além dos seus audios. Ligações não completadas não são baixadas (falta -m))

Script Python de exemplo

# modules that need install : run [pip install <module>] on the command line import requests import pytz # modules included in Python default libraries import json import csv from datetime import datetime, timedelta import time import base64 import os import sys import argparse def convert_to_brt(utc_string): utc_zone = pytz.utc brt_zone = pytz.timezone('America/Sao_Paulo') utc_dt = utc_zone.localize(datetime.fromisoformat(utc_string.replace('Z', ''))) brt_dt = utc_dt.astimezone(brt_zone) return brt_dt.strftime('%d-%m-%Y %H:%M:%S') def process_included_entities(calls, included): # Create a lookup dictionary for included entities entity_map = {(item['type'], item['id']): item['attributes'] for item in included} # Process each call and add related entity attributes for call in calls: attrs = call['attributes'] relationships = call['relationships'] # Convert UTC times to BRT attrs['started_at'] = convert_to_brt(attrs['started_at']) attrs['ended_at'] = convert_to_brt(attrs['ended_at']) if 'answered_at' in attrs: attrs['answered_at'] = convert_to_brt(attrs['answered_at']) # Add campaign_contact attributes # contact attributes are DYNAMIC, so lines mapping contact attributes to CSV fields should be changed/added according to what contact fields one wants on the CSV # As an EXAMPLE five contact attributes are included: "nome", "cpf", "produto" , "publico", "telefone", and they are mapped to the "contact_nome", "contact_cpf" and so on as CSV fields contact_data = relationships.get('campaign_contact', {}).get('data') if contact_data and (contact_data['type'], contact_data['id']) in entity_map: contact_attrs = entity_map[(contact_data['type'], contact_data['id'])] attrs['contact_nome'] = contact_attrs.get('nome', '') attrs['contact_cpf'] = contact_attrs.get('cpf', '') attrs['contact_produto'] = contact_attrs.get('produto', '') attrs['contact_publico'] = contact_attrs.get('publico', '') attrs['contact_telefone'] = contact_attrs.get('telefone', '') # Add qualification attributes if they exist qual_data = relationships.get('qualification', {}).get('data') if qual_data and (qual_data['type'], qual_data['id']) in entity_map: qual_attrs = entity_map[(qual_data['type'], qual_data['id'])] attrs['qualification_name'] = qual_attrs.get('name', '') attrs['qualification_cpc'] = qual_attrs.get('cpc', '') attrs['qualification_success'] = qual_attrs.get('success', '') attrs['qualification_is_system_qualification'] = qual_attrs.get('is_system_qualification', '') # Add user (agent) attributes if they exist agent_data = relationships.get('agent', {}).get('data') if agent_data and (agent_data['type'], agent_data['id']) in entity_map: agent_attrs = entity_map[(agent_data['type'], agent_data['id'])] attrs['agent_name'] = agent_attrs.get('name', '') attrs['agent_email'] = agent_attrs.get('email', '') attrs['agent_login'] = agent_attrs.get('login', '') return calls def download_call_audio(audio_link, protocol, headers, base_url, output_dir="call_audios"): if not os.path.exists(output_dir): os.makedirs(output_dir) filename = f"{output_dir}/{protocol}.mp3" # Check if audio file already exists locally if os.path.exists(filename) and os.path.getsize(filename) > 0: print(f"Audio for protocol {protocol} already exists and is non-empty, skipping download") return True full_audio_url = base_url + audio_link try: response = requests.get(full_audio_url, headers=headers) #sleeps for 500ms to respect API rate limit time.sleep(0.5) if response.status_code == 200: content_type = response.headers.get('Content-Type', '').lower() if 'application/json' in content_type: audio_data = response.json().get('audio', '') if audio_data: audio_bytes = base64.b64decode(audio_data) with open(filename, 'wb') as audio_file: audio_file.write(audio_bytes) print(f"Downloaded audio for protocol {protocol} (base64 JSON)") return True else: print(f"No audio data in JSON response for protocol {protocol}") return False else: with open(filename, 'wb') as audio_file: audio_file.write(response.content) print(f"Downloaded audio for protocol {protocol} (direct binary)") return True else: print(f"Failed to download audio for protocol {protocol}: Status {response.status_code}: {response.text}") return False except Exception as e: print(f"Error downloading audio for protocol {protocol}: {e}") return False def fetch_and_write_calls(base_url, export_type, headers, start_date, end_date, download_audio=False, output_dir="call_audios"): if export_type not in ["completed", "missed"]: raise ValueError("export_type must be 'completed' or 'missed'") # Infer endpoint, include, and filename based on export_type if export_type == "completed": endpoint = "/api/v1/campaign_completed_calls" include = "campaign_contact,qualification,agent" filename = "completed_calls.csv" rate_limit_delay = 7 # 10/minute = 6s + 1s buffer else: # export_type == "missed" endpoint = "/api/v1/campaign_missed_calls" include = "campaign_contact" filename = "missed_calls.csv" rate_limit_delay = 61 # 1/minute = 60s + 1s buffer total_calls = 0 is_first_file = True csv_path = os.path.join(output_dir, filename) if not os.path.exists(output_dir): os.makedirs(output_dir) try: current_date = start_date while current_date <= end_date: day_start = current_date.replace(hour=0, minute=0, second=0, microsecond=0) day_end = current_date.replace(hour=23, minute=59, second=59, microsecond=999999) start_str = day_start.strftime('%Y-%m-%dT%H:%M:%S.000Z') end_str = day_end.strftime('%Y-%m-%dT%H:%M:%S.000Z') params = { "filter[started_at]": f"{start_str},{end_str}", "page[limit]": 2000, "page[offset]": 0, "include": include } print(f"Processing {export_type} calls for {current_date.strftime('%Y-%m-%d')}...") is_first_page = True current_url = endpoint while current_url: response = requests.get( base_url + endpoint if current_url == endpoint else current_url, headers=headers, params=params if current_url == endpoint else None ) if response.status_code != 200: print(f"Error on {current_date.strftime('%Y-%m-%d')}: Received status code {response.status_code}: {response.text}") break data = response.json() calls = data['data'] if not calls: print(f"No {export_type} calls found for {current_date.strftime('%Y-%m-%d')}") break included = data.get('included', []) if included: calls = process_included_entities(calls, included) if download_audio and export_type == "completed": for call in calls: attrs = call['attributes'] if attrs.get('has_audio', False) and 'audio_link' in attrs: download_call_audio(attrs['audio_link'], attrs['protocol'], headers, base_url, output_dir) original_fieldnames = list(calls[0]['attributes'].keys()) relationship_fieldnames = ( ['contact_nome', 'contact_cpf', 'contact_produto', 'contact_publico', 'contact_telefone', 'qualification_name','qualification_cpc', 'qualification_success', 'qualification_is_system_qualification', 'agent_name', 'agent_login', 'agent_email'] if export_type == "completed" else ['contact_nome', 'contact_cpf', 'contact_produto', 'contact_publico', 'contact_telefone'] ) all_fieldnames = original_fieldnames + [f for f in relationship_fieldnames if f not in original_fieldnames] try: mode = 'w' if is_first_file and is_first_page else 'a' with open(csv_path, mode, newline='', encoding='utf-8') as csvfile: writer = csv.DictWriter(csvfile, fieldnames=all_fieldnames, delimiter=';') if is_first_page and is_first_file: writer.writeheader() is_first_file = False for call in calls: writer.writerow(call['attributes']) except PermissionError as pe: print(f"Permission denied when writing to {csv_path}: {pe}. Please ensure the file is not open elsewhere.") return except Exception as e: print(f"Error writing to {csv_path}: {e}") return total_calls += len(calls) print(f"Fetched and wrote {len(calls)} {export_type} calls for {current_date.strftime('%Y-%m-%d')}, total so far: {total_calls}") time.sleep(rate_limit_delay) next_page = data['links'].get('next') current_url = base_url + next_page if next_page else None current_date += timedelta(days=1) except requests.RequestException as e: print(f"Error: {e}") print(f"Finished processing {export_type} calls. Total calls saved: {total_calls}") def main(): parser = argparse.ArgumentParser(description="Fetch and write call data from Callix API.") parser.add_argument('-y', '--yesterday', action='store_true', help="Use yesterday's date for start and end date") parser.add_argument('-d', '--download', action='store_true', help="Download audio files for completed calls (default: False)") parser.add_argument('-m', '--missed', action='store_true', help="Also export missed calls (default: only completed calls)") parser.add_argument('-t', '--tenant', required=True, help="Tenant prefix for base URL (e.g., 'globo' for https://globo.callix.com.br)") parser.add_argument('-b', '--bearer', required=True, help="Bearer token for Authorization (e.g., '14ae293d-f1f0-4f0c-a220-c4bc9438a855')") parser.add_argument('start_date', nargs='?', help="Start date in dd/mm/aaaa format (required if -y not used)") parser.add_argument('end_date', nargs='?', help="End date in dd/mm/aaaa format (required if -y not used)") args = parser.parse_args() base_url = f"https://{args.tenant}.callix.com.br" headers = { 'Content-Type': "application/json", 'Authorization': f"Bearer {args.bearer}" } if args.yesterday: yesterday = datetime.now(pytz.utc) - timedelta(days=1) start_date = yesterday.replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=pytz.utc) end_date = yesterday.replace(hour=23, minute=59, second=59, microsecond=999999, tzinfo=pytz.utc) else: if not args.start_date or not args.end_date: print("Error: Start date and end date are required unless -y is used.") print("Usage: python export_calls_v2.py -t <tenant> -b <bearer> [-y] [-d] [-m] [<start_date> <end_date>]") print("Example: python export_calls_v2.py -t globo -b 14ae293d-f1f0-4f0c-a220-c4bc9438a855 31/03/2025 03/04/2025") print(" python export_calls_v2.py -t globo -b 14ae293d-f1f0-4f0c-a220-c4bc9438a855 -y -d") print("Dates must be in dd/mm/aaaa format.") sys.exit(1) try: start_date = datetime.strptime(args.start_date, '%d/%m/%Y').replace(tzinfo=pytz.utc) end_date = datetime.strptime(args.end_date, '%d/%m/%Y').replace(tzinfo=pytz.utc) except ValueError as e: print(f"Error: Invalid date format. {e}") print("Usage: python export_calls_v2 -t <tenant> -b <bearer> [-y] [-d] [-m] [<start_date> <end_date>]") print("Example: python export_calls_v2.py -t globo -b 14ae293d-f1f0-4f0c-a220-c4bc9438a855 31/03/2025 03/04/2025") print(" python export_calls_v2.py -t globo -b 14ae293d-f1f0-4f0c-a220-c4bc9438a855 -y -d") print("Dates must be in dd/mm/aaaa format.") sys.exit(1) print("Starting to fetch and write completed calls...") fetch_and_write_calls(base_url, "completed", headers, start_date, end_date, download_audio=args.download) if args.missed: print("Starting to fetch and write missed calls...") fetch_and_write_calls(base_url, "missed", headers, start_date, end_date, download_audio=False) if __name__ == "__main__": main()
Last updated on