Ir al contenido

Integración de Webhooks

Los webhooks permiten que tus sistemas reciban notificaciones en tiempo real cuando ocurren eventos en tu cuenta Wink — nuevas reservas, cancelaciones, actualizaciones de pagos y más. Esta guía te acompaña en la configuración y las mejores prácticas.

Esta guía está dirigida a desarrolladores que integran Wink con sistemas externos como sistemas de gestión de propiedades (PMS), gestores de canales, CRM o paneles personalizados.

  1. Registrás una URL de webhook en Wink.
  2. Cuando ocurre un evento (por ejemplo, una nueva reserva), Wink envía un HTTP POST a tu URL.
  3. Tu servidor procesa la carga útil y responde con un 200 OK.
  1. Iniciá sesión en tu cuenta (Extranet, Studio o TripPay — todos soportan webhooks).
  2. Navegá a Applications y luego a Webhooks. Consultá Webhooks.
  3. Hacé clic en Create webhook.
  4. Ingresá un nombre (por ejemplo, “Sincronización de reservas PMS”).
  5. Ingresá tu URL de webhook — el endpoint HTTPS en tu servidor.
  6. Seleccioná eventos — Elegí eventos específicos para suscribirte, o dejá vacío para recibir todos los eventos.
  7. Activá el interruptor Enabled.
  8. Hacé clic en Save — la respuesta muestra tu secreto de firma una sola vez; guardalo ahora.

Wink publica hoy 70 tipos de eventos de webhook en reservas, propiedades, cuentas (entidades gestoras) e inventario (tipos de habitación, planes tarifarios, tarifas maestras, complementos, servicios, canales de venta, promociones). Los más comunes:

CategoríaEjemplos
Reservabooking.create, booking.cancelled, booking.refund.partial, booking.refund.full, booking.review.created
Propiedadproperty.created, property.status.updated, property.policy.updated
Inventarioroom_type.updated, rate_plan.created, master_rate.updated, special_rate.created, sales_channel.created
Cuentamanaging_entity.created, managing_entity.status.updated, managing_entity.manager.added

La lista completa y generada — con descripción, destinatarios y enlace a la página de referencia de cada evento — es el Catálogo de Eventos de Webhook. La página de referencia para cada evento (cuerpo JSON, encabezados, política de reintentos) está en la API de Webhooks.

Ver todos los tipos de eventos

Cada entrega es un HTTP POST a tu URL de webhook con Content-Type: application/json y este sobre:

{
"id": "0198a4f2-6b0e-7c1d-9a3e-2f4b8c6d1e0a",
"type": "booking.create",
"occurredAt": "2026-08-15T09:30:00Z",
"ownerIdentifier": "3c6b1a5d-8e2f-4a0b-9c7d-6e4f0a8b2c51",
"recipientRole": "SUPPLIER",
"schemaVersion": 2,
"object": { "...": "payload específico del evento, p.ej. BookingWebhookPayload" }
}
  • id — identificador del evento; idéntico para cada endpoint de tu cuenta que reciba este evento y para cada reintento. Usalo como tu clave de idempotencia.
  • type — clave del tipo de evento (también enviada como encabezado Wink-Event-Type). Ramificá según type y schemaVersion para parsear object.
  • object — un resumen curado del recurso al que se refiere el evento (identificadores, estado, los campos sobre los que actuás) más links.self, la URL REST canónica del lado del proveedor del recurso completo. Consultala con tus propias credenciales API cuando necesites más que el resumen; si recibís el evento como revendedor o agencia de viajes, usá el endpoint correspondiente de tu propia API para el mismo identificador.

Cada esquema de carga útil está documentado por evento en la referencia de la API de Webhooks.

EncabezadoSignificado
Wink-VersionVersión del contrato wire, 2.0.
Wink-Event-IdIgual que id en el cuerpo — tu clave de idempotencia.
Wink-Delivery-IdÚnico por endpoint y evento; cambia solo si reentregás.
Wink-Event-TypeIgual que type en el cuerpo.
Wink-Delivery-AttemptNúmero de intento basado en 1 para esta entrega.
Wink-SignatureFirma HMAC — ver más abajo.

Cada webhook tiene un secreto de firma (whsec_…) que Wink muestra una sola vez, cuando creás el webhook o rotás su secreto. Guardalo como una contraseña. Cada entrega lleva

Wink-Signature: t=1755250200,v1=5d41402abc4b2a76b9719d911017c592…

