Endpoints de webhook
Diga ao WhatIsUp.dev para onde entregar as notificações de evento. Um cliente pode ter múltiplos endpoints (URLs diferentes para subconjuntos diferentes de eventos). Um canal pode ser a origem de eventos para múltiplos endpoints, e um endpoint pode assinar múltiplos canais — é um relacionamento muitos-para-muitos.
Modelo conceitual
Endpoints descrevem uma assinatura: para onde enviar, quais eventos enviar, e o secret de assinatura usado no HMAC. Os registros de entrega de fato estão em Entregas de webhook.
api_key → customer ──┬── channel ◄───┐
│ │
└── webhook_endpoint
│
└── webhook_delivery (per event)
Listar endpoints
Retorna todos os endpoints do cliente autenticado.
Criar um endpoint
| Field | Type | Required | Notes |
|---|---|---|---|
| url | string · URL | required | |
| events | array<`message.received` \| `message.sent` \| `message.reaction` \| `message.status` \| `channel.connected` \| `channel.disconnected` \| `qr.updated` \| `group.created` \| `group.updated` \| `group.participant_added` \| `group.participant_removed` \| `group.admin_promoted` \| `group.admin_demoted` \| `chat.updated` \| `chat.archived` \| `chat.pinned` \| `presence.updated` \| `order.placed` \| `order.cancelled` \| `cart.updated` \| `story.viewed` \| `call.offered` \| `call.terminated` \| `contact.resolved`> | required | |
| channel_id | string · uuid | optional | |
| signing_secret | string | optional | |
| enabled | boolean | optional | |
| chat_type_filter | `all` \| `direct_only` \| `groups_only` | optional |
curl -sX POST "$WHATISUP_API/v1/webhook-endpoints" \
-H "Authorization: Bearer $WHATISUP_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://api.acme.dev/whatisup/webhook","events":["message.received","message.sent"],"channel_ids":["inst_01J..."]}'A resposta inclui o secret de assinatura em texto puro — somente nesta chamada. Guarde-o; nunca mais mostramos. (Podemos rotacioná-lo; não conseguimos recuperá-lo.)
| Field | Type | Required | Notes |
|---|---|---|---|
| id | string · uuid | required | |
| customer_id | string · uuid | required | |
| channel_id | string · uuid | required · nullable | |
| url | string · URL | required | |
| events | array<`message.received` \| `message.sent` \| `message.reaction` \| `message.status` \| `channel.connected` \| `channel.disconnected` \| `qr.updated` \| `group.created` \| `group.updated` \| `group.participant_added` \| `group.participant_removed` \| `group.admin_promoted` \| `group.admin_demoted` \| `chat.updated` \| `chat.archived` \| `chat.pinned` \| `presence.updated` \| `order.placed` \| `order.cancelled` \| `cart.updated` \| `story.viewed` \| `call.offered` \| `call.terminated` \| `contact.resolved`> | required | |
| enabled | boolean | required | |
| has_custom_signing_secret | boolean | required | |
| chat_type_filter | `all` \| `direct_only` \| `groups_only` | required | |
| created_at | string · ISO 8601 | required | |
| updated_at | string · ISO 8601 | required |
A url é validada contra SSRF no momento da criação e no momento da entrega (defesa
contra DNS-rebind). IPs de loopback / link-local / RFC1918 / cloud-metadata são rejeitados com
um 400. Em produção (NODE_ENV=production), apenas https:// é aceito.
Atualizar um endpoint
| Field | Type | Required | Notes |
|---|---|---|---|
| url | string · URL | optional | |
| events | array<`message.received` \| `message.sent` \| `message.reaction` \| `message.status` \| `channel.connected` \| `channel.disconnected` \| `qr.updated` \| `group.created` \| `group.updated` \| `group.participant_added` \| `group.participant_removed` \| `group.admin_promoted` \| `group.admin_demoted` \| `chat.updated` \| `chat.archived` \| `chat.pinned` \| `presence.updated` \| `order.placed` \| `order.cancelled` \| `cart.updated` \| `story.viewed` \| `call.offered` \| `call.terminated` \| `contact.resolved`> | optional | |
| signing_secret | string \| unknown | optional | |
| enabled | boolean | optional | |
| chat_type_filter | `all` \| `direct_only` \| `groups_only` | optional |
Você pode rotacionar o secret de assinatura passando rotate_secret: true. O novo secret é retornado no corpo da resposta, exatamente uma vez. O secret antigo é invalidado imediatamente — verifique o rollout antes de virar a chave.
Excluir um endpoint
Retorna 204 No Content. Entregas em andamento que já foram enfileiradas vão falhar contra o endpoint agora excluído e esgotar o orçamento de retentativas normalmente.