# Webhooks > Receba eventos da HILLS Music no seu endpoint HTTPS: assinatura, os 13 tipos de evento, o envelope, o X-Hills-Signature, novas tentativas e o log. Os webhooks avisam o seu sistema quando algo acontece na HILLS Music, para que você não precise fazer polling. A HILLS envia um `POST` assinado para o seu endpoint HTTPS a cada evento que você assina. Os webhooks vêm com a API nos planos Enterprise e Custom. ## Assine eventos Crie um endpoint com `POST /webhooks` (escopo `webhooks:write`). Você também pode adicionar um no app web da HILLS, em **Settings → Webhooks**. ```bash curl -X POST "https://api.hillsmusic.com/v1/webhooks" \ -H "Authorization: Bearer $HILLS_API_KEY" \ -H "Content-Type: application/json" \ -d '{"url":"https://example.com/hills/webhooks","description":"Order system","events":["release.approved","delivery.store_status_changed"]}' ``` A resposta (`201`) traz o endpoint e o seu segredo de assinatura, `whsec_…`. **O segredo aparece uma única vez.** Guarde-o junto com os seus outros segredos. A URL precisa ser `https://` na porta 443, em um endereço público. Endereços privados, de loopback, link-local e de metadados de nuvem são recusados com `400 invalid_url`, tanto quando você salva a URL quanto em cada requisição. Você pode ter até 20 endpoints (`409 too_many` a partir daí). Todas as requisições de endpoints de webhook, com seus campos e erros, estão na [referência de Webhooks](https://developer.hillsmusic.com/pt/reference/webhooks/). `GET /webhooks/events` lista os tipos de evento com o significado de cada um. ## Eventos | Evento | Enviado quando | | --- | --- | | `release.submitted` | Um lançamento foi enviado para revisão. | | `release.approved` | Um lançamento foi aprovado e está na fila para as suas plataformas. | | `release.changes_requested` | Um lançamento foi devolvido com o que precisa ser corrigido. | | `release.live` | Um lançamento entrou no ar na primeira plataforma. | | `delivery.store_status_changed` | O status de um lançamento em uma plataforma mudou: a caminho, enviado, no ar, requer correção sua, remoção solicitada ou removido. | | `takedown.completed` | Todas as plataformas de uma solicitação de remoção aprovada removeram o lançamento. | | `ddex.import.report_ready` | Um teste DDEX terminou (a pasta `/test`, o app web ou `POST /ddex/validate`); o relatório está pronto. | | `ddex.import.imported` | Um lote DDEX entregue em produção terminou de ser importado: done, partial ou failed. | | `statement.published` | O relatório de royalties de um mês está pronto (no white label, primeiro pronto para a sua revisão). | | `payout.status_changed` | Uma solicitação de pagamento foi aprovada, enviada, paga, falhou ou foi cancelada. | | `request.status_changed` | A HILLS Music respondeu a uma solicitação: aprovada, requer alterações, concluída ou recusada. | | `chat.message` | A HILLS Music escreveu para você no Live support. | | `webhook.test` | Enviado apenas por `POST /webhooks/{id}/test`. Não é possível assiná-lo. | ## O envelope Todo evento tem o mesmo formato externo: ```json { "id": "5d0c2b7e-8a43-4c4e-9f1e-2f0b9a6c1d33", "type": "release.approved", "api_version": "v1", "occurred_at": "2026-10-13T09:05:00Z", "account_id": 12, "data": { "release": { "id": 812, "title": "Midnight Run" } } } ``` | Campo | Significado | | --- | --- | | `id` | O UUID do evento. É o mesmo em todas as tentativas: use-o para eliminar duplicatas. | | `type` | O tipo do evento. | | `api_version` | `v1`. Dentro de uma versão, campos só são adicionados. | | `occurred_at` | Quando aconteceu, em ISO 8601 e UTC. | | `account_id` | A sua conta de cliente. | | `data` | Os dados do evento. O formato depende de `type`. | Cada requisição também traz `X-Hills-Signature`, `X-Hills-Event` (o tipo), `X-Hills-Delivery` (o id da entrega, o mesmo em todas as tentativas), `X-Hills-Attempt` (1, 2, 3…), `Content-Type: application/json` e `User-Agent: HILLS-Webhooks/1.0`. Um corpo tem no máximo 256 KB. Um evento maior é enviado com `data.truncated: true` e apenas os ids. Leia o restante pela API. Não há garantia de que os eventos cheguem em ordem. Use `occurred_at` e leia o estado atual pela API quando a ordem importar. ## Verifique a assinatura `X-Hills-Signature` tem o formato `t=1792141500,v1=5257a869…`. Para verificá-la: 1. Leia `t` (segundos Unix) e `v1` no header. 2. Recuse a requisição se `t` estiver a mais de 5 minutos do seu relógio. Isso impede ataques de replay. 3. Calcule o HMAC-SHA256 de `.`, usando o seu segredo exatamente como está (incluindo `whsec_`) como chave. Codifique o resultado em hexadecimal. 4. Compare-o com `v1` em tempo constante. Use os bytes **brutos** do corpo. Um JSON interpretado e serializado de novo não vai bater. ```js import crypto from "node:crypto"; import express from "express"; const SECRET = process.env.HILLS_WEBHOOK_SECRET; // whsec_… const app = express(); app.post("/hills/webhooks", express.raw({ type: "application/json" }), (req, res) => { const header = req.get("X-Hills-Signature") ?? ""; const parts = Object.fromEntries(header.split(",").map((kv) => kv.split("=", 2))); const t = Number(parts.t); if (!Number.isInteger(t) || Math.abs(Date.now() / 1000 - t) > 300) return res.sendStatus(400); const expected = crypto.createHmac("sha256", SECRET).update(`${t}.${req.body}`).digest("hex"); const a = Buffer.from(expected, "hex"); const b = Buffer.from(parts.v1 ?? "", "hex"); if (a.length !== b.length || !crypto.timingSafeEqual(a, b)) return res.sendStatus(400); const event = JSON.parse(req.body.toString("utf8")); // Dedupe on event.id: a retry after a lost answer sends the same event again. queue.push(event); // do the work later res.sendStatus(200); // answer within 10 seconds }); app.listen(3000); ``` ## Responda rápido Qualquer `2xx` em até 10 segundos é sucesso. Qualquer outra coisa é falha, inclusive um timeout ou um `3xx` (redirecionamentos não são seguidos). Responda primeiro e faça o trabalho depois, em uma fila. ## Novas tentativas Uma entrega que falha é tentada de novo, até 8 tentativas no total. Depois de cada falha, a próxima tentativa vem **1 minuto, 5 minutos, 30 minutos, 2 horas, 12 horas, 12 horas e 12 horas** depois: cerca de um dia e meio no total. Depois da 8ª falha, a entrega é abandonada, mas você ainda pode [reenviá-la manualmente](#the-delivery-log). ## Desativação automática Um endpoint que falha 20 tentativas seguidas, sem nenhum sucesso no meio, é desativado. Você recebe uma notificação. As entregas pendentes naquele momento são abandonadas. `GET /webhooks/{id}` mostra o motivo: `enabled: false`, `disabled_at` e `disabled_reason`. Corrija o seu endpoint e reative-o: ```bash curl -X PATCH "https://api.hillsmusic.com/v1/webhooks/7" \ -H "Authorization: Bearer $HILLS_API_KEY" \ -H "Content-Type: application/json" \ -d '{"enabled":true}' ``` Isso zera `failure_count`. Depois, reenvie o que foi abandonado. ## O log de entregas `GET /webhooks/{id}/deliveries` lista o que foi enviado nos últimos 30 dias, das mais recentes para as mais antigas: o payload, as tentativas, o último status HTTP, o horário e o erro. Filtre com `status` (`pending`, `succeeded`, `failed`) e `event`. A paginação é feita com `before`. Veja [Paginação](https://developer.hillsmusic.com/pt/guides/pagination/). Para enviar uma entrega de novo, com o mesmo id de evento e o mesmo payload: ```bash curl -X POST "https://api.hillsmusic.com/v1/webhooks/7/deliveries/1042/retry" \ -H "Authorization: Bearer $HILLS_API_KEY" ``` O reenvio e o teste respondem `409 endpoint_disabled` enquanto o endpoint estiver desativado. ## Renove o segredo `POST /webhooks/{id}/roll-secret` gera um novo segredo. A resposta o traz, uma única vez. **O segredo antigo para de funcionar na hora**, então planeje a troca: 1. Faça o seu verificador aceitar uma lista de segredos: teste um de cada vez. 2. Renove o segredo e adicione o novo à lista. 3. Quando as entregas passarem com o novo segredo, remova o antigo. Renove o segredo imediatamente se ele vazar. ## Envie um evento de teste `POST /webhooks/{id}/test` coloca na fila um evento `webhook.test` para o endpoint, quaisquer que sejam os eventos que ele assina. Ele é assinado como qualquer outro evento, e o resultado aparece no log de entregas. ```bash curl -X POST "https://api.hillsmusic.com/v1/webhooks/7/test" \ -H "Authorization: Bearer $HILLS_API_KEY" ``` ```js const res = await fetch("https://api.hillsmusic.com/v1/webhooks/7/test", { method: "POST", headers: { Authorization: `Bearer ${process.env.HILLS_API_KEY}` }, }); const { delivery } = await res.json(); // 202; check GET /webhooks/7/deliveries for the outcome ``` Para testar na sua própria máquina, use um túnel que forneça uma URL HTTPS pública. Fonte: https://developer.hillsmusic.com/pt/guides/webhooks/