Skip to main content
← Voltar aos Guias
Avançado📖 12 min

Integração de API e Webhooks: Conectando o Predite à Sua Própria Stack

A maior parte do Predite vive no dashboard, mas os casos mais interessantes acontecem quando você para de clicar e começa a programar. Talvez você queira sinais de EV enviados para um canal do Discord no momento em que o scanner os encontra. Talvez você rode um bot fora da plataforma e precise das suas posições ao vivo na Polymarket em JSON a cada minuto. Talvez você queira que um gatilho de stop-loss te acione, publique no Slack e dispare um fluxo no n8n que fecha a posição automaticamente. É para isso que existem a API e o sistema de webhooks do Predite.

Este guia percorre toda a superfície de integração de ponta a ponta: gerar uma chave de API, chamar os endpoints REST com autenticação Bearer, respeitar os rate limits, assinar eventos de webhook, verificar as assinaturas de entrega com HMAC-SHA256 e, por fim, costurar tudo isso em um pipeline de alertas personalizado. Tudo aqui é exclusivo do plano Bot ($99/mês) — os planos Starter ($29) e Pro ($59) usam o dashboard e as notificações integradas, mas o acesso programático (chaves de API e webhooks de saída) é restrito ao Bot.

Gerando uma Chave de API

O Predite usa chaves de API de longa duração para a API REST. Elas têm escopo limitado à sua conta, então qualquer chave pode ler seu portfólio e o feed de sinais, e nada além disso — não há endpoints de escrita, então uma chave vazada não pode realizar trades nem movimentar fundos.

  1. Abra Configurações → API no dashboard. Se você não estiver no plano Bot, verá um aviso de upgrade no lugar do gerenciador de chaves.
  2. Clique em Criar chave de API e dê a ela um rótulo descritivo, como n8n-prod ou discord-bot. O rótulo serve apenas para sua própria organização quando você tem várias chaves.
  3. O Predite gera a chave e a exibe para você exatamente uma vez. Copie-a imediatamente e guarde-a em um gerenciador de segredos ou no seu ambiente.

Uma chave tem esta aparência:

`` pdt_live_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6 ``

O formato é o prefixo pdt_live_ seguido de 32 caracteres hexadecimais — 40 caracteres no total. No servidor, apenas um hash SHA-256 da chave completa é armazenado, além dos 16 primeiros caracteres (pdt_live_ + 7 caracteres) como prefixo não secreto, para que o dashboard possa mostrar pdt_live_a1b2c3… na lista de chaves sem nunca armazenar o segredo em texto puro. É também por isso que não podemos mostrar a chave completa novamente após a criação: nós literalmente não a temos.

Algumas observações operacionais:

  • Revogar uma chave é uma exclusão lógica (soft delete). O registro é mantido para auditoria, o status muda para revoked e qualquer requisição que a utilize passa imediatamente a retornar 401. Faça a rotação criando uma nova chave, implantando-a e, então, revogando a antiga.
  • O dashboard rastreia last_used_at e um contador de requisições por chave, para que você possa identificar uma chave que ficou silenciosa (sinal de que sua integração quebrou) ou que está mais movimentada do que o esperado (sinal de que vazou).
  • Trate a chave como uma senha. Nunca a inclua em commits, nunca a coloque em JavaScript do lado do cliente, nunca a cole em um screenshot. Todos os exemplos abaixo a leem de uma variável de ambiente justamente por esse motivo.

Autenticando Requisições

Toda chamada à API REST carrega a chave no cabeçalho Authorization padrão, como um token Bearer:

`` Authorization: Bearer pdt_live_a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6 ``

O servidor extrai o token, faz o hash dele, busca a chave ativa correspondente e compara os hashes. Se o cabeçalho estiver ausente, malformado, apontar para uma chave revogada ou se a chave tiver expirado, você recebe:

``json { "error": "invalid_api_key" } ``

com status HTTP 401. Não há etapa de login separada nem refresh de token — a chave é a credencial. Defina-a uma vez no seu cliente e pronto.

Os Endpoints REST

A API v1 é deliberadamente pequena e somente leitura. Dois endpoints cobrem os dados que a maioria das integrações realmente precisa: seu portfólio e o feed de sinais. Ambos ficam sob /api/v1/ e ambos retornam JSON com um timestamp ISO em generated_at, para que você possa saber quão recente é uma resposta.

GET /api/v1/portfolio

Retorna suas posições ao vivo e um resumo do portfólio, lidos da carteira Polymarket que você conectou em Configurações → Plataformas.

``bash curl -s https://app.predite.io/api/v1/portfolio \ -H "Authorization: Bearer $PREDITE_API_KEY" ``

Uma resposta típica:

