---
title: "Alarmes CloudWatch por ligação sem Lambda nem Twilio"
description: "O SNS não liga para um telefone. Envie os alarmes do CloudWatch pelo EventBridge a um webhook de ligação: configuração, filtros e armadilhas."
date: 2026-10-16
author: Nooc
authorAvatarLink: /images/avatars/nooc.webp
authorLink: https://nooc.me
tags:
  - AWS
  - CloudWatch
  - EventBridge
  - alertas por ligação
  - webhooks
  - plantão
---

# Alarmes do CloudWatch por ligação: do EventBridge ao seu telefone, sem Lambda

Um alarme do CloudWatch não consegue ligar para o seu telefone. As únicas ações embutidas são SNS, Auto Scaling, EC2 e Systems Manager, e o SNS termina em e-mail, SMS ou numa fila que você precisa estar olhando. Os tutoriais que de fato fazem o telefone tocar montam uma função Lambda e uma conta Twilio. Dá para pular os dois: deixe o EventBridge receber a mudança de estado do alarme, remodelá-la num pequeno corpo JSON e enviá-la por POST a um canal de ligação do Echobell.

Este guia cobre o caminho inteiro: o canal, a regra do EventBridge, o input transformer, os filtros que mantêm o toque relevante e por que apontar o SNS direto para o webhook parece que deveria funcionar e, na maioria das vezes, não funciona.

## Por que o caminho usual pelo SNS fica complicado

O conselho padrão é "alarme → tópico SNS → assinante". Para uma ligação telefônica, isso tem três problemas:

- **O SNS não tem protocolo de voz.** Os tipos de assinante são e-mail, SMS, push móvel, SQS, Lambda, Firehose e HTTP(S). SMS é fácil de perder e é bloqueado pelo Não Perturbe; uma chamada de voz exige algo que você mesmo monte.
- **A mensagem HTTPS é uma string dentro de uma string.** O SNS entrega seu envelope com `Content-Type: text/plain`, e o alarme em si é um documento JSON serializado no campo `Message` do envelope. Um receptor de webhook que lê campos — `AlarmName`, `NewStateValue` — encontra apenas um bloco opaco.
- **O envelope é grande.** Entre a assinatura, a URL do certificado de assinatura e o link de cancelamento, o envelope adiciona bem mais de um kilobyte antes de o texto do seu alarme começar. O Echobell trunca o corpo de uma notificação em 1500 bytes, então um template "despeje o corpo inteiro" corta justamente onde a parte útil começa.

Dá para fazer o SNS funcionar com uma Lambda que desembrulha a mensagem e a encaminha. Mas o CloudWatch já publica toda mudança de estado de alarme no EventBridge como um evento JSON limpo — sem nenhuma ação para configurar — e o EventBridge consegue remodelá-lo e chamar um endpoint HTTPS por conta própria.

## O que você precisa

