Brasão da República Federativa do Brasil
SENADO FEDERAL Gabinete do Senador Astronauta Marcos Pontes

API de Contatos

Consulta e webhooks para integração com outros sistemas — v1

Como começar

A API é somente leitura. Ela permite consultar os contatos digitalizados pelo gabinete e receber avisos automáticos quando algo muda. Não há nenhum endpoint de escrita: nenhum sistema externo cria, altera ou apaga dados.

1. Peça um token

Os tokens são gerados pela administração do gabinete, na aba API do sistema. Cada sistema integrado deve ter o seu, para que o acesso possa ser revogado individualmente. O token aparece uma única vez, no momento da criação — guarde-o em cofre de segredos, nunca no código-fonte.

2. Endereço base

https://contatos.marcospontes.com.br/v1

3. Autentique cada chamada

Envie o token no cabeçalho Authorization, no formato Bearer:

# primeira chamada: confirma que o token funciona
curl -H "Authorization: Bearer cgab_seu_token_aqui" \
     https://contatos.marcospontes.com.br/v1/estatisticas

Os dados são pessoais de terceiros. Nomes, telefones, e-mails e endereços coletados em eventos públicos estão sujeitos à LGPD. Ao integrar, você assume as obrigações de quem trata esses dados: finalidade declarada, acesso restrito, transmissão em HTTPS e prazo de guarda definido. Não replique essa base para sistemas sem controle de acesso.

Consulta

GET/v1/contatos lista contatos, com filtros e paginação
GET/v1/contatos/{id} um contato específico
GET/v1/estatisticas totais por setor, prioridade, segmento e situação

Parâmetros de /v1/contatos

ParâmetroTipoDescrição
qtextoBusca livre em nome, cargo, empresa, sigla, e-mail, telefone, cidade, temas e evento. Ignora acentos: q=jose encontra "José".
setorenumpublico, privado, terceiro_setor, academico, midia, outro
segmentoenumÁrea de atuação. Lista completa em Modelo de dados.
prioridadeenumalta, media, baixa
statusenumnovo, revisado, encaminhado, arquivado
eventotextoFiltra pelo evento de origem (busca parcial).
desdedata ISOSó contatos criados a partir desta data. Ex.: 2026-08-01
limiteinteiroItens por página. Padrão 50, máximo 200.
offsetinteiroQuantos pular. Use com total para paginar.

Exemplo

# contatos de prioridade alta do agronegócio, criados em agosto
curl -H "Authorization: Bearer $TOKEN" \
  "https://contatos.marcospontes.com.br/v1/contatos?prioridade=alta&segmento=agronegocio&desde=2026-08-01&limite=20"
{
  "total": 37,
  "limite": 20,
  "offset": 0,
  "contatos": [ /* ver Modelo de dados */ ]
}

total é a contagem de todos os contatos que casam com o filtro, não o tamanho desta página. Para percorrer tudo, incremente offset de limite em limite enquanto offset < total.

Modelo de dados

Campos sem valor vêm como null, nunca como string vazia. Listas vêm sempre como array, possivelmente vazio.

{
  "id": "62a64bbe-8f82-473a-b14f-69a3e4548de8",
  "nome": "Ricardo Salgueiro Neto",
  "cargo": "Diretor de Relações Institucionais",
  "empresa": "Conselho Nacional de Desenvolvimento Científico e Tecnológico",
  "sigla": "CNPq",
  "departamento": null,
  "contato": {
    "telefone": "+55 (61) 3315-0000",
    "celular": "+55 (61) 99145-2277",
    "email": "ricardo.neto@exemplo.br",
    "email_secundario": null,
    "site": "exemplo.br"
  },
  "endereco": {
    "logradouro": "SBN Quadra 2, Bloco A",
    "cidade": "Brasília", "uf": "DF", "cep": "70040-020"
  },
  "redes": {
    "linkedin": "linkedin.com/in/exemplo",
    "instagram": null, "facebook": null, "twitter": null,
    "outras": ["Telegram: @exemplo"]
  },
  "classificacao": {
    "setor": "publico",
    "segmento": "ciencia_pesquisa",
    "prioridade": "alta",
    "temas": ["fomento à pesquisa", "inovação"],
    "resumo": "Diretor do CNPq, interlocutor para pautas de C&T.",
    "confianca_leitura": 0.97
  },
  "origem": {
    "evento": "Audiência pública sobre inovação",
    "local": "Senado Federal, Brasília",
    "data_captura": "2026-08-26",
    "registrado_por": "Maria Souza"
  },
  "status": "revisado",
  "observacoes": null,
  "tem_foto": true,
  "tem_verso": false,
  "criado_em": "2026-08-26T19:15:21.929Z",
  "atualizado_em": "2026-08-26T19:15:21.929Z"
}

