Formato del sobre
La solicitud que Vipter hace a tu endpoint, campo por campo, con los encabezados, el plazo de respuesta, cómo tratar los eventos repetidos y lo que Vipter garantiza sobre el orden.
Cada evento llega como un POST a la URL del endpoint. El cuerpo es un objeto JSON, el sobre, que es igual para todos los tipos. Lo que cambia es el objeto dentro de data.object, descrito en el catálogo de eventos.
La solicitud
POST /webhooks/vipter HTTP/1.1
Host: erp.example.com
Content-Type: application/json
User-Agent: Vipter-Webhooks/1.0
Vipter-Signature: t=1790604192,v1=5d41402abc4b2a76b9719d911017c592ae2e6b7c0f1d3e5a7b9c2d4f6a8b0c1e
Vipter-Event-Id: evt_3f9a1c7e5b2d4f6a8c0e1b3d
Vipter-Event-Type: order.paid
{"id":"evt_3f9a1c7e5b2d4f6a8c0e1b3d","object":"event","type":"order.paid","created":1790604191,"livemode":true,"api_version":"2026-09-01","data":{"object":{"object":"order","id":"ord_5c1e8a2b9d4f4e7a8b3c6d1e2f7a9b0c", "…": "…"}}}El cuerpo viene en JSON compacto, sin espacios ni saltos de línea, en UTF-8. No reformatees el cuerpo antes de verificar la firma.
Encabezados
| Encabezado | Contenido |
|---|---|
Content-Type | application/json |
User-Agent | Vipter-Webhooks/1.0 |
Vipter-Signature | t=<segundos Unix>,v1=<HMAC en hexadecimal>. Durante la rotación del secreto, viene un segundo v1=. Consulta Verificar la firma. |
Vipter-Event-Id | El mismo valor del campo id del cuerpo. Permite descartar una repetición antes de leer el cuerpo. |
Vipter-Event-Type | El mismo valor del campo type del cuerpo. Permite enrutar el evento sin leer el cuerpo. |
En la mayoría de los frameworks, los nombres de encabezado no distinguen mayúsculas: vipter-signature en Node.js, $_SERVER['HTTP_VIPTER_SIGNATURE'] en PHP.
Campos del sobre
| Campo | Tipo | Contenido |
|---|---|---|
id | texto | ID único del evento: evt_ seguido de 24 caracteres hexadecimales. Es el mismo en todos los intentos y reenvíos. |
object | texto | Siempre "event". |
type | texto | Tipo del evento, como order.paid. Consulta el catálogo. |
created | entero | Cuándo se creó el evento, en segundos Unix. No cambia entre intentos. |
livemode | booleano | El entorno de Vipter que generó el evento. No indica si el proveedor de pago estaba en modo de prueba: una compra de prueba con un proveedor en sandbox llega con el mismo valor que las ventas reales. |
api_version | texto | Versión del formato. Hoy todos los eventos salen con "2026-09-01". |
data.object | objeto | El pedido, la suscripción, el cliente o el checkout abandonado, en el estado en que estaba cuando se creó el evento. |
created y la t de la firma son cosas distintas. created es el momento del evento y queda fijo. t es el momento del envío de ese intento y cambia en cada intento.
La respuesta
La entrega funciona cuando tu servidor responde con cualquier código 2xx en hasta 10 segundos. El cuerpo de la respuesta se ignora.
Cuenta como fallo, y genera un reintento:
- Cualquier código fuera de 2xx, incluido 3xx. Vipter no sigue redirecciones: si la URL cambió, actualiza el endpoint.
- Ninguna respuesta en 10 segundos. La entrega queda con el error
timeout. - Error de red, DNS o TLS. La URL tiene que ser
https://, con un certificado válido.
Responde 2xx también para los tipos de evento que tu sistema ignora. Una respuesta de error no descarta el evento: vuelve en los intentos siguientes y cuenta para la desactivación automática.
Responde antes de procesar
Verifica la firma, graba el evento en una cola o tabla y responde 200. Haz el trabajo pesado, como llamar a otros sistemas, después de responder.
Idempotencia
La entrega es "al menos una vez". El mismo evento, con el mismo id, puede llegar más de una vez cuando:
- tu servidor procesó el evento pero la respuesta no llegó a Vipter en 10 segundos, y el intento siguiente repite el envío;
- alguien hizo clic en Reenviar en una entrega;
- el envío se interrumpió del lado de Vipter antes de registrar el resultado. La entrega vuelve a la cola después de 5 minutos y se envía de nuevo.
Para eso, guarda el id (o el encabezado Vipter-Event-Id) en una columna con restricción de unicidad e ignora el evento si ya existe:
create table vipter_events (
id text primary key, -- evt_…
type text not null,
received_at timestamptz not null default now()
);
-- Al recibir: si la fila ya existe, el evento es una repetición.
insert into vipter_events (id, type) values ($1, $2) on conflict (id) do nothing;Hay un segundo caso, con un id distinto: el mismo hecho en dos eventos. Un cambio hecho en el panel o en el portal del cliente, como cancelar una suscripción, genera el evento en el momento, y la confirmación del proveedor sobre el mismo cambio puede generar otro evento del mismo tipo. Por eso, además de descartar el id repetido, haz que el procesamiento dependa del estado y no del evento: "marcar la suscripción como cancelada" puede ejecutarse dos veces sin problema, "enviar el correo de cancelación" tiene que comprobar si ya se envió.
Orden de los eventos
Vipter no garantiza el orden de llegada. Las entregas se hacen en paralelo, y una entrega que falló vuelve horas después, cuando ya llegaron eventos más nuevos. Un subscription.renewed puede llegar antes del order.paid de la renovación, y un order.refunded puede llegar antes del order.paid del mismo pedido, si el order.paid falló en el primer intento.
Para no sobrescribir un estado nuevo con uno antiguo:
- Compara
data.object.updated_atcon lo que ya tienes grabado e ignora el evento si es más antiguo. - O, al recibir cualquier evento de un pedido o suscripción, toma el
statusdel objeto como la verdad en ese momento, y no el tipo del evento.
Qué hacer después
- Verifica la firma con el código listo en Node.js, PHP y Python.
- Consulta el calendario de reintentos y lo que pasa cuando el endpoint está caído.
Catálogo de eventos
Los 20 tipos de evento que Vipter envía por webhook, cuándo se dispara cada uno y lo que viene en data.object, con un ejemplo completo de pedido, suscripción, cliente y checkout abandonado.
Verificar la firma
Cómo firma Vipter cada webhook, código probado en Node.js, PHP y Python para comprobar la firma, cómo rotar el secreto sin perder eventos y los errores que más hacen fallar la verificación.