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.
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.
booking.created e booking.updated.GET /public/v1/bookings/{id}, incluindo datas, canal, estado, montantes, campos relevantes para as missões e os seus parâmetros de reserva personalizados.webhooks.write. POST /public/v1/webhooks exige também bookings.read, porque o conteúdo é o detalhe completo de uma reserva.2xx.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.
Cada entrega é um HTTP POST para o seu URL, com estes cabeçalhos:
Content-Type: application/jsonUser-Agent: Nowistay-Webhooks/1.0Nowistay-Webhook-Id: 801Nowistay-Webhook-Event-Id: evt_LkP2sxR9zV8QyW... (único por evento)Nowistay-Webhook-Event-Type: booking.updatedNowistay-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.
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).
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);
}
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 objeto data.booking espelha o que GET /public/v1/bookings/{id} devolve quando chamado sem o âmbito bookings.sensitive.read. Isto significa que:
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"
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):
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.
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.
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.
# 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).
https://user:pass@... é rejeitado).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.id do corpo e ignore o evento se já o processou.2xx de imediato e processe o evento numa tarefa em segundo plano, se for pesado.Nowistay-Webhook-Event-Id e o id do pedido nos seus registos. Acelera o suporte.GET /public/v1/bookings/{id} depois de receber o evento.Guias para anfitriões: leia o blogue.
Pronto para pôr o seu alojamento em piloto automático?