Sobre empresa e sigla

Instituições conhecidas por sigla são gravadas nos dois campos: empresa traz o nome por extenso e sigla traz a sigla. Se o cartão trouxer apenas a sigla e não for possível expandi-la com segurança, os dois campos trazem a sigla. A busca por q cobre os dois — procurar por "CNPq" encontra o registro mesmo que o cartão só tivesse o nome por extenso.

Sobre confianca_leitura

Número de 0 a 1 informado pela IA que leu o cartão, ou null. Todo contato disponível na API já passou por conferência humana, mas esse valor ajuda a priorizar auditorias. Abaixo de 0,7 vale reconferir.

Valores de segmento

agronegocio, saude, educacao, infraestrutura, transporte, energia, meio_ambiente, seguranca_publica, tecnologia, telecomunicacoes, industria, comercio, servicos, financeiro, juridico, construcao, turismo, cultura, esporte, assistencia_social, ciencia_pesquisa, comunicacao_imprensa, politica_governo, religioso, outro.

As fotos dos cartões não são expostas pela API. tem_foto e tem_verso apenas informam que existem, para o caso de alguém precisar solicitá-las ao gabinete.

Webhooks

Em vez de ficar consultando a API em busca de novidades, cadastre uma URL e nós avisamos. A administração cadastra o webhook na aba API do sistema, informando o nome, a URL e quais eventos interessam.

Eventos

EventoQuando dispara
contato.criadoUm cartão foi conferido e virou contato definitivo.
contato.atualizadoAlgum campo de um contato existente foi alterado.
contato.excluidoUm contato foi apagado. O corpo traz só id e nome.

O que chega na sua URL

Um POST com content-type: application/json e estes cabeçalhos:

CabeçalhoConteúdo
x-eventoNome do evento, ex.: contato.criado
x-entrega-idUUID único desta entrega. Use para descartar duplicatas.
x-assinaturasha256=<hex> — HMAC-SHA256 do corpo. Sempre valide.
{
  "evento": "contato.criado",
  "enviado_em": "2026-08-26T19:15:22.100Z",
  "dados": { /* mesmo formato de /v1/contatos/{id} */ }
}

Regras de entrega

Validar a assinatura

Calcule o HMAC-SHA256 do corpo bruto da requisição usando o segredo do webhook e compare com o valor de x-assinatura. Três cuidados:

Node.js (Express)

const crypto = require('crypto');

// o corpo precisa chegar cru: express.raw, não express.json
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const esperado = 'sha256=' + crypto
    .createHmac('sha256', process.env.WEBHOOK_SEGREDO)
    .update(req.body)
    .digest('hex');

  const recebido = req.get('x-assinatura') || '';
  const a = Buffer.from(esperado), b = Buffer.from(recebido);

  if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) {
    return res.status(401).send('assinatura inválida');
  }

  const evento = JSON.parse(req.body.toString('utf8'));
  res.sendStatus(200);              // responda primeiro
  processarEmSegundoPlano(evento);   // trabalhe depois
});

Python (Flask)

import hmac, hashlib, os
from flask import request, abort

def valido(corpo: bytes, assinatura: str) -> bool:
    esperado = 'sha256=' + hmac.new(
        os.environ['WEBHOOK_SEGREDO'].encode(), corpo, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(esperado, assinatura)

@app.post('/webhook')
def webhook():
    if not valido(request.get_data(), request.headers.get('x-assinatura', '')):
        abort(401)
    evento = request.get_json()
    # enfileire aqui e devolva na hora
    return '', 200

PHP

$corpo = file_get_contents('php://input');
$esperado = 'sha256=' . hash_hmac('sha256', $corpo, getenv('WEBHOOK_SEGREDO'));
$recebido = $_SERVER['HTTP_X_ASSINATURA'] ?? '';

if (!hash_equals($esperado, $recebido)) {
    http_response_code(401);
    exit;
}
$evento = json_decode($corpo, true);
http_response_code(200);

A aba API do sistema tem um botão Enviar teste que dispara um contato.criado fictício (com "teste": true no corpo) para a sua URL. Use para validar a integração antes de entrar em produção — e ignore esse registro no seu banco.

Erros e limites

Erros vêm sempre com este formato:

{ "erro": { "codigo": "nao_autenticado", "mensagem": "Token ausente ou inválido..." } }
HTTPCódigoO que fazer
401nao_autenticadoToken ausente, inválido ou revogado. Confira o cabeçalho Authorization: Bearer ....
404nao_encontradoO contato não existe ou foi excluído.
404rota_invalidaCaminho inexistente. Confira a grafia.
405metodo_nao_permitidoA API é somente leitura: use GET.
500erro_internoFalha nossa. Tente de novo com espera progressiva e avise o gabinete se persistir.

Boas práticas