API pública: receba webhooks de reservas em tempo real

Atualizado em

Os webhooks são a forma mais rápida de se manter sincronizado com a Nowistay. Em vez de consultar a API a cada poucos minutos para detetar reservas novas ou alteradas, indica à Nowistay para onde enviar os eventos e o seu serviço recebe-os assim que acontecem. Este artigo mostra como criar um webhook, verificar a assinatura e tratar as repetições.

Lembrete rápido: o que é a API pública

A API REST pública da Nowistay permite que as suas ferramentas e os seus parceiros leiam e escrevam dados da Nowistay por HTTPS. Autentica-se com OAuth2 (código de autorização + PKCE), recebe um token de acesso e chama os endpoints em /public/v1/*. Os webhooks são o complemento por envio (push): os mesmos dados, com a mesma estrutura de GET /public/v1/bookings/{id}, mas a Nowistay envia-os para si quando algo muda.

Se ainda não configurou o OAuth, comece pelo guia principal: Usar a API REST pública da Nowistay. Abrange o registo, o fluxo de consentimento, os âmbitos (scopes) e como testar chamadas a partir do playground da documentação.

O que obtém

  • Entrega em tempo real dos eventos booking.created e booking.updated.
  • O mesmo corpo JSON de GET /public/v1/bookings/{id}, incluindo datas, canal, estado, montantes, campos relevantes para as missões e os seus parâmetros de reserva personalizados.
  • Assinatura HMAC-SHA256 para poder verificar que cada pedido vem mesmo da Nowistay.
  • Repetições automáticas com recuo exponencial quando o seu endpoint falha ou excede o tempo limite.
  • Desativação automática após 72 horas de falhas contínuas (pode reativar a partir da API).

Antes de começar

  • Precisa da subscrição AI Channel Manager no alojamento. Sem ela, as entregas são ignoradas.
  • O token OAuth usado para criar o webhook tem de ter o âmbito webhooks.write. POST /public/v1/webhooks exige também bookings.read, porque o conteúdo é o detalhe completo de uma reserva.
  • O seu URL de receção tem de estar acessível na internet pública, servido por HTTPS em produção, e responder em 10 segundos com um estado 2xx.
  • Um webhook está associado a um alojamento. Pode registar até 3 webhooks por alojamento.

Passo 1. Crie um webhook

curl -X POST https://api.nowistay.com/public/v1/webhooks \
  -H "Authorization: Bearer nis_access_token_example" \
  -H "Content-Type: application/json" \
  -d '{
    "propertyId": 101,
    "url": "https://partner.example.com/nowistay/webhooks",
    "events": ["booking.created", "booking.updated"],
    "description": "PMS sync"
  }'

Resposta:

{
  "id": 801,
  "propertyId": 101,
  "url": "https://partner.example.com/nowistay/webhooks",
  "events": ["booking.created", "booking.updated"],
  "description": "PMS sync",
  "status": "active",
  "createdAt": "2026-05-27T16:40:00Z",
  "updatedAt": "2026-05-27T16:40:00Z",
  "signingSecret": "whsec_9npwH6J8QyZp1L8U3xqG7sN2"
}

Guarde o signingSecret agora. É devolvido uma única vez, na criação. Não há forma de o voltar a ler a partir da API. Se o perder, elimine o webhook e crie um novo.

Passo 2. Receba o primeiro evento

Cada entrega é um HTTP POST para o seu URL, com estes cabeçalhos:

  • Content-Type: application/json
  • User-Agent: Nowistay-Webhooks/1.0
  • Nowistay-Webhook-Id: 801
  • Nowistay-Webhook-Event-Id: evt_LkP2sxR9zV8QyW... (único por evento)
  • Nowistay-Webhook-Event-Type: booking.updated
  • Nowistay-Webhook-Timestamp: 1779292800 (segundos Unix)
  • Nowistay-Webhook-Signature: v1=3a7b...e1c (HMAC-SHA256 em hexadecimal)

E um corpo JSON com esta estrutura:

{
  "id": "evt_LkP2sxR9zV8QyW...",
  "type": "booking.updated",
  "createdAt": "2026-05-27T16:40:00Z",
  "apiVersion": "2026-05-27",
  "propertyId": 101,
  "bookingId": 9001,
  "data": {
    "booking": {
      "id": 9001,
      "propertyId": 101,
      "arrival": "2026-07-14",
      "departure": "2026-07-18",
      "status": "confirmed",
      "channel": "airbnb",
      "reservationNumber": "HMABCDE",
      "guest": { "firstName": "Maya", "lastName": "M." },
      "amounts": { "amount": "620.00", "currency": "EUR" },
      "identity": { "status": "verified" }
    }
  }
}

O seu endpoint deve responder com qualquer estado 2xx (200 ou 204 são os habituais). Qualquer outra resposta, ou uma resposta que demore mais de 10 segundos, conta como falha e desencadeia uma repetição.

Passo 3. Verifique a assinatura

Verifique sempre a assinatura antes de confiar no corpo. Sem verificação, qualquer pessoa que adivinhe o seu URL poderia enviar eventos falsos ao seu serviço.

A Nowistay calcula a assinatura assim:

signature = "v1=" + HMAC_SHA256(
  signing_secret,
  timestamp + "." + event_id + "." + raw_request_body
).hex()

Volte a calculá-la do seu lado com os cabeçalhos Nowistay-Webhook-Timestamp e Nowistay-Webhook-Event-Id e o corpo bruto exato do pedido (não analise nem volte a serializar o JSON antes de verificar, ou arrisca diferenças de bytes).

Exemplo em Node.js

import crypto from "node:crypto";

function verify(req, signingSecret) {
  const timestamp = req.headers["nowistay-webhook-timestamp"];
  const eventId = req.headers["nowistay-webhook-event-id"];
  const signature = req.headers["nowistay-webhook-signature"];
  const rawBody = req.rawBody; // Buffer of the raw HTTP body

  const signed = Buffer.concat([
    Buffer.from(`${timestamp}.${eventId}.`, "utf8"),
    rawBody,
  ]);
  const expected = "v1=" + crypto
    .createHmac("sha256", signingSecret)
    .update(signed)
    .digest("hex");

  const a = Buffer.from(signature);
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Exemplo em Python

import hmac, hashlib

def verify(request, signing_secret: str) -> bool:
    timestamp = request.headers["Nowistay-Webhook-Timestamp"]
    event_id = request.headers["Nowistay-Webhook-Event-Id"]
    signature = request.headers["Nowistay-Webhook-Signature"]
    raw_body = request.body  # bytes

    signed = f"{timestamp}.{event_id}.".encode("utf-8") + raw_body
    expected = "v1=" + hmac.new(
        signing_secret.encode("utf-8"),
        signed,
        hashlib.sha256,
    ).hexdigest()

    return hmac.compare_digest(signature, expected)

Verifique também se o timestamp é recente (por exemplo, nos últimos 5 minutos) para se proteger de ataques de repetição (replay).

O que está (e o que não está) no conteúdo

O objeto data.booking espelha o que GET /public/v1/bookings/{id} devolve quando chamado sem o âmbito bookings.sensitive.read. Isto significa que:

  • Incluídos: datas, canal, estado, montantes, nome próprio e apelido do hóspede, adultos/crianças, parâmetros de reserva personalizados (se ativados no webhook).
  • Não incluídos: e-mail, telefone, morada, cidade, código postal, país e idioma do hóspede e o código da fechadura inteligente. Estes dados são considerados sensíveis e a Nowistay não os envia aos subscritores de webhooks.

Quando a sua ferramenta precisar de um desses campos sensíveis, vá buscar o detalhe da reserva à API com um token que tenha o âmbito bookings.sensitive.read. O bookingId do envelope do webhook é a entrada certa para essa chamada.

curl https://api.nowistay.com/public/v1/bookings/9001 \
  -H "Authorization: Bearer nis_access_token_with_sensitive_scope"

Repetições e desativação automática após 72 horas

Se o seu endpoint devolver um estado que não seja 2xx, exceder o tempo limite ou falhar por qualquer outro motivo, a Nowistay não descarta o evento. Agenda uma repetição, com recuo exponencial e um pouco de variação aleatória (jitter):

  • 1.ª repetição: cerca de 1 minuto após a falha.
  • 2.ª repetição: cerca de 2 minutos depois.
  • 3.ª repetição: cerca de 4 minutos depois, e depois 8, 16, 32 e assim sucessivamente.
  • Limite: as repetições são espaçadas no máximo por 6 horas.

Isto continua até o seu endpoint aceitar o evento, ou até o webhook estar a falhar há mais de 72 horas seguidas. Nessa altura, a Nowistay define o estado do webhook como disabled e deixa de lhe enviar novos eventos. Os contadores de falhas são repostos sempre que uma entrega tem êxito, pelo que um incidente isolado nunca ativa a desativação automática.

Os registos de entrega são conservados durante 30 dias e depois eliminados.

Reative um webhook desativado

Um webhook desativado pode ser reativado a partir da API quando o seu endpoint voltar a estar saudável. Envie um PATCH com status: "active":

curl -X PATCH https://api.nowistay.com/public/v1/webhooks/801 \
  -H "Authorization: Bearer nis_access_token_example" \
  -H "Content-Type: application/json" \
  -d '{"status": "active"}'

A Nowistay não volta a enviar os eventos que ocorreram enquanto o webhook estava desativado. Se perdeu alguns, faça um preenchimento único com GET /public/v1/bookings usando um filtro de datas e depois retome o seu fluxo baseado em webhooks.

Pausar, retomar, eliminar

Pode pausar temporariamente as entregas (durante uma janela de manutenção, por exemplo) sem perder a configuração do webhook:

# Pause
curl -X PATCH https://api.nowistay.com/public/v1/webhooks/801 \
  -H "Authorization: Bearer nis_access_token_example" \
  -H "Content-Type: application/json" \
  -d '{"status": "paused"}'

# Resume
curl -X PATCH https://api.nowistay.com/public/v1/webhooks/801 \
  -H "Authorization: Bearer nis_access_token_example" \
  -H "Content-Type: application/json" \
  -d '{"status": "active"}'

# Delete permanently
curl -X DELETE https://api.nowistay.com/public/v1/webhooks/801 \
  -H "Authorization: Bearer nis_access_token_example"

Pode também alterar o url, a lista de events ou a description na mesma chamada. Alterar o URL não muda o segredo de assinatura.

Consulte os seus webhooks

# List all webhooks (optionally filtered by property)
curl "https://api.nowistay.com/public/v1/webhooks?propertyId=101" \
  -H "Authorization: Bearer nis_access_token_example"

# Read one webhook
curl https://api.nowistay.com/public/v1/webhooks/801 \
  -H "Authorization: Bearer nis_access_token_example"

A resposta da listagem mostra os mesmos campos da resposta de criação, menos o segredo de assinatura (só é devolvido na criação).

Regras e limites do URL

  • URL HTTP ou HTTPS absoluto, com no máximo 2048 caracteres. HTTPS obrigatório em produção.
  • Sem credenciais de autenticação básica no URL (https://user:pass@... é rejeitado).
  • Sem localhost, sem intervalos de IP privados ou reservados. A Nowistay resolve também o nome do anfitrião no momento da entrega e bloqueia as chamadas que resolvam para endereços privados.
  • Sem redirecionamentos HTTP. A Nowistay não os segue, a entrega é marcada como falhada se o seu servidor redirecionar.
  • No máximo 3 webhooks por alojamento, em todas as suas aplicações.
  • Descrição: opcional, no máximo 120 caracteres.

Boas práticas

  • Verifique a assinatura sempre. É a única coisa que prova que a chamada vem mesmo da Nowistay.
  • Seja idempotente. O mesmo id de evento pode ser reenviado após uma falha. Guarde o id do corpo e ignore o evento se já o processou.
  • Responda depressa. Confirme a chamada com um 2xx de imediato e processe o evento numa tarefa em segundo plano, se for pesado.
  • Registe os cabeçalhos. Guarde o Nowistay-Webhook-Event-Id e o id do pedido nos seus registos. Acelera o suporte.
  • Trate o webhook como uma indicação, não como a fonte de verdade. Se a sua lógica de negócio precisar de campos sensíveis ou dos dados mais recentes, chame GET /public/v1/bookings/{id} depois de receber o evento.

Para onde ir a seguir

Pronto para pôr o seu alojamento em piloto automático?

Junte-se a mais de 1000 gestores de alojamentos que poupam horas todas as semanas com a comunicação com hóspedes assistida por IA.