Webhooks e alertas de preço
Receba callbacks HTTP assinados quando um preço cruzar o seu limiar, verifique-os com HMAC-SHA256 e trate as novas tentativas de entrega com segurança.
Como funcionam os alertas e webhooks
A MetalAPI envia webhooks quando um alerta de preço é disparado. Um alerta monitora um símbolo em uma moeda de sua escolha. Ele é disparado quando o preço sobe acima ou cai abaixo de um limiar, ou quando sobe ou cai uma determinada porcentagem desde o fechamento anterior.
Os alertas disparam no cruzamento: você é notificado uma vez quando a condição se torna verdadeira, e o alerta é rearmado depois que a condição volta a ser falsa. Um tempo de espera (60 minutos por padrão, configurável de 5 minutos a 7 dias) evita uma enxurrada de notificações quando o preço oscila em torno do limiar.
Os alertas notificam você por e-mail a partir do plano Lite, ou por webhook (um POST HTTPS para o seu servidor) a partir do plano Advanced.
Configurando um webhook
- Abra Alerts no painel e crie um alerta.
- Escolha o símbolo, a moeda, a condição e o limiar.
- Escolha a entrega por webhook e informe a URL do seu endpoint.
- Copie o segredo de assinatura do webhook (ele começa com
whsec_) do painel para a configuração do seu servidor.
As URLs de webhook devem usar https:// em um host público. URLs que contêm credenciais ou que resolvem para endereços IP privados ou internos são rejeitadas, e redirecionamentos não são seguidos.
O payload
Cada entrega é um POST JSON com estes cabeçalhos e este corpo:
POST /webhooks/metalapi HTTP/1.1
Content-Type: application/json
User-Agent: MetalAPI-Webhooks/1.0
X-MetalAPI-Timestamp: 1792765800
X-MetalAPI-Signature: t=1792765800,v1=3b9f0c4e…a71d
{
"event": "alert.triggered",
"id": 18342,
"created": 1792765800,
"alert": {
"id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"symbol": "XAU",
"base": "USD",
"condition": "above",
"threshold": 4000,
"note": "Gold above 4k"
},
"price": 4001.25,
"unit": "troy_oz"
}condition é um destes: above, below, change_pct_up ou change_pct_down. price é o preço que disparou o alerta, em base por unit. id é único por evento, então você pode usá-lo para ignorar entregas duplicadas.
Verificando a assinatura
Sempre verifique se uma requisição realmente vem da MetalAPI antes de agir com base nela. O cabeçalho X-MetalAPI-Signature tem o formato t=<unix timestamp>,v1=<signature>. A assinatura é o HMAC-SHA256, codificado em hexadecimal, da string <timestamp>.<raw request body>, usando o seu segredo de assinatura como chave.
- Calcule o HMAC sobre o corpo bruto da requisição, antes de qualquer parsing de JSON.
- Compare as assinaturas em tempo constante.
- Rejeite timestamps com mais de cinco minutos para bloquear requisições reenviadas (ataques de replay).
Em Node.js com Express:
import crypto from "node:crypto";
import express from "express";
const app = express();
const SECRET = process.env.METALAPI_WEBHOOK_SECRET;
app.post("/webhooks/metalapi", express.raw({ type: "application/json" }), (req, res) => {
const header = req.get("X-MetalAPI-Signature") ?? "";
const parts = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const body = req.body.toString("utf8");
const expected = crypto.createHmac("sha256", SECRET).update(`${parts.t}.${body}`).digest("hex");
const valid =
typeof parts.v1 === "string" &&
parts.v1.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(parts.v1), Buffer.from(expected));
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
if (!valid || !fresh) return res.status(400).send("invalid signature");
const event = JSON.parse(body);
console.log(event.alert.symbol, event.alert.condition, event.alert.threshold, event.price);
res.sendStatus(200);
});Verificação em PHP
A mesma verificação em PHP puro:
<?php
$secret = getenv('METALAPI_WEBHOOK_SECRET');
$payload = file_get_contents('php://input');
$header = $_SERVER['HTTP_X_METALAPI_SIGNATURE'] ?? '';
parse_str(str_replace(',', '&', $header), $parts);
$timestamp = (int) ($parts['t'] ?? 0);
$expected = hash_hmac('sha256', $timestamp . '.' . $payload, $secret);
if (!hash_equals($expected, $parts['v1'] ?? '') || abs(time() - $timestamp) > 300) {
http_response_code(400);
exit('invalid signature');
}
$event = json_decode($payload, true);
// Trate $event['alert'] e $event['price'] aqui.
http_response_code(200);Respostas e novas tentativas
Retorne qualquer status 2xx em até 8 segundos. Faça o trabalho demorado depois de responder, por exemplo em uma fila, porque timeouts e respostas que não sejam 2xx contam como entregas com falha.
As entregas com falha são repetidas com intervalos crescentes, até três tentativas no total na primeira hora após o disparo do alerta. Uma nova tentativa pode chegar depois de uma entrega que expirou do nosso lado, mas foi bem-sucedida do seu, então armazene o id do evento e torne o seu handler idempotente.
Guias relacionados
Pronto para começar a desenvolver?
Crie uma conta grátis para obter sua chave de API ou explore todos os endpoints na referência da API.