``json { "wallet": "0x3a1f...8c2d", "positions": [ { "market": "Will the Fed cut rates in July 2026?", "outcome": "Yes", "shares": 420.0, "avg_price": 0.61, "current_price": 0.68, "value": 285.6, "unrealized_pnl": 29.4 } ], "summary": { "total_value": 4128.55, "unrealized_pnl": 312.10, "open_positions": 7 }, "generated_at": "2026-06-01T14:32:08.114Z" } ``

Se você ainda não conectou uma carteira, receberá { "positions": [], "summary": null, "error": "no_wallet_connected" } com status 200 — então verifique o campo error em vez de presumir que um 200 significa dados. O endpoint lê os mesmos dados de posição on-chain que o dashboard usa, então o P&L aqui corresponde ao que você vê na interface.

GET /api/v1/signals

Retorna os principais sinais de EV (valor esperado) do scanner de mercado — o mesmo motor que alimenta o feed de sinais do dashboard. Cada sinal compara o preço atual de mercado com o preço justo estimado pela AI do Predite e reporta o edge em pontos percentuais.

Ele aceita dois parâmetros de query:

  • limit — quantos sinais retornar, de 1 a 100, padrão 20.
  • min_edge — edge mínimo em pontos percentuais, padrão 5. Use isto para filtrar sinais marginais.

``bash curl -s "https://app.predite.io/api/v1/signals?limit=10&min_edge=8" \ -H "Authorization: Bearer $PREDITE_API_KEY" ``

``json { "signals": [ { "market_title": "Will Bitcoin close above $120k on 2026-06-30?", "direction": "YES", "edge_pp": 11.4, "market_price": 0.42, "ai_price": 0.534, "created_at": "2026-06-01T14:05:00.000Z" } ], "count": 1, "filters": { "min_edge": 8, "limit": 10 }, "generated_at": "2026-06-01T14:32:40.781Z" } ``

Aqui, um edge_pp de 11.4 significa que o preço justo da AI (0.534) está 11.4 pontos acima do preço de mercado (0.42) no lado YES — o tipo de diferença que um trader de EV procura. Os resultados são ordenados por edge decrescente e, em seguida, por recência. O bloco filters ecoa de volta o que foi aplicado, para que você possa confirmar que seus parâmetros foram interpretados corretamente.

Um cliente em Python

Aqui está um pequeno wrapper que lida com a autenticação e com ambos os endpoints. Ele usa requests, mas o formato se traduz para qualquer cliente HTTP.

```python import os import requests

BASE = "https://app.predite.io/api/v1" KEY = os.environ["PREDITE_API_KEY"] SESSION = requests.Session() SESSION.headers["Authorization"] = f"Bearer {KEY}"

def get_portfolio(): r = SESSION.get(f"{BASE}/portfolio", timeout=10) r.raise_for_status() return r.json()

def get_signals(limit=20, min_edge=5.0): params = {"limit": limit, "min_edge": min_edge} r = SESSION.get(f"{BASE}/signals", params=params, timeout=10) r.raise_for_status() return r.json()

if __name__ == "__main__": for s in get_signals(limit=5, min_edge=10)["signals"]: print(f"{s['edge_pp']:+.1f}pp {s['direction']:>3} {s['market_title']}") ```

Reutilizar uma única Session mantém a conexão viva entre as chamadas, o que faz diferença quando você está fazendo polling em um cronograma.

Rate Limits

A API permite 120 requisições por minuto por chave, e esse teto é o mesmo em todos os planos. É generoso para a carga somente-leitura para a qual a API foi feita: consultar o portfólio uma vez por minuto usa menos de 1% dele, sobrando folga de sobra para checar sinais. Se estourar, a chamada volta 429 com o teto no corpo — recue em vez de tentar de novo na hora.

Alguns hábitos mantêm você bem dentro do limite:

  • Não faça polling mais rápido do que precisa. Os sinais de EV são atualizados na cadência do cron do scanner, não continuamente, então fazer polling de /signals a cada poucos segundos apenas queima a cota retornando dados idênticos. Uma vez por minuto é mais que suficiente; uma vez a cada poucos minutos costuma bastar.
  • Prefira webhooks para eventos. Se você está fazendo polling para detectar *que algo aconteceu* — uma whale se movimentou, um stop-loss disparou — você está usando a ferramenta errada. Os webhooks (abaixo) entregam esses eventos a você no instante em que ocorrem, com custo zero de polling.
  • Faça cache dentro de um mesmo ciclo. Se três partes do seu pipeline precisam do portfólio, busque-o uma vez e repasse-o, em vez de chamar três vezes.
  • Recue diante de erros. Se você for limitado em algum momento, encare isso como um sinal para desacelerar, não para tentar de novo em um loop apertado.

