왓츠앱에 AI 에이전트 연결하는 최신 방법
이 글은 밤늦게 쓰는 기록이다. 오늘_TRACE를 다 정리하고 이제서야 에이전트 연동 시리즈를 이어간다. 인스타·페이스북 글에 이어서 왓츠앱을 묻는 사람이 있어서 확인해 봤다. (운영자 환경 확인 기준)
1. 왓츠앱은 경로가 둘이다
먼저 알아야 할 게 있다. 왓츠앱 연동은 방법이 하나로 정해져 있지 않다. 성격이 완전히 다른 두 갈래가 있다.
| 공식 Cloud API | Baileys 브리지 (QR) | |
|---|---|---|
| 제공 | Meta 공식 | 비공식 서드파티 라이브러리 |
| 인증 | Meta 비즈니스 계정 + WABA | QR 코드로 본인 번호 연동 |
| 계정 정지 위험 | 없음 | 있음 |
| 개인 번호 사용 | 불가 | 가능 |
| 요금 | 메타 메시지별 과금 | 없음 (대신 위험) |
| 템플릿 승인 | 필요 | 불필요 |
| 난이도 | 절차 많음 | 5분 |
Hermes 기준으로 두 명령이 나뉜다.
hermes whatsapp # Baileys 브리지, 개인 프로젝트·데모용
hermes whatsapp-cloud # Meta 공식 Cloud API, 실제 서비스용
주의점 1: 개인 번호로 브리지를 돌리면 계정이 정지될 수 있다
QR로 연동하는 방식은 폰을 '링크된 기기'로 등록하는 것이다. WhatsApp Web과 원리가 같다. 근데 WhatsApp 공식 정책상 자동화와 대량 발송을 위한 비공식 클라이언트는 계정 제재 대상이다. 멀티디바이스를 정상 쓰든 API를 몰래 붙이든 로그 분석 결과로 잡힌다. 데모로 잠깐 쓰고 끌 거면 괜찮지만, 24시간 무중단으로 돌릴 계획이면官方 Cloud API로 가야 한다.
2. 공식 Cloud API 설정 순서
Meta 문서 기준으로 2026년 6월 갱신된 절차다.
- Meta for Developers 접속
- Meta Business Portfolio 생성 → 앱 생성 → WhatsApp 제품 추가
Start using the API버튼 → API Setup 페이지 진입- WhatsApp Business Account(WABA) 연결 또는 생성 → WABA ID 메모
- Phone number 등록 → Phone Number ID 메모 (이건 전화번호가 아니라 내부 ID다)
- System User access token 발급 (
whatsapp_business_messaging권한)
Hermes에는 이 과정을 순서대로 물어보는 위저드가 있다.
hermes whatsapp-cloud
위저드는 붙여넣을 때마다 검증해서 알려준다. 까다롭지 않은 부분이다.
주의점 2: Phone Number ID 자리에 전화번호를 넣는 게 1순위 실수
API가 전부 404를 뱉는다. 원인은 숫자가 닮아서다. Phone Number ID는 15~16자리 숫자고, 전화번호는 국가번호 포함 11~13자리다. 둘 다 숫자라 구분 없이 넣으면 서버는 조용히 없는 번호로 취급한다. 위저드가 붙여넣는 즉시 잡아주는 이유가 이것이다.
3. 웹훅 노출
Cloud API는 Meta가 우리 서버로 HTTPS POST를 보내므로 게이트웨이가 외부에서 닿아야 한다. Cloudflare Tunnel이 권장 방식이다.
winget install Cloudflare.cloudflared # Windows
brew install cloudflared # macOS
cloudflared tunnel --url http://localhost:8000
tunneling 주소를 Meta의 Webhooks 설정에 넣고 Verify 토큰을 맞춘다. 줄에서 쓰는 채널과 사이트 작성에 쓰는 채널이 완전히 다르다는 걸 유의하자. 메신저엔 Site, 사이트엔 Channel이라는 쓰임이 섞여 있으니 헷갈리면 API Setup 탭 위치부터 다시 본다.
주의점 3: 웹훅 서명 검증은 X-Hub-Signature-256
라인과 거의 같은 구조지만 헤더 이름이 다르고, app secret이 시크릿 역할을 한다.
import hashlib, hmac
def verify_meta_webhook(raw_body: bytes, header: str, app_secret: str) -> bool:
expected = "sha256=" + hmac.new(
app_secret.encode(), raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, header)
여기서도 라인 때랑 같은 규칙이 적용된다. 반드시 원본 바이트로 검증한다. 파싱했다가 다시 직렬화하면 서명이 깨진다.
검증 핸드셰이크는 GET으로 한 번만 온다.
GET /webhooks/whatsapp?hub.mode=subscribe&hub.challenge=12345&hub.verify_token=TOKEN
challenge 값을 그대로 돌려줘야 한다.
4. 허용 사용자 설정
Hermes는 숫자 형식으로만 받는다는 점이 중요하다.
WHATSAPP_CLOUD_ALLOWED_USERS=15551234567,15557654321
# 국가번호 포함, '+'와 공백, 하이픈 없이 쉼표로
# 메타 수신자 화이트리스트와 함께만 안전하게 전체 허용
WHATSAPP_CLOUD_ALLOW_ALL_USERS=true
주의점 4: '+'를 붙이면 아무도 통과 못 한다
+15551234567로 쓰면 값이 잘려서 필터가 전부 탈락한다. 마이너스도 공백도 안 된다. 순수 숫자만 쉼표로 이어붙인다. 텔레그램은 9~10자리, 디스코드는 17~18자리, 왓츠앱은 국가번호 포함 11~13자리로 자릿수가 다르니 헷갈리기 쉽다.
5. 24시간 창과 템플릿
Cloud API는 사용자가 먼저 연락한 뒤 24시간 동안만 자유롭게 응답할 수 있다. 시간이 지나면 승인받은 템플릿으로만 대화를 시작할 수 있다.
{
"messaging_product": "whatsapp",
"to": "15551234567",
"type": "template",
"template": {
"name": "cart_reminder",
"language": { "code": "ko" }
}
}
템플릿은 Meta Business Manager에서 미리 심사받아야 한다. 거부 사유가 ABUSED_FOR_PROMOTIONAL처럼 구체적으로 오는 게 특징이다. 템플릿 상태 변경 이벤트도 웹훅으로 오므로 승인/거부 모니터링을 붙여두면 편하다.
주의점 5: 템플릿에 버튼·리스트를 넣으면 심사가 까다로워진다
단순 텍스트 템플릿은 통과가 쉽지만 버튼이나 카탈로그를 넣으면 심사 기준이 빡빡해진다. 처음에는 텍스트만으로 통과를 시도하고, 통과한 뒤에 요소를 늘리는 게 빠르다.
6. 요금 구조
Cloud API는 '대화별 과금' 방식이다. 대화가 열리면 첫 24시간 안의 여러 메시지를 하나의 대화로 묶어 计정한다. 템플릿 메시지는 별도로 과금된다. 브리지 방식과 달리 트래픽에 따라 실제 비용이 붙으므로, 알림성 발송이 많으면 템플릿을 거치게 하는 편이 저렴하다.
7. 플랫폼 정리
| 항목 | 텔레그램 | 디스코드 | 라인 | 왓츠앱 |
|---|---|---|---|---|
| 수신 방식 | polling | 웹소켓 | 웹훅 | 웹훅 |
| HTTPS 필요 | 아니오 | 아니오 | 예 | 예 |
| 서명 검증 | 없음 | 없음 | X-Line-Signature | X-Hub-Signature-256 |
| 토큰 만료 | 없음 | 없음 | 단기 있음 | 장기 사용 |
| 사용자 ID | 숫자 | 숫자 | U 접두사 | 국가번호 숫자 |
| 정책 심사 | 없음 | 앱 심사 | 개발자 콘솔 | WABA + 템플플릿 |
| 난이도 | 쉬움 | 보통 | 어려움 | 절차 많음 |
정리
- 개인 데모면
hermes whatsapp(QR), 실제 서비스면hermes whatsapp-cloud(공식) - 공식 경로는 Phone Number ID와 전화번호를 혼동하지 말 것 (가장 흔한 404 원인)
- 웹훅은 Cloudflare Tunnel로 노출 후 원본 바이트 기준으로 X-Hub-Signature-256 검증
WHATSAPP_CLOUD_ALLOWED_USERS는 '+' 없이 쉼표 구분 숫자만- 24시간 창 밖 대화가 필요하면 템플릿을 먼저 심사받아야 하고, 텍스트부터 시작할 것
이제야 정리 끝났다. 왜 이걸 밤새 붙여놓냐는 문제가 있을 텐데, 어차피 오전엔 다른 것들이 쌓여서 밀린다. 접속 안 된다고 다시 안 쓰는 경우가 많아서라도 기록은 남기는 편이 낫다.
AI Knowledge Hub
댓글 (1개)
Baileys 경로에 대해 다른 관점 하나 덧붙인다.
"계정 정지 위험 있음"으로만 적혀 있는데, 실제로는 세 가지 층위로 나뉜다. 가장 흔한 것은 링크 세션이 만료되어 재연결 반복되는 것이고, 그 다음이 번호 자체가 차단되는 경우다. 세 번째는 실제 제재로 통화 기능이 정지되는 경우.
연결은 되는데 메시지가 안 오면 세션 만료일 가능성이 높다. 이때는 QR를 다시 스캔하면 복구되지만, 자동 재연결 설정이 켜져 있으면 세션을 오래 붙잡는 게 오히려 위험해진다. 임시로 수동 재연결 모드로 바꿔 두는 편이 낫다.
Cloud API 전환 시점도 현실적인 기준이 있다. 브리지로 하루 이상 실제 고객이 아닌 사람에게 응대할 상황이라면 이미 사업자 고객 대응이 시작된 것이라 policies 위반 소지가 있다. 개인 실험이라면 브리지, 뭐라도 실제 응대한다면 공식 Cloud API로 넘어가는 게 안전하다.
참고로 템플릿 심사_ABUSED_FOR_PROMOTIONAL은 사용자가 메시지를 차단한 비율이 일정 수준 넘으면 자동으로 붙는 사유다. 템플릿 문구를 반복发送하면 금방 걸린다.