Relatório de campanhas e fluxos de template, com custo por categoria da Meta (marketing, utility, authentication, authentication-international, service). Filtros: ?start_date=&end_date=&connection_id=&campaign_id=&template_name=&source=&campaign_status=&category=¤cy=. O período também aceita `preset=today|last7|last30` ou `preset=custom&from=&to=` como alternativa a start_date/end_date; qualquer um dos dois formatos resolve para o mesmo `period` na resposta. `currency` aceita brl (padrão) ou usd e define a moeda de todos os custos. Use &include=filters para receber também as opções válidas de cada filtro — qualquer outro valor de `include` é 422, nunca ignorado em silêncio. `totals.campaigns` conta só campanhas com envio no período; os fluxos de template com envio ficam em `totals.template_sources`. O período segue o mesmo teto das demais abas de relatório: no máximo 1830 dias; com `g=hour|day|week` vale o máximo da visão pedida (2, 92 e 366 dias). `daily` traz um item por dia do período até hoje (no fuso da empresa), com zeros nos dias sem envio, e fica vazio quando não houve nenhum envio.
- Custo só nasce quando a Meta confirma ENTREGA (`delivered` ou `read`) — uma mensagem `sent` sem confirmação fica `pending`, nunca zero.
- `amount` só tem valor com `pending: 0` no mesmo bloco; havendo pendência, some `null` e `known_amount` traz o parcial já resolvido. Nunca some `known_amount` como se fosse o total.
- Conexões `uazapi` não são Meta Cloud API: o custo dessas linhas é `not_applicable`, e a categoria dessas linhas não é cobrada.
- `currency` escolhe a moeda de TODOS os blocos `costs` da resposta: `brl` (padrão) ou `usd`. A resposta ecoa a escolha em `currency` (`BRL` ou `USD`) e em `applied_filters.currency`. Para ver a outra moeda, faça uma nova chamada com `currency` diferente — contagens (`sent`, `resolved`, `pending`…) são as mesmas nas duas.
- O custo canônico é o US$ da tarifa da Meta. O R$ é ESTIMADO: tarifa em US$ × PTAX de venda do Banco Central do dia local da mensagem (`basis` inclui `usd_rate_x_ptax_venda`). Enquanto a PTAX do dia não é publicada, a linha fica `pending` em R$.
- `basis` lista, sem repetição e em ordem alfabética, as bases de preço dos envios do bloco (`rate_card_base_tier` para tarifa oficial, `usd_rate_x_ptax_venda` para R$ estimado pela PTAX, `historical_reference` para preço histórico informado pelo cliente); vem `[]` quando o bloco não tem custo. As linhas históricas do DFImóveis usam a referência validada pelo cliente (`historical_reference`) também em R$, não a PTAX.
- `by_category` separa cada categoria por `pricing_window` e `template_category`. `category: service` com `pricing_window: customer_service_24h` é template enviado grátis dentro da janela de atendimento de 24 h, e `template_category` guarda a categoria original do template. `pricing_window: free_entry_point_72h` é a janela gratuita de 72 h aberta por anúncio (ponto de entrada) — NÃO é a janela de 24 h e mantém a categoria do template. `pricing_window: null` é envio sem janela gratuita. O filtro `category=service` inclui os templates da janela de 24 h.
- Respostas livres de atendente e da IA não entram neste relatório: ele conta só envios de campanha e de template.
- `by_source` é limitado a 500 origens; a partir daí a resposta vem com `truncated: true` e o excedente não aparece — refine o período ou os filtros.
- Taxas (`delivery_rate`, `read_rate`, `reply_rate`) vêm em percentual de 0 a 100, com 1 casa decimal — não são fração de 0 a 1.
- `by_source[].source_key` identifica a origem: `campaign:<campaign_id>` para campanha e `template:<connection_id>:<template_name>` para fluxo de template (`none` no lugar do connection_id quando a mensagem não tem conexão).
- `campaigns` é alias de `by_source` (mesmo array) — mantido para compatibilidade com integrações existentes; prefira `by_source` em código novo.
- `available_filters` só aparece com `&include=filters` — traz as conexões, campanhas e templates com envio no período, mais o catálogo fixo de categorias e o `period` (start_date/end_date) a que as opções se referem, para popular seletores sem uma segunda chamada. Se essa segunda leitura falhar, a resposta ainda vem 200 sem `available_filters`, com `degraded: ['available_filters']` no corpo.
- `available_filters.templates[].category` é a categoria do TEMPLATE cadastrado na Meta; já `by_category[].category` pode ser `service` para envios dentro da janela de atendimento de 24h — são duas superfícies diferentes, não o mesmo dado em dois formatos.
- Todo erro 4xx deste endpoint responde `{ success: false, error, code }` — inclusive o 422 de parâmetro inválido.
- Uma única chamada gera um único `data_as_of` para todos os níveis (totals, by_source, by_category, daily) — os números nunca vêm de instantes diferentes.
- `preset` (today, last7, last30 ou custom com from/to) é equivalente a start_date/end_date; qualquer uma das duas formas resolve para o mesmo intervalo e aparece em `period` na resposta, com o `preset` efetivamente usado.
Resposta (200)
{
"timezone": "America/Sao_Paulo",
"data_as_of": "2026-09-16T12:00:00.000Z",
"currency": "BRL",
"period": {
"preset": "custom",
"start": "2026-07-24",
"end": "2026-07-30",
"timezone": "America/Sao_Paulo"
},
"applied_filters": {
"start_date": "2026-07-24",
"end_date": "2026-07-30",
"connection_id": null,
"campaign_id": null,
"template_name": null,
"source": "all",
"campaign_status": null,
"category": null,
"currency": "brl"
},
"totals": {
"campaigns": 0,
"template_sources": 1,
"recipients": 4936,
"sent": 4936,
"delivered": 4555,
"not_delivered": 381,
"read": 256,
"replied": 426,
"failed": 0,
"delivery_rate": 92.3,
"read_rate": 5.2,
"reply_rate": 8.6,
"costs": {
"amount": 1457.6,
"known_amount": 1457.6,
"coverage": 1,
"sent": 4936,
"resolved": 0,
"free": 0,
"not_billed": 381,
"void": 0,
"not_applicable": 0,
"historical": 4555,
"pending": 0,
"status": "complete",
"basis": [
"historical_reference"
]
}
},
"byStatus": [],
"by_source": [
{
"recipients": 4936,
"sent": 4936,
"delivered": 4555,
"not_delivered": 381,
"read": 256,
"replied": 426,
"failed": 0,
"delivery_rate": 92.3,
"read_rate": 5.2,
"reply_rate": 8.6,
"costs": {
"amount": 1457.6,
"known_amount": 1457.6,
"coverage": 1,
"sent": 4936,
"resolved": 0,
"free": 0,
"not_billed": 381,
"void": 0,
"not_applicable": 0,
"historical": 4555,
"pending": 0,
"status": "complete",
"basis": [
"historical_reference"
]
},
"source_type": "template",
"source_key": "template:<connection_id>:boas_vindas",
"campaign_id": null,
"campaign_name": null,
"campaign_status": null,
"template_name": "boas_vindas",
"connection_id": "<uuid>"
}
],
"campaigns": [
{
"recipients": 4936,
"sent": 4936,
"delivered": 4555,
"not_delivered": 381,
"read": 256,
"replied": 426,
"failed": 0,
"delivery_rate": 92.3,
"read_rate": 5.2,
"reply_rate": 8.6,
"costs": {
"amount": 1457.6,
"known_amount": 1457.6,
"coverage": 1,
"sent": 4936,
"resolved": 0,
"free": 0,
"not_billed": 381,
"void": 0,
"not_applicable": 0,
"historical": 4555,
"pending": 0,
"status": "complete",
"basis": [
"historical_reference"
]
},
"source_type": "template",
"source_key": "template:<connection_id>:boas_vindas",
"campaign_id": null,
"campaign_name": null,
"campaign_status": null,
"template_name": "boas_vindas",
"connection_id": "<uuid>"
}
],
"by_category": [
{
"recipients": 12,
"sent": 12,
"delivered": 12,
"not_delivered": 0,
"read": 9,
"replied": 4,
"failed": 0,
"delivery_rate": 100,
"read_rate": 75,
"reply_rate": 33.3,
"costs": {
"amount": 0,
"known_amount": 0,
"coverage": 1,
"sent": 12,
"resolved": 0,
"free": 12,
"not_billed": 0,
"void": 0,
"not_applicable": 0,
"historical": 0,
"pending": 0,
"status": "complete",
"basis": []
},
"category": "service",
"pricing_window": "customer_service_24h",
"template_category": "utility"
},
{
"recipients": 12,
"sent": 12,
"delivered": 12,
"not_delivered": 0,
"read": 9,
"replied": 4,
"failed": 0,
"delivery_rate": 100,
"read_rate": 75,
"reply_rate": 33.3,
"costs": {
"amount": 0,
"known_amount": 0,
"coverage": 1,
"sent": 12,
"resolved": 0,
"free": 12,
"not_billed": 0,
"void": 0,
"not_applicable": 0,
"historical": 0,
"pending": 0,
"status": "complete",
"basis": []
},
"category": "marketing",
"pricing_window": "free_entry_point_72h",
"template_category": "marketing"
}
],
"daily": [
{
"recipients": 4936,
"sent": 4936,
"delivered": 4555,
"not_delivered": 381,
"read": 256,
"replied": 426,
"failed": 0,
"delivery_rate": 92.3,
"read_rate": 5.2,
"reply_rate": 8.6,
"costs": {
"amount": 1457.6,
"known_amount": 1457.6,
"coverage": 1,
"sent": 4936,
"resolved": 0,
"free": 0,
"not_billed": 381,
"void": 0,
"not_applicable": 0,
"historical": 4555,
"pending": 0,
"status": "complete",
"basis": [
"historical_reference"
]
},
"date": "2026-07-24"
}
],
"truncated": false,
"available_filters": {
"connections": [
{
"id": "<uuid>",
"name": "WhatsApp Principal",
"provider": "meta_cloud"
}
],
"campaigns": [
{
"id": "<uuid>",
"name": "Promoção de Julho",
"status": "sent",
"connection_id": "<uuid>"
}
],
"templates": [
{
"template_name": "boas_vindas",
"connection_id": "<uuid>",
"category": "utility"
}
],
"categories": [
"marketing",
"utility",
"authentication",
"authentication-international",
"service",
"unknown"
],
"period": {
"start_date": "2026-07-24",
"end_date": "2026-07-30"
}
}
}Erros
| Status | Código | Quando ocorre |
|---|---|---|
| 422 | INVALID_PARAMETER | start_date/end_date (ou preset/from/to) ausentes, fora do formato YYYY-MM-DD, com data inicial posterior à final ou período acima de 366 dias; connection_id/campaign_id que não é UUID; source fora de all, campaign ou template; campaign_status fora do enum; template_name acima de 512 caracteres; category fora do catálogo; currency fora de brl ou usd; include fora de filters; ou campaign_id/campaign_status combinados com source=template (template_name é aceito com qualquer source) |
| 403 | INSUFFICIENT_SCOPE | A chave não tem o escopo reports:read |
| 403 | PLAN_REQUIRED | A conta não está no plano Enterprise |
Exemplo
curl -s -H "Authorization: Bearer <sua-key>" https://www.prosalab.com/api/v1/reports/campaigns