O modelo mental: use a API REST para puxar estado sob demanda e use webhooks para reagir a eventos. Confundir os dois é a causa mais comum de cota desperdiçada.

Assinaturas de Webhook

Os webhooks invertem a direção. Em vez de você perguntar ao Predite "tem algo novo?", o Predite chama a *sua* URL no momento em que algo acontece. Como o payload é JSON puro sobre HTTPS POST, qualquer coisa que consiga receber uma requisição HTTP funciona como destino: webhooks de entrada do Discord e do Slack, plataformas de automação como n8n e Zapier, ou o seu próprio servidor.

Criando uma assinatura

  1. Vá em Configurações → Webhooks (plano Bot necessário).
  2. Clique em Adicionar webhook e cole a URL de destino. Em produção, ela deve ser https:// — HTTP puro é rejeitado.
  3. Dê a ela um rótulo e selecione quais tipos de evento ela deve receber. Uma assinatura precisa escutar pelo menos um evento.
  4. Salve. O Predite gera um segredo de assinatura e o exibe na visualização de detalhes da assinatura.

O segredo tem esta aparência:

`` whsec_4f8c...d2a1 ``

(o prefixo whsec_ mais 64 caracteres hexadecimais). Você precisará dele para verificar as entregas — guarde-o junto ao destino, idealmente como uma variável de ambiente no serviço receptor.

Tipos de evento

São seis tipos de evento assináveis, e os seis entregam hoje:

  • `ev_signal` — o scanner encontrou uma nova oportunidade de EV acima do limiar.
  • `arb_opportunity` — apareceu uma diferença de arbitragem entre Polymarket e Kalshi em mercados equivalentes.
  • `stop_loss_triggered` — uma das suas proteções cruzou o gatilho e disparou.
  • `bot_trade_executed` — um dos seus bots abriu ou fechou um trade.
  • `whale_move` — uma carteira rastreada tomou posição de US$ 25 mil ou mais. Evento global: todo assinante recebe, e cada move é deduplicado pra nunca chegar duas vezes.
  • `resolution_imminent` — um mercado *em que você tem posição* entrou nas últimas 24 horas antes da resolução. Por usuário, e enviado uma vez por mercado.

Os dois últimos eram assináveis e mudos: as fontes deles são consultas puras, então sem memória do que já foi enviado um cron reemitiria os mesmos eventos a cada run. Essa memória agora existe — foi o que os tornou entregáveis.

Assine cada destino apenas nos eventos que lhe interessam. Seu pager de plantão provavelmente quer apenas stop_loss_triggered; um canal de pesquisa pode querer whale_move e ev_signal; um fluxo de automação que faz rebalanceamento pode querer bot_trade_executed.

Payload e cabeçalhos de entrega

Toda entrega é um POST HTTP com um corpo JSON no formato:

``json { "event": "ev_signal", "timestamp": "2026-06-01T14:33:12.004Z", "data": { "market_title": "Will the ECB hold rates in June 2026?", "direction": "NO", "edge_pp": 9.2, "market_price": 0.71, "ai_price": 0.618 } } ``

O campo event informa o tipo, timestamp é quando o Predite o despachou e data é o payload específico do evento. Junto ao corpo, o Predite envia estes cabeçalhos:

  • X-Predite-Event — o tipo do evento, para que você possa rotear sem fazer parsing do corpo.
  • X-Predite-Timestamp — o horário de despacho (corresponde ao timestamp do corpo).
  • X-Predite-Signature — a assinatura HMAC-SHA256, formatada como sha256=<hex>.
  • User-Agent: Predite-Webhooks/1.0.

Seu endpoint deve responder com qualquer status 2xx rapidamente. A entrega expira após 5 segundos, então confirme rápido e faça o trabalho pesado de forma assíncrona — retorne 200 e, então, processe.

Comportamento de confiabilidade

A entrega não é do tipo "dispare e esqueça" do lado do Predite. Entregas com falha (uma resposta não-2xx, um timeout ou um erro de rede) são registradas, e falhas transitórias de 5xx/rede são repetidas com backoff exponencial. Se uma assinatura acumular 10 falhas consecutivas, o Predite a desativa automaticamente (status disabled_by_failures) para parar de martelar um endpoint morto. Você verá o motivo da falha em Configurações → Webhooks, e reativar a assinatura zera o contador de falhas. Cada assinatura também rastreia o último sucesso, a última falha e o total de entregas, para que você possa auditar a saúde de relance.

Verificando Assinaturas de Webhook

Como a URL do seu webhook é apenas um endpoint HTTP, qualquer pessoa que a descubra poderia enviar eventos falsos por POST. O cabeçalho de assinatura é como você comprova que uma entrega veio genuinamente do Predite e não foi adulterada em trânsito.

