Contrato 2026-10-01
Firma y cabeceras
Cada llamada trae HMAC-SHA256 del cuerpo crudo. Acepta la petición solo si la firma y la hora coinciden.
Astro firma el cuerpo con un secreto que empieza por whsec_. El negocio lo ve una sola vez, al generarlo en la cuenta, y te lo entrega por un canal seguro. Guárdalo fuera del repositorio.
Cabeceras
| Cabecera | Valor |
|---|---|
Content-Type |
application/json |
User-Agent |
Astro-Integraciones/1.0 |
X-Astro-Event |
El mismo valor que evento en el cuerpo |
X-Astro-Delivery |
Identificador de este intento. Cambia si Astro reintenta |
Idempotency-Key |
Igual a id_evento. Se mantiene entre reintentos |
X-Astro-Signature |
t=<unix>,v1=<hex> |
X-Astro-Delivery sirve para tu bitácora. La clave para no duplicar un pedido es Idempotency-Key.
Cuerpo
El cuerpo es JSON en UTF-8, compacto y sin espacios. Los acentos van como caracteres, no como \u00e9.
| Campo | Qué es |
|---|---|
version |
Versión del contrato. Hoy 2026-10-01 |
evento |
ping, catalogo.consultar, transaccion.crear o transaccion.cancelar |
id_evento |
UUID de la operación. En un pedido o una cita se mantiene en cada reintento |
fecha |
Momento del envío, en ISO 8601 con zona horaria |
tenant_id |
Cuenta del negocio en Astro |
sucursal |
{ "codigo", "nombre" }, o null si el negocio tiene una sola sede |
data |
Datos de la operación. Cambian según el evento |
Ignora los campos que no reconozcas. Astro también ignora campos de más en tu respuesta.
Cómo comprobar la firma
- Lee el cuerpo como bytes, antes de interpretarlo como JSON.
- De la cabecera
X-Astro-Signature, toma elty todos losv1. - Rechaza la petición si
tno es un número, si no hay ningúnv1, o sitse aleja más de 300 segundos de la hora actual. - Calcula HMAC-SHA256 de
t, un punto y el cuerpo. La clave es el secreto en UTF-8. - Acepta si ese hexadecimal coincide con algún
v1. Compara en tiempo constante.
Durante las 24 horas posteriores a una rotación, la cabecera trae dos v1: el secreto nuevo y el anterior. Con el secreto que tengas guardado basta con que uno coincida.
Si la firma no coincide, responde HTTP 401. Astro lo toma como fallo de autenticación y vuelve a intentar. Reserva el 401 para ese caso.
La firma cubre los bytes recibidos. Si vuelves a serializar el JSON antes de verificar, los espacios o el orden cambian y una petición válida parece falsa.
Ejemplo en Python
import hashlib
import hmac
import time
VENTANA_SEGUNDOS = 300
def verificar(cuerpo: bytes, cabecera: str, secreto: str) -> bool:
marcas = []
firmas = []
for item in cabecera.split(","):
clave, _, valor = item.partition("=")
clave, valor = clave.strip(), valor.strip()
if clave == "t":
marcas.append(valor)
elif clave == "v1" and valor:
firmas.append(valor)
if len(marcas) != 1 or not marcas[0].isdigit() or not firmas:
return False
timestamp = int(marcas[0])
if abs(time.time() - timestamp) > VENTANA_SEGUNDOS:
return False
esperado = hmac.new(
secreto.encode("utf-8"),
f"{timestamp}.".encode("utf-8") + cuerpo,
hashlib.sha256,
).hexdigest()
return any(hmac.compare_digest(esperado, firma) for firma in firmas)
Los mismos pasos valen en cualquier lenguaje que tenga HMAC-SHA256.
Direcciones que Astro acepta
El negocio configura el origen, no la ruta. Astro llama a {origen}/astro/producto/webhook y a {origen}/astro/pedido/webhook.
Ese origen tiene que usar https y un dominio que resuelva a una IP pública. Astro rechaza http, direcciones privadas, usuario o contraseña en la URL, y parámetros de consulta. La comprobación de la IP se hace en cada llamada.