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
/v1/contatos
lista contatos, com filtros e paginação
/v1/contatos/{id}
um contato específico
/v1/estatisticas
totais por setor, prioridade, segmento e situação
Parâmetros de /v1/contatos
| Parâmetro | Tipo | Descrição |
|---|---|---|
q | texto | Busca livre em nome, cargo, empresa, sigla, e-mail, telefone, cidade, temas e evento. Ignora acentos: q=jose encontra "José". |
setor | enum | publico, privado, terceiro_setor, academico, midia, outro |
segmento | enum | Área de atuação. Lista completa em Modelo de dados. |
prioridade | enum | alta, media, baixa |
status | enum | novo, revisado, encaminhado, arquivado |
evento | texto | Filtra pelo evento de origem (busca parcial). |
desde | data ISO | Só contatos criados a partir desta data. Ex.: 2026-08-01 |
limite | inteiro | Itens por página. Padrão 50, máximo 200. |
offset | inteiro | Quantos 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
| Evento | Quando dispara |
|---|---|
contato.criado | Um cartão foi conferido e virou contato definitivo. |
contato.atualizado | Algum campo de um contato existente foi alterado. |
contato.excluido | Um 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çalho | Conteúdo |
|---|---|
x-evento | Nome do evento, ex.: contato.criado |
x-entrega-id | UUID único desta entrega. Use para descartar duplicatas. |
x-assinatura | sha256=<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
- Responda 2xx em até 10 segundos. Fora disso, contamos como falha.
- Responda rápido e processe depois: enfileire internamente em vez de trabalhar antes de responder.
- Após 10 falhas seguidas o webhook é desativado automaticamente e precisa ser reativado pelo gabinete. Uma entrega bem-sucedida zera o contador.
- Não há reenvio automático de uma entrega isolada que falhou. Se perder um evento,
reconcilie pela API usando o parâmetro
desde. - A URL precisa ser
https://. Dado pessoal não trafega em HTTP. - A ordem de chegada não é garantida. Use
atualizado_empara decidir qual versão é a mais recente.
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:
- Use o corpo exatamente como chegou, antes de qualquer parse de JSON. Serializar de novo muda os bytes e a assinatura não bate.
- Compare em tempo constante, nunca com
==. - Rejeite a requisição se a assinatura não bater. Não processe "só para conferir".
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..." } }
| HTTP | Código | O que fazer |
|---|---|---|
| 401 | nao_autenticado | Token ausente, inválido ou revogado. Confira o cabeçalho Authorization: Bearer .... |
| 404 | nao_encontrado | O contato não existe ou foi excluído. |
| 404 | rota_invalida | Caminho inexistente. Confira a grafia. |
| 405 | metodo_nao_permitido | A API é somente leitura: use GET. |
| 500 | erro_interno | Falha nossa. Tente de novo com espera progressiva e avise o gabinete se persistir. |
Boas práticas
- Não há limite de chamadas publicado, mas evite varrer a base inteira em ciclo curto.
Prefira webhook para saber de mudanças e use a consulta com
desdeapenas para reconciliar. - As respostas trazem
cache-control: no-store. Não guarde em cache compartilhado — são dados pessoais. - Guarde o
iddo contato como chave estrangeira. Ele é estável e não muda. - Tokens são revogáveis a qualquer momento pelo gabinete. Trate 401 como situação esperada, com alerta para o responsável, não como erro fatal silencioso.