# Eventos de webhook São 13 eventos, mais o `ping` de verificação. Esta página diz **quando** cada um dispara e mostra o **payload** de exemplo. Como cadastrar o endereço, responder e tratar reentregas está em [Webhooks](https://developers.festero.com.br/webhooks.md); como conferir a assinatura, em [Verificar a assinatura](https://developers.festero.com.br/webhooks/assinatura.md). ## O envelope Todo evento chega como um JSON com o mesmo envelope. O que muda é o conteúdo de `data`. | Campo | Tipo | Descrição | |---|---|---| | `event_id` | GUID | Identificador estável do evento. É o mesmo valor do cabeçalho `Festero-Event-Id` e a chave para [deduplicar](https://developers.festero.com.br/webhooks.md#idempotencia-e-ordem). | | `type` | texto | O tipo do evento, por exemplo `quote.accepted`. Igual ao cabeçalho `Festero-Event`. | | `occurred_at` | instante | Quando aconteceu, em **UTC** (ISO 8601, termina em `Z`). | | `workspace_id` | GUID | Identificador da sua empresa. | | `api_version` | texto | Versão da API. Hoje é `v1`. | | `resource_url` | texto | Endereço do recurso na API. Busque o estado atual por ele, com a sua credencial. | | `data` | objeto | Dados resumidos do evento. | O `data` é enxuto de propósito: traz identificadores, situação e datas. Nunca traz custo, margem, contato de pessoas nem textos livres. Para o resto, use `resource_url`. Os nomes de campo do webhook usam `snake_case` (`event_id`, `occurred_at`), diferente da API, que usa `camelCase`. > **Dica**: O estado pode ter andado > > Quando você busca o `resource_url`, recebe o estado **de agora**, que pode ser mais novo do que o do evento. Isso é o esperado: processe sempre o estado atual. Um recurso excluído responde `404`. ## Escopos e assinatura Cada evento pertence a um conjunto de dados, e você só pode assinar os eventos dos conjuntos que **você mesmo enxerga** no Festero. Para buscar o recurso do aviso, a sua credencial precisa do escopo correspondente. | Conjunto | Escopo | Eventos | |---|---|---| | Eventos | `events:read` | `event.created`, `event.status_changed`, `event.cancelled` | | Orçamentos | `quotes:read` | `quote.sent`, `quote.accepted`, `quote.rejected` | | Visitas | `visits:read` | `visit.scheduled`, `visit.confirmed`, `visit.cancelled` | | CRM | `crm:read` | `opportunity.created`, `opportunity.stage_changed` | | Financeiro | `finance:read` | `receivable.paid`, `receivable.overdue` | ## Eventos ### `event.created` **Quando dispara:** um evento é criado: pelo cadastro de evento no Festero, pela importação de eventos ou quando um orçamento aprovado gera o evento (inclusive quando o cliente aceita a proposta pelo portal). Um evento cadastrado já como realizado dispara também um `event.status_changed`. | Campo de `data` | Descrição | |---|---| | `id` | Identificador do evento. | | `status` | Situação do evento: `draft`, `upcoming`, `confirmed`, `inprogress`, `completed` ou `cancelled`. | | `event_date` | Data do evento (`yyyy-MM-dd`). | ```json { "event_id": "5a2f9c40-8e17-4b63-a0d5-3c7e1b9f6d28", "type": "event.created", "occurred_at": "2026-10-21T14:32:08.4519833Z", "workspace_id": "0f8fad5b-d9cb-469f-a165-70867728950e", "api_version": "v1", "resource_url": "https://platform.festero.com.br/v1/events/b7e2c4a1-95d3-4f08-a6b2-1c8d3e5f7a90", "data": { "id": "b7e2c4a1-95d3-4f08-a6b2-1c8d3e5f7a90", "status": "draft", "event_date": "2027-03-20" } } ``` ### `event.status_changed` **Quando dispara:** a situação do evento muda. Se a situação nova é igual à antiga, nada é enviado. O campo `source` diz o que provocou a mudança. | Campo de `data` | Descrição | |---|---| | `id` | Identificador do evento. | | `from` | Situação anterior. | | `to` | Situação nova. | | `source` | Origem da mudança (tabela abaixo). | | `event_date` | Data do evento (`yyyy-MM-dd`). | | `source` | O que significa | |---|---| | `status_change` | A situação foi trocada diretamente no Festero. | | `contract_sent` | O envio do contrato moveu a situação. | | `contract_signed` | A assinatura do contrato levou o evento a `upcoming` (Agendado). | | `contract_cancelled` | O cancelamento do contrato moveu a situação. | | `manual_contract_upload` | O envio manual de um contrato (assinado ou enviado) moveu a situação. | | `already_happened` | O evento foi cadastrado já como realizado. | | `import` | A importação de eventos criou o evento já numa situação. | Quando a situação nova é `cancelled`, o Festero envia também um [`event.cancelled`](#event-cancelled). ```json { "event_id": "9d4b7e12-0a63-4c85-b1f9-6e2a8c3d5f07", "type": "event.status_changed", "occurred_at": "2026-10-21T14:32:08.4519833Z", "workspace_id": "0f8fad5b-d9cb-469f-a165-70867728950e", "api_version": "v1", "resource_url": "https://platform.festero.com.br/v1/events/b7e2c4a1-95d3-4f08-a6b2-1c8d3e5f7a90", "data": { "id": "b7e2c4a1-95d3-4f08-a6b2-1c8d3e5f7a90", "from": "draft", "to": "upcoming", "source": "contract_signed", "event_date": "2027-03-20" } } ``` ### `event.cancelled` **Quando dispara:** um evento é cancelado ou excluído. O campo `reason` diz qual dos dois: `cancelled` quando a situação passa a cancelado (junto com um `event.status_changed`) e `deleted` quando o evento é excluído (inclusive ao desfazer uma importação). Um evento excluído deixa de existir na API: o `resource_url` responde `404`. | Campo de `data` | Descrição | |---|---| | `id` | Identificador do evento. | | `reason` | `cancelled` ou `deleted`. | | `event_date` | Data do evento (`yyyy-MM-dd`). | ```json { "event_id": "e1c8a5f3-7b20-4d96-8a4e-0f3b6d9c2a15", "type": "event.cancelled", "occurred_at": "2026-10-21T14:32:08.4519833Z", "workspace_id": "0f8fad5b-d9cb-469f-a165-70867728950e", "api_version": "v1", "resource_url": "https://platform.festero.com.br/v1/events/b7e2c4a1-95d3-4f08-a6b2-1c8d3e5f7a90", "data": { "id": "b7e2c4a1-95d3-4f08-a6b2-1c8d3e5f7a90", "reason": "cancelled", "event_date": "2027-03-20" } } ``` ### `quote.sent` **Quando dispara:** um orçamento é enviado ao cliente. Também dispara quando a validade do orçamento é renovada; nesse caso `reason` vem como `renewed`. | Campo de `data` | Descrição | |---|---| | `id` | Identificador do orçamento. | | `status` | Situação do orçamento (`sent`). | | `reason` | `renewed` na renovação da validade; `null` no envio normal. | | `valid_until` | Data até a qual o orçamento vale (`yyyy-MM-dd`). | ```json { "event_id": "3b6e0d92-5c14-4a78-9e2f-8d1a7c4b0e63", "type": "quote.sent", "occurred_at": "2026-10-21T14:32:08.4519833Z", "workspace_id": "0f8fad5b-d9cb-469f-a165-70867728950e", "api_version": "v1", "resource_url": "https://platform.festero.com.br/v1/quotes/e4f6a9b2-3c71-4d85-9b0e-7a1c5d8f2634", "data": { "id": "e4f6a9b2-3c71-4d85-9b0e-7a1c5d8f2634", "status": "sent", "reason": null, "valid_until": "2026-11-15" } } ``` ### `quote.accepted` **Quando dispara:** um orçamento é aceito (situação `approved`), seja pelo cliente, na resposta ao orçamento, seja pela empresa, ao aprovar no Festero. Quando o aceite gera um evento, `event_id` traz o identificador dele. | Campo de `data` | Descrição | |---|---| | `id` | Identificador do orçamento. | | `status` | Situação do orçamento (`approved`). | | `event_id` | Evento criado a partir do orçamento, ou `null` se nenhum foi criado. | ```json { "event_id": "c0a8e5b2-3f41-4d7a-9b16-2e8d5a7f1c04", "type": "quote.accepted", "occurred_at": "2026-10-21T14:32:08.4519833Z", "workspace_id": "0f8fad5b-d9cb-469f-a165-70867728950e", "api_version": "v1", "resource_url": "https://platform.festero.com.br/v1/quotes/e4f6a9b2-3c71-4d85-9b0e-7a1c5d8f2634", "data": { "id": "e4f6a9b2-3c71-4d85-9b0e-7a1c5d8f2634", "status": "approved", "event_id": "b7e2c4a1-95d3-4f08-a6b2-1c8d3e5f7a90" } } ``` ### `quote.rejected` **Quando dispara:** um orçamento é recusado (situação `rejected`), pelo cliente ou pela empresa. | Campo de `data` | Descrição | |---|---| | `id` | Identificador do orçamento. | | `status` | Situação do orçamento (`rejected`). | ```json { "event_id": "7f1d3a85-2e60-4b94-a8c7-5d0e9b3f1a42", "type": "quote.rejected", "occurred_at": "2026-10-21T14:32:08.4519833Z", "workspace_id": "0f8fad5b-d9cb-469f-a165-70867728950e", "api_version": "v1", "resource_url": "https://platform.festero.com.br/v1/quotes/e4f6a9b2-3c71-4d85-9b0e-7a1c5d8f2634", "data": { "id": "e4f6a9b2-3c71-4d85-9b0e-7a1c5d8f2634", "status": "rejected" } } ``` ### `visit.scheduled` **Quando dispara:** uma visita é agendada ou reagendada. No reagendamento, `rescheduled` vem `true`. | Campo de `data` | Descrição | |---|---| | `id` | Identificador da visita. | | `status` | Situação da visita. | | `scheduled_start` | Início agendado, em **UTC** (`2026-10-24T13:00:00Z`). | | `rescheduled` | `true` se é um reagendamento. | ```json { "event_id": "a4e7c1d9-6b30-4f52-8d0a-1c9e5b3f7a68", "type": "visit.scheduled", "occurred_at": "2026-10-21T14:32:08.4519833Z", "workspace_id": "0f8fad5b-d9cb-469f-a165-70867728950e", "api_version": "v1", "resource_url": "https://platform.festero.com.br/v1/visits/4e9a1c68-d205-4b73-8f1e-b6c0a3d95e27", "data": { "id": "4e9a1c68-d205-4b73-8f1e-b6c0a3d95e27", "status": "scheduled", "scheduled_start": "2026-10-24T13:00:00Z", "rescheduled": false } } ``` ### `visit.confirmed` **Quando dispara:** a empresa confirma um pedido de visita. | Campo de `data` | Descrição | |---|---| | `id` | Identificador da visita. | | `status` | Situação da visita. | | `scheduled_start` | Início agendado, em **UTC**. | ```json { "event_id": "2c9f6b04-d718-4e35-b0a1-7e4d8a2c5f91", "type": "visit.confirmed", "occurred_at": "2026-10-21T14:32:08.4519833Z", "workspace_id": "0f8fad5b-d9cb-469f-a165-70867728950e", "api_version": "v1", "resource_url": "https://platform.festero.com.br/v1/visits/4e9a1c68-d205-4b73-8f1e-b6c0a3d95e27", "data": { "id": "4e9a1c68-d205-4b73-8f1e-b6c0a3d95e27", "status": "scheduled", "scheduled_start": "2026-10-24T13:00:00Z" } } ``` ### `visit.cancelled` **Quando dispara:** uma visita é cancelada ou um pedido de visita é recusado. O campo `reason` diz qual dos dois: `cancelled` quando a visita é cancelada (pela empresa, ou pelo cliente a partir do link da visita) e `declined` quando a empresa recusa o pedido. | Campo de `data` | Descrição | |---|---| | `id` | Identificador da visita. | | `status` | Situação da visita. | | `reason` | `cancelled` ou `declined`. | ```json { "event_id": "f6a30d7b-9c25-4e18-a4b2-0d8e1c5a7f39", "type": "visit.cancelled", "occurred_at": "2026-10-21T14:32:08.4519833Z", "workspace_id": "0f8fad5b-d9cb-469f-a165-70867728950e", "api_version": "v1", "resource_url": "https://platform.festero.com.br/v1/visits/4e9a1c68-d205-4b73-8f1e-b6c0a3d95e27", "data": { "id": "4e9a1c68-d205-4b73-8f1e-b6c0a3d95e27", "status": "cancelled", "reason": "cancelled" } } ``` ### `opportunity.created` **Quando dispara:** uma oportunidade de venda nasce no CRM: por cadastro no Festero, por uma mensagem recebida nos canais de atendimento conectados, por um pedido de orçamento feito pela vitrine ou por um agendamento de visita pela página pública. | Campo de `data` | Descrição | |---|---| | `id` | Identificador da oportunidade. | | `status` | Situação da oportunidade (`open` ao nascer). | | `stage_id` | Identificador da etapa em que ela entrou. | ```json { "event_id": "1d8c4f60-a3e9-4b27-9f05-6c2b7e0d3a84", "type": "opportunity.created", "occurred_at": "2026-10-21T14:32:08.4519833Z", "workspace_id": "0f8fad5b-d9cb-469f-a165-70867728950e", "api_version": "v1", "resource_url": "https://platform.festero.com.br/v1/crm/opportunities/8d3f6b1e-52a4-4c7e-9a18-0b6c2f4d7e31", "data": { "id": "8d3f6b1e-52a4-4c7e-9a18-0b6c2f4d7e31", "status": "open", "stage_id": "6b0d2f84-1c93-4e57-a8d6-3f7a9c5e1b20" } } ``` ### `opportunity.stage_changed` **Quando dispara:** uma oportunidade muda de etapa do funil: ao movê-la no quadro, ao fechá-la como ganha ou perdida, ao reabri-la e quando as automações do funil a movem. Se a etapa de origem e a de destino são a mesma, nada é enviado. | Campo de `data` | Descrição | |---|---| | `id` | Identificador da oportunidade. | | `from_stage_id` | Etapa de origem. Pode ser `null`. | | `to_stage_id` | Etapa de destino. | | `status` | Situação da oportunidade depois da mudança. | Os identificadores de etapa são os de [`GET /v1/crm/pipeline-stages`](https://developers.festero.com.br/api/crm.md#etapas-do-funil). ```json { "event_id": "8e2b5a73-c0d4-4f19-b6a8-3d9f1c7e0a52", "type": "opportunity.stage_changed", "occurred_at": "2026-10-21T14:32:08.4519833Z", "workspace_id": "0f8fad5b-d9cb-469f-a165-70867728950e", "api_version": "v1", "resource_url": "https://platform.festero.com.br/v1/crm/opportunities/8d3f6b1e-52a4-4c7e-9a18-0b6c2f4d7e31", "data": { "id": "8d3f6b1e-52a4-4c7e-9a18-0b6c2f4d7e31", "from_stage_id": "6b0d2f84-1c93-4e57-a8d6-3f7a9c5e1b20", "to_stage_id": "c41e9a77-6d02-4b58-8f3a-91d5e7b20a64", "status": "open" } } ``` ### `receivable.paid` **Quando dispara:** o Festero registra um pagamento, total ou parcial, em um recebível. `partial` diz qual dos dois: `false` quando o recebível ficou pago e `true` quando ficou parcialmente pago. | Campo de `data` | Descrição | |---|---| | `id` | Identificador do recebível. | | `status` | Situação do recebível depois do pagamento (`paid` ou `partiallypaid`). | | `partial` | `true` se o pagamento foi parcial. | | `event_id` | Evento a que o recebível pertence, ou `null`. | | `due_date` | Vencimento (`yyyy-MM-dd`). | ```json { "event_id": "b9d0e6a1-4f72-4c38-8e15-2a7c3d5f0b96", "type": "receivable.paid", "occurred_at": "2026-10-21T14:32:08.4519833Z", "workspace_id": "0f8fad5b-d9cb-469f-a165-70867728950e", "api_version": "v1", "resource_url": "https://platform.festero.com.br/v1/finance/receivables/0f7c3a95-e1d8-4b26-9a40-5c2e8b6d1f73", "data": { "id": "0f7c3a95-e1d8-4b26-9a40-5c2e8b6d1f73", "status": "partiallypaid", "partial": true, "event_id": "b7e2c4a1-95d3-4f08-a6b2-1c8d3e5f7a90", "due_date": "2026-11-10" } } ``` ### `receivable.overdue` **Quando dispara:** um recebível passa a vencido. Isso acontece quando a rotina diária do Festero marca como vencidos os recebíveis com vencimento anterior a hoje e quando uma edição ou reagendamento deixa o recebível vencido. O aviso sai uma vez, na passagem; um recebível que já estava vencido não dispara de novo. | Campo de `data` | Descrição | |---|---| | `id` | Identificador do recebível. | | `status` | Situação do recebível (`overdue`). | | `event_id` | Evento a que o recebível pertence, ou `null`. | | `due_date` | Vencimento (`yyyy-MM-dd`). | ```json { "event_id": "4a7d1c95-e320-4b68-a0f4-9c6e2b8d5f13", "type": "receivable.overdue", "occurred_at": "2026-10-21T14:32:08.4519833Z", "workspace_id": "0f8fad5b-d9cb-469f-a165-70867728950e", "api_version": "v1", "resource_url": "https://platform.festero.com.br/v1/finance/receivables/0f7c3a95-e1d8-4b26-9a40-5c2e8b6d1f73", "data": { "id": "0f7c3a95-e1d8-4b26-9a40-5c2e8b6d1f73", "status": "overdue", "event_id": "b7e2c4a1-95d3-4f08-a6b2-1c8d3e5f7a90", "due_date": "2026-10-10" } } ``` ## O evento `ping` O `ping` não se assina: ele é o teste que o Festero envia ao cadastrar o endereço, ao trocar a URL e quando você clica em **Enviar teste**. O endereço só passa a receber os avisos reais depois de responder 2xx a um `ping`. O `resource_url` do `ping` é `GET /v1/me`. | Campo de `data` | Descrição | |---|---| | `endpoint_id` | Identificador do endereço de webhook cadastrado. | ```json { "event_id": "d2f8b4e6-1a70-4c93-8b05-7e3a9c1d6f28", "type": "ping", "occurred_at": "2026-10-21T14:32:08.4519833Z", "workspace_id": "0f8fad5b-d9cb-469f-a165-70867728950e", "api_version": "v1", "resource_url": "https://platform.festero.com.br/v1/me", "data": { "endpoint_id": "6c3e9a15-b8d2-4f70-a1e4-5d0b7c2f8a39" } } ``` ## Dicas para tratar os eventos - **Decida pelo `type`** (ou pelo cabeçalho `Festero-Event`) e ignore o que não reconhece. Você só recebe os tipos que assinou. - **Use `data` para rotear e `resource_url` para ler.** O payload serve para saber o que aconteceu e onde; o detalhe vem da API. - **Não dependa da ordem.** Dois avisos sobre o mesmo recurso podem chegar fora de ordem. Compare `occurred_at` ou, melhor, busque o estado atual. - **Deduplique pelo `event_id`.** O mesmo aviso pode chegar mais de uma vez.