인터페이스가 텍스트 메시지인 제품
매력은 분명합니다. 사람들은 앱을 설치하거나 웹사이트를 방문하는 것을 좋아하지 않지만, 모두가 문자를 보낼 수 있습니다. 하지만 당신은 채널을 소유하지 않습니다. US 통신사가 채널을 소유하며, 콘텐츠 정책과 승인 프로세스를 통해 영구적으로 거부할 수 있습니다. 이 관문이 제품이 무엇이 될 수 있는지를 결정하므로, 가장 먼저 고려해야 합니다.
0단계 — 관문. 설계 후가 아니라 설계 전에 하세요.
2025년 2월 이후로 US 통신사는 등록되지 않은 A2P 트래픽을 완전히 차단하므로, 단 하나의 메시지도 보내기 전에 브랜드와 캠페인이 승인되어야 합니다. 실시간 오류 문서를 매번 다시 읽으세요; 통신사 정책은 변합니다.
| 코드 | 트리거 | 재제출 가능? |
|---|---|---|
| 30953 | 노골적/암시적 언어, 또는 "포르노, 에스코트, 데이트, 성인 엔터테인먼트 콘텐츠"를 호스팅하는 연결된 사이트 | 아니요 |
| 30885 | "고위험" — 기만적인 마케팅, 또는 트래픽을 잘못 표현한 등록 | 아니요 |
| 30898 | 하나의 EIN에 너무 많은 브랜드, 또는 중복으로 보이는 등록 | 예, 사유가 있는 경우 |
규칙: 첫 번째 답변에서 제품의 콘텐츠 카테고리를 소리 내어 말하고, 그 카테고리가 해당 목록에 있는지 말하세요. 만약 카테고리(예: 데이트)가 거부 규칙의 키워드 그 자체라면, 그 사실을 5분 안에 발견하는 것은 아키텍처, 랜딩 카피, 대체 계획을 바꿉니다. SHAFT는 Sex(성), Hate(증오), Alcohol(알코올), Firearms(총기), Tobacco(담배)의 약어이며, 여기에 Cannabis(대마초)가 추가되고 데이트가 그 옆에 붙습니다.
규칙: 사이드 프로젝트는 기존 회사의 EIN이 아니라 개인 사업자(Sole Proprietor) 브랜드로 등록하세요. 위험 패턴은 세금 ID(30898)를 따르므로, 사이드 프로젝트의 거부가 주 사업의 발송 능력에 영향을 주어서는 안 됩니다. 개인 사업자는 세금 ID가 없는 개인으로 범위가 한정됩니다. 처리량은 낮지만, 베타에는 충분합니다.
규칙: 캠페인 승인을 얻기 위해 가짜 랜딩 페이지를 절대 만들지 마세요. 실제 트래픽을 숨기는 페이지는 그 자체로 30885 패턴이며, 한 번호의 위험을 앞으로 등록할 모든 번호의 위험으로 전환합니다. 확률을 솔직하게 말하고 소유자가 결정하게 하세요.
서비스를 기계적으로 수행하는 작업으로 설명하세요. 그리고 그것이 사실인 경우에만 그렇게 하세요(예: 실제로 메시지가 그런 내용일 때, 일기 쓰기와 소규모 그룹 피드백). 어떤 진실된 설명도 관문을 통과하지 못한다면, 답은 더 나은 완곡어가 아니라 다른 채널입니다.
심사는 가리킬 수 있는 사실에 보상합니다: 인바운드 전용 옵트인, 대화량, 반복 메시지에 링크 없음, 모든 것보다 먼저 처리되는 STOP, 문 앞에서 거부되는 노골적인 콘텐츠.
0.5단계 — 의도가 아니라 숫자와 객체에 대한 브리핑을 읽으세요
창업자의 브리핑을 README.md로 그대로 보관하고, CLAUDE.md에 모든 엔지니어링 문서보다 우선한다고 명시하세요.
규칙: 상태 기계를 작성하기 전에, 브리핑의 모든 기간과 그 기간이 작용하는 모든 명사를 나열하세요. 두 개의 기간이 있다면, 그것들은 두 개의 기능입니다. 친구의 답장이 작성자에게 도달하는 일주일 지연과 작성자 자신의 업데이트에 대한 2주 잠금이 있는 브리핑은 두 지연 모두가 대상이 아니었던 항목 하나에 대한 7일 보류로 쉽게 합쳐질 수 있으며, 모든 하위 오류는 그 병합에서 비롯됩니다.
규칙: 모든 지연의 객체를 명명하세요 — "누구에게서 보류"인지. 친구에게서 보류된 것과 작성자에게서 보류된 것은 반대되는 제품입니다.
규칙: 브리핑의 문자 그대로의 디자인을 채널에서 시행할 수 없을 때, 그렇게 말하고 대안을 제시하세요 — 조용히 시행 가능한 쪽을 선택하지 마세요. (그룹 채팅의 생성자는 그룹의 구성원이므로, 어떤 코드도 그룹의 답장을 생성자로부터 막을 수 없습니다.) 이것은 전제 결정이며, 전제 결정은 소유자의 것입니다.
일관된 오독은 오독처럼 느껴지지 않습니다. 대체된 설계 단락을 삭제하는 대신 취소선을 그어 그 자리에 두세요. 이후 세션에서 브리핑이 어떻게 오독되었는지 보여줍니다.
1단계 — 채널이 할 수 있는 것과 할 수 없는 것
- 코드로 그룹 문자 보내기는 작동합니다. Twilio Conversations는 +1 롱코드에서만 그룹 MMS를 지원합니다 — 무료 전화번호나 숏코드가 아닙니다 — 자신의 번호를 포함하여 최대 10명의 참가자. 기억에서 다르게 단언하지 마세요.
- 그룹-MMS SMS 참가자는 Address를 가지며 ProxyAddress는 없습니다. 따라서 하나의 번호가 모든 방에 서비스되지만, 참가자/프록시 고유성 규칙(50425)은 프록시가 없는 참가자에 대해 문서화된 동작이 없습니다. 하나의 번호와 하나의 참가자 집합을 공유하는 여러 동시 방을 절대 설계하지 마세요. 사람당 하나의 내구성 있는 방은 안정적인 참가자 집합을 가집니다.
- 웹페이지는 iPhone 주소록을 읽을 수 없습니다. Contact Picker API는 Chrome-on-Android 전용입니다. "연락처 동기화"는 처음부터 실패합니다. 사용자가 전달하는 한 줄로 대체하세요 — 수신자의 인바운드 문자는 어차피 통신사가 원하는 동의 기록입니다.
- Telegram 봇은 그룹을 생성하거나 누군가를 그룹에 추가할 수 없습니다. 또한 프라이버시 모드가 켜져 있으면 자신에게 주소 지정된 명령만 듣습니다. 둘 다 도움이 됩니다: 생성자는 자신이 이미 가지고 있는 채팅에 봇을 추가하고, 일반 잡담은 입력으로 오인될 수 없습니다. 봇을 생성하려면 @BotFather에서 사람이 필요합니다(API 없음). 토큰을 요청하세요.
- WhatsApp Cloud API에는 그룹 메시징이 없습니다.
- iMessage API(Sendblue, LoopMessage)는 파란색 버블과 실제 그룹 채팅을 지원하지만, 회선당 월 약 $100와 설정 비용이 듭니다.
2단계 — 채널을 잃어도 버틸 수 있는 아키텍처
채널이 거부될 수 있기 때문에, 전송 계층은 하나의 파일이어야 합니다:
machine.js 순수: (context, event) -> {replies, effects} I/O 없음, 시계 없음
engine.js 컨텍스트 조립 -> machine -> effects 전체 경로
effects.js createEffects({db, channel}) 결정 수행
twilio.js / telegram.js 교체 가능
api/*.js engine.js 위의 얇은 shim 각각 ~10줄
sim/ 인메모리 db + 콘솔 채널
- 결정 코어는
now를 인자로 받으며 네트워크에 접촉하지 않습니다. 이 제품 유형의 모든 버그는 순서 버그입니다(두 번 발사되는 알림, 빈 방으로 가는 메시지, 절대 도착하지 않는 보류 항목). 그리고 그 중 어느 것도 실제 네트워크와 7일 타이머로 테스트할 수 없습니다. - 어댑터는 인자이며, 이펙트 레이어에서 임포트되지 않습니다 — 시뮬레이터와 대체 채널이 필요한 것과 동일한 리팩터입니다.
- HTTP 핸들러 안에 로직이 없습니다.
api/telegram.js내부에 작성된 채널별 온보딩은 테스트 불가능합니다. 분리하세요.
3단계 — 그냥 테스트하지 말고 실제로 구동하세요
node sim/drive.js 스크립트: 전체 루프, 일주일 점프, 가드
node sim/drive.js --repl 모든 숫자로 입력; /as <n> /jump <days> /state
- 시뮬레이터는 프로덕션 경로를 실행해야 합니다, 복사본이 아니라.
- 첫 번째 구동은 단위 테스트가 도달할 수 없는 몇 가지 실제 결함을 표면화할 것으로 예상하세요: 아무도 들어오기 전에 스스로 인사하는 방, 일주일 후 날짜에 대한 맨 요일, 어떤 항목을 의미하는지 말하지 않는 알림, 모든 구성원이 떠난 방.
- 테스트가 실패하면, 먼저 테스트를 확인하세요 (
Sun, Aug 30의 쉼표를 허용하지 않는 날짜 정규식; 다른 정당한 메시지에서 발사되는doesNotMatch). - 저장소에 찍힌 타임스탬프로 결정하거나 출력하지 마세요. 저장소는 엔진의 주입된
now가 아니라 실제 시계에서created_at을 쓰므로, 점프된 시계 아래에서는 거짓말을 합니다(그리고 과거의 날짜를 출력합니다). 기계가 쓴 열에서만 결정하고, 관련 날짜를 하나의 저장된 앵커에 연결하세요.
4단계 — 잠금 화면을 위한 카피
모든 아웃바운드 문자열을 하나의 파일에 보관하세요.
- 이모지 없음, 느낌표 없음, 마케팅 없음.
- 하루 이틀 이상 떨어진 날짜에 대해 맨 요일을 절대 사용하지 마세요.
Sun Aug 30. - 모든 키워드에 대해 구두점과 대소문자를 용서하세요 —
Wait.와Yes!가 허용되어야 합니다. - 키워드를 포함하는 산문은 여전히 산문입니다: "we should wait and see"는 WAIT가 아닙니다. 정규화된 전체 메시지를 일치시키고, 부분 문자열은 일치시키지 마세요.
- STOP / HELP / START는 모든 애플리케이션 상태와 모든 모델 호출보다 먼저 파싱됩니다. 따라서 옵트아웃이 다른 곳의 버그나 API 중단으로 인해 깨질 수 없습니다.
- 모델이 콘텐츠를 검사한다면, 실패 시 폐쇄하세요: 검사를 실행할 수 없으면 아무것도 보내지 말고 그렇게 말하세요.
5단계 — 모델이 사용자 콘텐츠를 재작성한다면, 신뢰하기 전에 먼저 실행하세요
사용자 항목에서 제3자 이름을 제거하는 가드는 완벽하게 읽을 수 있지만 여전히:
- 사실을 지어낼 수 있습니다 — "Jane Doe"를 "자연스러운 핸들"로 바꾸라고 요청했을 때, 앱에 대해 아무것도 언급되지 않았는데 "앱에서 온 그 여자"라고 썼습니다. 수신자는 지어낸 사실을 진실로 읽습니다.
- 과도하게 수정하여 실제 세부 사항을 삭제할 수 있습니다 — "런 클럽의 Jane"이 "그녀"가 되었습니다.
규칙: 명령어는 모델이 무엇을 지어낼 수 없는지, 무엇을 삭제할 수 없는지 말해야 합니다. 작동하는 형태: 출처에 대체를 근거하고, 주변 구절이 아니라 토큰을 대체하며, 문장이 이미 명시한 세부 사항을 재사용하는 것보다 중립적인 대체를 선호하세요.
규칙: 각 라이브 모델 결함을 옵트인 테스트로 고정하고, API 키 없이 건너뛰도록 하세요. 프롬프트 편집이 모든 테스트 실행에서 비용을 발생시키지 않으면서 조용히 회귀할 수 없도록 합니다. 가드 호출당 2-5초를 예산하세요.
비용 (규모: 실제로 확인하세요)
+1 롱코드는 월 1-2달러입니다. SMS는 세그먼트당 1센트 미만입니다. MMS는 몇 센트입니다. 개인 사업자 등록은 몇 달러의 일회성 비용과 소액의 월 캠페인 수수료가 있습니다. Telegram은 무료입니다.
묻기 vs 결정하기
- 묻기: 영구적으로 거부될 수 있음을 알면서 정직한 등록을 제출할지, 아니면 Telegram에서 시작할지(돈과 되돌릴 수 없는 결과); 제품이 무엇인지를 바꾸는 모든 것.
- 결정하기: 아키텍처, 카피 어조, 상태 기계, 테스트 범위, 어떤 어댑터를 먼저 만들지, 날짜 형식, 가드 동작.
무언가가 차단되었다고 보고하기 전에, 모든 후보 자격 증명을 열거하세요 (디스크에 고갈된 API 키 하나가 있다고 해서 작동하는 다른 키가 없다는 의미는 아닙니다).
참고: build-remote-mcp-server (또 다른 "네이티브 표면" 패턴).