donde t es un timestamp Unix (segundos) y v1 es el HMAC-SHA256 en minúsculas hexadecimal de la cadena t + "." + rawBody, con clave tu secreto, y rawBody es el cuerpo exacto de la solicitud tal como se recibió — no reserialices el JSON antes de verificar. Durante 24 horas después de rotar el secreto, el encabezado lleva un segundo valor v1= firmado con el secreto anterior; aceptá la entrega si algún v1 coincide.

Verificá en cuatro pasos: parseá t y cada v1; recalculá el HMAC sobre t.rawBody con tu secreto; compará con una comparación en tiempo constante; rechazá si |now − t| excede tu tolerancia (se recomiendan 5 minutos).

// Node.js (estilo Express; asegurate de tener el cuerpo RAW, no un objeto parseado)
import { createHmac, timingSafeEqual } from 'node:crypto';
export function verifyWinkSignature(header, rawBody, secret, toleranceSeconds = 300) {
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=').map((s) => s.trim())));
const t = Number(parts.t);
if (!Number.isFinite(t) || Math.abs(Date.now() / 1000 - t) > toleranceSeconds) return false;
const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
return header
.split(',')
.filter((p) => p.trim().startsWith('v1='))
.map((p) => p.trim().slice(3))
.some((v1) => v1.length === expected.length && timingSafeEqual(Buffer.from(v1, 'utf8'), Buffer.from(expected, 'utf8')));
}
// Java
static boolean verify(String header, String rawBody, String secret, long nowSeconds, long toleranceSeconds) throws Exception {
long t = Long.MIN_VALUE;
List<String> signatures = new ArrayList<>();
for (String part : header.split(",")) {
String[] kv = part.trim().split("=", 2);
if (kv[0].equals("t")) t = Long.parseLong(kv[1]);
else if (kv[0].equals("v1")) signatures.add(kv[1]);
}
if (t == Long.MIN_VALUE || Math.abs(nowSeconds - t) > toleranceSeconds) return false;
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] expected = HexFormat.of().formatHex(mac.doFinal((t + "." + rawBody).getBytes(StandardCharsets.UTF_8))).getBytes(StandardCharsets.US_ASCII);
return signatures.stream().anyMatch(v1 -> MessageDigest.isEqual(expected, v1.toLowerCase().getBytes(StandardCharsets.US_ASCII)));
}

Rotá el secreto desde el portal o con POST /api/managing-entity/{id}/webhook/{webhookId}/rotate-secret; la respuesta muestra el nuevo secreto una sola vez, y el anterior sigue verificando durante 24 horas mientras lo implementás.

  • Respondé con cualquier 2xx dentro de 10 segundos para confirmar. Hacé el trabajo pesado de forma asíncrona.
  • Un 5xx, un timeout, 408 o 429 se reintenta con backoff: después de 1 minuto, 5 minutos, 30 minutos, 2 horas, 6 horas, 12 horas, luego diario — 10 intentos en unos 3 días — tras lo cual la entrega se marca como muerta.
  • Cualquier otro 4xx se trata como “rechazaste esta entrega” y no se reintenta.
  • Cada evento, entrega e intento (estado, fragmento de respuesta) es visible en Applications > Webhooks y a través de la API (…/webhook/event/grid, …/webhook/delivery/grid). Podés reentregar cualquier entrega (POST …/webhook/delivery/{deliveryId}/redeliver, que inicia una nueva serie de reintentos), reentregar todas las entregas muertas de un webhook a la vez (POST …/webhook/{webhookId}/redeliver-dead), o cancelar una.
  • Las entregas se conservan por 30 días.

Enviate un evento sintético webhook.test desde el portal o con POST /api/managing-entity/{id}/webhook/{webhookId}/test. Está firmado y entregado exactamente como un evento real, así podés verificar tu endpoint, la verificación de firma y el manejo de idempotencia antes de suscribirte a eventos en vivo.

  • Usá HTTPS — Wink envía cargas útiles solo a endpoints HTTPS.
  • Respondé rápido — Devolvé un 200 OK tan pronto recibas la carga útil. Hacé cualquier procesamiento pesado de forma asíncrona.
  • Idempotencia — Tu manejador debe ser idempotente; deduplicá con Wink-Event-Id. Wink reintenta si no recibe una respuesta 2xx.
  • Validá la fuente — Verificá el encabezado Wink-Signature (ver Verificación de firmas) antes de procesar; rechazá todo lo que falle.
  • Registro — Registrá cada carga útil de webhook que recibas. Esto facilita mucho la depuración de problemas de integración.

Podés desactivar un webhook sin eliminarlo. Esto pausa la entrega para que puedas solucionar problemas sin perder tu configuración. Cuando estés listo, activalo de nuevo.

Eliminar un webhook lo borra permanentemente. Cualquier integración que dependa de ese webhook dejará de recibir notificaciones.