- Uma conta AWS e permissão para criar regras, conexões e API destinations no EventBridge
- Echobell instalado ([App Store](https://apps.apple.com/app/apple-store/id6743597198?pt=128151925&ct=blog-cloudwatch-alarm-phone-call-alerts-pt&mt=8) / [Google Play](https://play.google.com/store/apps/details?id=one.echobell.echobellandroid))
- Pelo menos um alarme do CloudWatch existente
- Dez minutos

Nada do seu lado precisa aceitar tráfego de entrada. Quem faz a requisição de saída é o EventBridge.

## Passo 1 — Crie um canal que toca

No Echobell, crie um canal e defina o tipo de notificação como **Ligação**. Um canal de ligação se comporta como uma chamada recebida em vez de um push, então atravessa o modo Foco e o Não Perturbe do iOS.

O transformer do passo 3 envia um corpo plano com os campos `alarm`, `state`, `reason`, `region` e `link`, então os templates ficam curtos:

```
Título: {{state}}: {{alarm}}
Corpo: {{reason}}
```

Configure o **template de link** nas configurações avançadas para que tocar na notificação abra o alarme no console:

```
{{link}}
```

Copie a URL do webhook na tela de detalhes do canal:

```
https://hook.echobell.one/t/<channel-token>
```

## Passo 2 — Crie a connection e a API destination

O EventBridge chama endpoints HTTPS externos por meio de uma **API destination**, que precisa de uma **connection** que carregue a autenticação. O Echobell autentica pelo token secreto na URL, então a connection é só formalidade — mas o EventBridge não cria uma destination sem ela.

1. Abra **EventBridge → Integration → API destinations → Connections → Create connection**
2. **Name**: `echobell`
3. **Authorization type**: **API key**
4. **API key name**: `x-source`, **Value**: `eventbridge`
5. Crie-a e depois vá em **API destinations → Create API destination**
6. **Name**: `echobell-calling`
7. **API destination endpoint**: a URL do seu canal Echobell do passo 1
8. **HTTP method**: `POST`
9. **Invocation rate limit**: `1` por segundo é mais que suficiente e funciona como válvula de segurança
10. **Connection**: `echobell`

O cabeçalho da API key é enviado em toda requisição. Você pode verificá-lo numa condição de canal do Echobell se quiser uma segunda barreira antes do token:

```
header["x-source"] == "eventbridge"
```

## Passo 3 — Crie a regra e o input transformer

Vá em **EventBridge → Rules → Create rule** na mesma região dos seus alarmes. Regras são regionais: um alarme em `eu-west-1` é invisível para uma regra em `us-east-1`.

Use o event bus **default** e uma regra de **event pattern**. Este padrão casa com todo alarme que passa para `ALARM`:

```json
{
  "source": ["aws.cloudwatch"],
  "detail-type": ["CloudWatch Alarm State Change"],
  "detail": {
    "state": { "value": ["ALARM"] }
  }
}
```

Como alvo, escolha **EventBridge API destination** e selecione `echobell-calling`. Deixe o console criar uma nova execution role — ela concede `events:InvokeApiDestination` para esta destination e mais nada.

Depois expanda **Additional settings → Configure target input → Input transformer**:

**Input path**

```json
{
  "alarm": "$.detail.alarmName",
  "state": "$.detail.state.value",
  "reason": "$.detail.state.reason",
  "region": "$.region",
  "time": "$.time"
}
```

**Template**

```json
{
  "alarm": "<alarm>",
  "state": "<state>",
  "reason": "<reason>",
  "region": "<region>",
  "time": "<time>",
  "link": "https://console.aws.amazon.com/cloudwatch/home?region=<region>#alarmsV2:alarm/<alarm>"
}
```

O EventBridge envia o resultado como `application/json`, e é isso que permite ao Echobell ler `{{alarm}}` e `{{state}}` diretamente.

## Passo 4 — Saiba como o evento realmente se parece

O input path lê do evento que o CloudWatch publica. Resumido, ele é assim:

```json
{
  "version": "0",
  "id": "c4c1c1c9-6542-e61b-6ef0-8c4d36933a92",
  "detail-type": "CloudWatch Alarm State Change",
  "source": "aws.cloudwatch",
  "account": "123456789012",
  "time": "2026-10-16T03:12:44Z",
  "region": "us-east-1",
  "resources": ["arn:aws:cloudwatch:us-east-1:123456789012:alarm:prod-api-5xx"],
  "detail": {
    "alarmName": "prod-api-5xx",
    "state": {
      "value": "ALARM",
      "reason": "Threshold Crossed: 1 out of the last 1 datapoints [42.0] was greater than the threshold (10.0).",
      "timestamp": "2026-10-16T03:12:44.000+0000"
    },
    "previousState": { "value": "OK" },
    "configuration": { "description": "API 5xx errors on the prod load balancer" }
  }
}
```

O que vale saber antes de editar um template:

- **O alarme fica sob `detail`.** `$.alarmName` não casa com nada; `$.detail.alarmName` casa.
- **`state.reason` é texto gerado.** Ele cita o datapoint e o limiar, que é exatamente o que você quer ouvir às 3 da manhã.
- **Nomes de alarme são inseridos crus.** Uma aspa dupla dentro do nome ou do reason de um alarme quebraria o JSON que o transformer produz. Use apenas letras, dígitos, hifens e underscores nos nomes de alarme — o que já é boa prática — ou a ligação falhará com um 400.
- **O link precisa de um nome simples.** Um nome com espaços ou `#` não vai levar ao lugar certo; a busca do console ainda o encontra.

## Passo 5 — Estreite até o que merece uma ligação

Um canal que toca a cada `ALARM` é a forma mais rápida de aprender a ignorá-lo. Filtre em dois lugares.

**No padrão do EventBridge**, que roda antes de qualquer coisa ser enviada:

| Objetivo | Mudança no padrão |
| --- | --- |
| Só alarmes de produção | `"alarmName": [{ "prefix": "prod-" }]` dentro de `detail` |
| Ignorar o que não é urgente | Nomeie-os `warn-…` e use `"alarmName": [{ "anything-but": { "prefix": "warn-" } }]` |
| Só alarmes específicos | `"alarmName": ["prod-api-5xx", "prod-db-cpu"]` |
| Só alarmes de verdade, não estados de teste | Mantenha `"state": { "value": ["ALARM"] }` — um evento `INSUFFICIENT_DATA` nunca casa |
| Só uma conta ou região | Adicione `"account": ["123456789012"]` ou `"region": ["us-east-1"]` no nível superior |

**No Echobell**, use uma condição de canal para o que o padrão não consegue expressar, como uma janela de horário:

```
state == "ALARM" && (hour >= 17 || hour < 9)
```

As variáveis de hora do Echobell são em UTC, então converta do seu fuso primeiro. A [referência de condições](/pt/docs/conditions) lista todas as variáveis.

## Passo 6 — Dê às recuperações uma porta mais silenciosa

Uma segunda regra com `"value": ["OK"]` pode avisar que o problema passou, sem tocar. Crie um segundo canal Echobell do tipo **Urgente (Time Sensitive)** (ou normal), uma segunda API destination apontando para ele e reutilize o mesmo input transformer. O template de título `{{state}}: {{alarm}}` já se lê como "OK: prod-api-5xx".

| Regra | Estado | Tipo de canal | Comportamento |
| --- | --- | --- | --- |
| `alarms-to-call` | `ALARM`, prefixo `prod-` | Ligação | Toca atravessando o Foco |
| `alarms-warn` | `ALARM`, prefixo `warn-` | Urgente | Tela bloqueada, sem tocar |
| `alarms-recovered` | `OK` | Normal | Push comum |

## Teste antes de confiar

Não espere a produção quebrar. Force um alarme para o estado que você quer:

```bash
aws cloudwatch set-alarm-state \
  --alarm-name prod-api-5xx \
  --state-value ALARM \
  --state-reason "Manual test of the Echobell path"
```

O alarme volta ao estado real na próxima avaliação, mas o evento de mudança de estado é real e passa pela sua regra. Para testar apenas o lado do Echobell, dispare o canal diretamente:

```bash
curl -X POST https://hook.echobell.one/t/<channel-token> \
  -H 'Content-Type: application/json' \
  -d '{"alarm":"prod-api-5xx","state":"ALARM","reason":"Manual test","region":"us-east-1","link":"https://console.aws.amazon.com/cloudwatch/"}'
```

Se o `curl` toca e o teste do alarme não, o problema está na regra, no transformer ou na destination — não no canal.

## E o SNS com e-mail para ligação?

Se você já tem um tópico SNS em todo lugar e não quer mexer no EventBridge, existem outros dois caminhos:

- **SNS → Lambda → webhook.** Poucas linhas de código leem `Message` e depois enviam um corpo plano para a mesma URL do Echobell. Funciona, mas agora você é dono de uma função, de uma role e dos logs dela.
- **SNS → e-mail → gatilho de e-mail do Echobell.** Assine o [endereço do gatilho de e-mail](/pt/docs/email-trigger) do canal no tópico. O alarme chega como e-mail, e `{{subject}}` e `{{text}}` nos seus templates viram a notificação. É a configuração mais enxuta de todas, com a ressalva de que as assinaturas de e-mail do SNS exigem um clique de confirmação único e o texto não é remodelado.

Assinar a URL do webhook do Echobell diretamente no tópico como endpoint HTTPS é o caminho a evitar: a requisição de confirmação chega como notificação, e todo alarme depois disso é o envelope longo descrito acima.

## Limites que importam durante um incidente

- **Corpo de 1 MiB.** O Echobell rejeita corpos de disparo acima de 1 MiB com HTTP 413. Um alarme transformado tem algumas centenas de bytes, então você não vai chegar nisso aqui.
- **120 requisições por minuto por token.** Acima disso, o gatilho responde `429` com `RATE_LIMIT_EXCEEDED` e um `Retry-After`. O limite de 1 por segundo da destination te mantém abaixo, e o EventBridge refaz o envio de eventos que não conseguiu entregar.
- **1500 bytes de corpo da notificação.** Renderizações mais longas são truncadas. `state.reason` é uma ou duas frases e cabe.

## O que esta configuração não oferece

- **Sem confirmação.** Atender a ligação não diz nada ao CloudWatch e não silencia os outros assinantes.
- **Sem escalonamento nem escala.** Todos os inscritos no canal são chamados; ninguém é chamado depois.
- **Sem deduplicação além do alarme.** Um alarme instável que vai de `OK → ALARM` a cada poucos minutos vai tocar toda vez. Resolva com períodos de avaliação mais longos ou `DatapointsToAlarm` no próprio alarme.
- **Sem sincronização bidirecional.** Corrigir a causa não limpa nada no seu telefone.

Se isso for impeditivo, você quer uma plataforma de plantão completa. Se o que você precisa é "me ligue quando o alarme de produção disparar", são algumas telas do console.

## Solução de problemas

**O alarme disparou mas nada chegou.** Abra a aba **Monitoring** da regra: `Invocations` mostra se o padrão casou, `FailedInvocations` se a entrega falhou. Regras numa região diferente da do alarme nunca casam.

**A regra casa mas a ligação falha com um 400.** O transformer produziu JSON inválido. Procure uma aspa ou barra invertida no nome ou no reason do alarme.

**A notificação está em branco.** O template está lendo um campo que o transformer não enviou. O corpo é plano: `{{alarm}}`, não `{{detail.alarmName}}`.

**Toca repetidamente por um alarme.** O alarme está instável. Aumente o período de avaliação ou exija mais datapoints; isso é uma configuração do CloudWatch, não do Echobell.

**Toca por um alarme que você não esperava.** Verifique no padrão se falta um filtro `prefix` e lembre que alarmes compostos e alarmes criados por outras ferramentas (políticas do Auto Scaling, por exemplo) também emitem mudanças de estado.

## Perguntas frequentes

### O CloudWatch consegue ligar para um número de telefone nativamente?

Não. As ações de alarme do CloudWatch são notificações SNS, Auto Scaling, ações do EC2 e Systems Manager. Uma chamada de voz exige um serviço externo: uma Lambda que disca por meio de um provedor, uma plataforma de plantão ou um webhook de ligação como o Echobell.

### Preciso de um tópico SNS?

Não. O CloudWatch emite eventos de mudança de estado de alarme para o EventBridge por conta própria, tenha o alarme ações ou não. Você pode deixar a lista de ações do alarme vazia.

### Um alarme do CloudWatch atravessa o Não Perturbe?

Só se chegar como alerta em formato de ligação. Um canal do Echobell do tipo ligação se comporta como uma chamada recebida, e isso o modo Foco e o Não Perturbe do iOS deixam passar. Um SMS ou um push comum, não.

### Por que não simplesmente assinar a URL do webhook no SNS?

O SNS envia `text/plain` com o alarme serializado dentro de uma string `Message`, então o template não consegue ler campos individuais, e o envelope é longo o bastante para ser truncado. O SNS também envia primeiro uma confirmação de assinatura, que chegaria como notificação.

### Isso tem custo na AWS?

Eventos de mudança de estado de alarme chegam ao event bus default sem cobrança; as invocações de API destination são cobradas por requisição. No volume de um conjunto saudável de alarmes, o valor é desprezível, mas confira os preços atuais do EventBridge para a sua região.

### Posso ligar para pessoas diferentes para alarmes diferentes?

Pode. Crie um canal por público, uma regra por canal e deixe cada pessoa assinar com o tipo de notificação que quiser. Como não há confirmação, todos que assinaram com "ligação" são chamados.

### E alarmes em várias regiões ou contas?

Regras são por região, então crie a regra em cada região que você usa (a mesma definição de API destination se repete por região). Para várias contas, envie os eventos a um bus central em uma conta e coloque a regra lá.

## Fechando

O caminho tem cinco partes: um canal de ligação, uma connection e uma API destination apontando para ele, uma regra que casa apenas com os alarmes pelos quais vale acordar alguém, um input transformer que achata o evento e um teste forçado com `set-alarm-state` antes de confiar em qualquer coisa. Todo o resto aqui é sobre mantê-lo estreito o bastante para que, daqui a um mês, o toque ainda signifique alguma coisa.

[Baixe o Echobell para iPhone](https://apps.apple.com/app/apple-store/id6743597198?pt=128151925&ct=blog-cloudwatch-alarm-phone-call-alerts-pt&mt=8) ou [pegue no Google Play](https://play.google.com/store/apps/details?id=one.echobell.echobellandroid) e depois rode o teste com `set-alarm-state`.

## Artigos relacionados

- [Referência de condições de canal](/pt/docs/conditions)
- [Guia de integração com webhooks](/pt/docs/webhook)
- [Guia do gatilho de e-mail](/pt/docs/email-trigger)
- [Alertas do Sentry por ligação](/pt/blog/sentry-phone-call-alerts)
- [Alertas por ligação para indisponibilidade de API](/pt/blog/phone-call-alerts-api-downtime)
- [Guia do desenvolvedor para a fadiga de alertas](/pt/blog/fix-alert-fatigue-developer-guide)
- [Furando o modo Foco do iOS para alertas críticos](/pt/blog/how-to-bypass-ios-focus-mode-for-critical-alerts)