O esquema é simples: o Predite calcula HMAC-SHA256(raw_request_body, your_secret) e envia o digest hexadecimal em X-Predite-Signature como sha256=<digest>. Do seu lado, você recalcula o mesmo HMAC sobre o corpo bruto, sem parsing e compara. A parte do "corpo bruto" importa — se o seu framework faz o parsing do JSON e você o re-serializa, o espaçamento e a ordem das chaves podem diferir e a assinatura não vai bater. Capture os bytes antes do parsing.

Uma comparação à prova de timing em Python:

```python import hashlib import hmac

def verify(raw_body: bytes, signature_header: str, secret: str) -> bool: if not signature_header or not signature_header.startswith("sha256="): return False sent = signature_header.split("=", 1)[1] expected = hmac.new( secret.encode(), raw_body, hashlib.sha256 ).hexdigest() return hmac.compare_digest(sent, expected) ```

Um receptor Flask mínimo que rejeita qualquer coisa não assinada:

```python import os from flask import Flask, request, abort

app = Flask(__name__) SECRET = os.environ["PREDITE_WEBHOOK_SECRET"]

@app.post("/predite-webhook") def receiver(): raw = request.get_data() # raw bytes, before JSON parsing if not verify(raw, request.headers.get("X-Predite-Signature", ""), SECRET): abort(401) payload = request.get_json() event = payload["event"] # route on event, then return fast handle(event, payload["data"]) return "", 200 ```

Use sempre uma comparação de tempo constante (hmac.compare_digest, não ==), para não vazar bytes da assinatura por meio de timing. E rejeite a requisição antes de fazer qualquer trabalho real se a verificação falhar — essa única checagem é a diferença entre um pipeline confiável e uma porta aberta.

Construindo um Pipeline de Alertas Personalizado

Vamos amarrar tudo com um exemplo concreto: você quer que todo sinal de EV de alta convicção e todo gatilho de stop-loss cheguem ao Slack, e quer que os stop-losses adicionalmente disparem um fluxo no n8n que registra o evento e (opcionalmente) fecha a posição por meio da sua própria integração com a corretora.

  1. Crie duas assinaturas de webhook em Configurações → Webhooks. Uma aponta para a URL do seu webhook de entrada do Slack e assina ev_signal. A outra aponta para o seu nó de webhook do n8n e assina stop_loss_triggered. Cada uma recebe seu próprio segredo.
  2. Para o Slack, a abordagem mais limpa é um pequeno relay (uma função serverless funciona bem): ele verifica a assinatura do Predite, formata o data em uma mensagem do Slack e a publica na URL do Slack. O relay permite que você filtre — por exemplo, encaminhar apenas sinais em que data.edge_pp >= 10 — para que você não seja barulhento no canal. Mercados com edge pequeno são descartados; apenas os fortes acionam a equipe.
  3. Para o n8n, coloque um nó de trigger Webhook primeiro e, em seguida, um nó Function que execute a mesma verificação HMAC contra o corpo bruto antes que qualquer coisa downstream seja executada. Após a verificação, faça um branch com base em data para registrar o evento e chamar sua corretora. O Zapier segue o mesmo padrão, com um passo Code fazendo a verificação.
  4. Adicione a API REST para contexto. Quando um evento stop_loss_triggered chega, seu fluxo no n8n pode chamar imediatamente GET /api/v1/portfolio com sua chave de API para puxar o conjunto completo de posições atuais, de modo que o alerta seja enriquecido com P&L ao vivo em vez de apenas os dados do gatilho. Este é o padrão de leitura sob demanda funcionando ao lado do padrão de push.
  5. Torne-o idempotente. As redes fazem retentativas; a mesma entrega pode chegar duas vezes. Baseie suas ações downstream em algo estável (o evento mais seu timestamp, ou um ID dentro de data) para que uma entrega duplicada não aja em dobro.
  6. Monitore o monitor. Verifique Configurações → Webhooks periodicamente em busca de disabled_by_failures, e faça com que seu relay registre toda assinatura rejeitada — um pico repentino significa que ou seu segredo foi rotacionado ou alguém está sondando seu endpoint.

Com isso no lugar, você tem um ciclo fechado: o scanner e o motor de risco do Predite empurram eventos no instante em que disparam, seus relays os verificam e filtram, e suas chamadas REST preenchem o estado ao vivo sob demanda — tudo sem um único loop de polling queimando cota.

Se você está no plano Bot, a maneira mais rápida de sentir isso fazer sentido é ir até Configurações → Webhooks, apontar uma assinatura de teste para um canal descartável do Discord, assiná-la em ev_signal e observar o primeiro sinal real chegar como um POST JSON — depois conecte o trecho de verificação acima e você estará a um relay de distância da produção.