Webhooks
Registra una URL HTTPS en API / Desarrolladores → Webhooks o con POST /webhooks. Gravitas te envía
un POST con un JSON por cada evento:
document.accepted, document.accepted_conditional, document.rejected, document.delivered,
document.commercially_approved, document.commercially_rejected, received_document.arrived,
range.low.
Verificar la firma
Sección titulada «Verificar la firma»Cada entrega trae el encabezado Gravitas-Signature: t=<unix>,v1=<hex>. La firma es HMAC-SHA256 de
"{t}.{cuerpo}" con el secreto del webhook. Rechaza firmas con más de 5 minutos.
# Verificar la firma de un webhook (Gravitas-Signature: t=<unix>,v1=<hex>[,v1=<hex>]).# BODY es el cuerpo recibido sin modificar; WEBHOOK_SECRET es el secreto del endpoint (whsec_...).T=$(echo "$SIGNATURE_HEADER" | tr ',' '\n' | sed -n 's/^t=//p')EXPECTED=$(printf '%s.%s' "$T" "$BODY" | openssl dgst -sha256 -hmac "$WEBHOOK_SECRET" | sed 's/^.* //')echo "$SIGNATURE_HEADER" | tr ',' '\n' | grep -qx "v1=$EXPECTED" && echo "firma válida" || echo "firma inválida"# Rechace eventos con t de más de 5 minutos y elimine duplicados por Gravitas-Event-Id.// Verificar la firma de un webhook (Gravitas-Signature: t=<unix>,v1=<hex>[,v1=<hex>]).using System.Security.Cryptography;using System.Text;
static bool VerifyGravitasSignature(string secret, string header, string body){ var parts = header.Split(',').Select(p => p.Split('=', 2)).Where(p => p.Length == 2).ToList(); var t = parts.FirstOrDefault(p => p[0] == "t")?[1]; if (t is null || Math.Abs(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - long.Parse(t)) > 300) { return false; // más de 5 minutos: rechazar }
var expected = Convert.ToHexStringLower(HMACSHA256.HashData(Encoding.UTF8.GetBytes(secret), Encoding.UTF8.GetBytes(t + "." + body))); return parts.Where(p => p[0] == "v1").Any(p => CryptographicOperations.FixedTimeEquals(Encoding.ASCII.GetBytes(p[1]), Encoding.ASCII.GetBytes(expected)));}// Elimine duplicados por el encabezado Gravitas-Event-Id (entrega al menos una vez).// Verificar la firma de un webhook (Gravitas-Signature: t=<unix>,v1=<hex>[,v1=<hex>]). Node 20+.import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyGravitasSignature(secret, header, rawBody) { const parts = header.split(',').map((p) => p.split('=')); const t = parts.find(([k]) => k === 't')?.[1]; if (!t || Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; // más de 5 minutos: rechazar const expected = Buffer.from(createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex')); return parts .filter(([k]) => k === 'v1') .some(([, v]) => v.length === expected.length && timingSafeEqual(Buffer.from(v), expected));}// Elimine duplicados por el encabezado Gravitas-Event-Id (entrega al menos una vez).Durante la rotación del secreto (24 horas), cada entrega trae dos valores v1: acepta el mensaje si
cualquiera coincide.
Entregas
Sección titulada «Entregas»- Responde
2xxen menos de 10 segundos; procesa en segundo plano. - Si fallas, reintentamos durante 24 horas con espera creciente. Puedes ver el historial y reenviar desde el portal.
- La entrega es al menos una vez: elimina duplicados con el encabezado
Gravitas-Event-Id. - Usa el botón Probar para enviar un evento de prueba.