문자 메시지
문자 메시지
ClawOps는 하나의 엔드포인트로 SMS·LMS·MMS를 모두 발송합니다. 본문 길이와 첨부 유무에 맞는 Type만 지정하면 통신사까지의 전달은 플랫폼이 처리합니다.
성공하면 201과 함께 접수된 메시지가 돌아옵니다. 통신사 리포트는 그보다 늦게 도착하므로 최종 발송 결과는 webhook으로 받습니다.
지원 채널은 SMS·LMS·MMS 세 가지입니다. iMessage, RCS와 카카오 알림톡은 발송할 수 없습니다.
채널
한도의 기준이 서로 다릅니다. SMS는 UTF-8 바이트로, LMS와 MMS는 글자 수로 셉니다. 한글은 한 자에 3바이트이므로 한글로만 작성하면 SMS에 66자까지 들어가고, 영문과 숫자를 섞으면 더 많이 들어갑니다.
본문이 200byte를 넘길지 확실하지 않다면 처음부터 Type을 lms로 지정하세요. sms로 보냈다가 한도를 넘기면 발송되지 않고 400이 반환됩니다. 본문에 이름 같은 변수를 넣는 경우 이름이 긴 수신자에서만 실패하므로 뒤늦게 발견됩니다.
메시지 보내기
Python은 예약어를 피해 from_을 사용합니다. 두 SDK 모두 메서드 이름은 create입니다.
status의 queued는 발송 요청이 접수됐다는 뜻이며 전달 성공을 보장하지 않습니다. 이 시점에는 통신사 리포트가 돌아오기 전이라 성공과 실패를 알 수 없습니다. 최종 결과는 webhook으로 확인하세요.
수신번호
To는 국내 표기와 +82 E.164를 모두 받습니다. 공백, 하이픈, 괄호, 점은 제거되므로 010-1234-5678과 01012345678은 같은 값입니다. 저장과 조회는 정규화된 형태로 이뤄집니다.
발신번호
From은 필수이며 계정이 보유한 번호여야 합니다. 보유하지 않은 번호를 지정하면 400이 반환됩니다. 발신번호를 자동으로 고르는 동작은 없습니다. 수신번호와 달리 From은 하이픈만 제거하고 숫자 3~12자리인지만 확인합니다.
이미지 첨부
Type을 mms로 지정하고 MediaUrl에 이미지 URL을 전달합니다.
URL은 인증 없이 접근할 수 있는 주소여야 합니다. 플랫폼이 직접 내려받아 통신사로 전달하므로 인증이 필요한 주소나 내부망 주소는 거절됩니다.
형식은 두 단계로 검사합니다. URL 경로의 확장자가 위 목록에 있어야 하고, 내려받은 응답의 Content-Type도 image/jpeg, image/png, image/bmp 중 하나여야 합니다. 확장자가 드러나지 않는 URL은 내용과 무관하게 거절되므로 .jpg처럼 확장자가 붙은 주소를 사용하세요.
PNG와 BMP는 서버가 JPG로 변환해 발송합니다. 통신사가 MMS에서 PNG와 BMP를 거부하기 때문입니다. 변환 과정에서 투명 배경은 흰색으로 합성되고 EXIF 회전 정보는 픽셀에 반영됩니다. 원본이 이미 JPEG이면 재인코딩하지 않고 그대로 씁니다.
첨부 없이 Type을 mms로 지정하면 전달 자체는 LMS와 같아지지만, 한도와 과금은 MMS로 잡힙니다. 첨부가 없다면 처음부터 lms를 사용하세요.
조회
발송과 수신 이력을 한 목록에서 조회합니다. 응답의 direction으로 구분하며 정렬은 최신순 고정입니다.
단건 조회와 첨부 원본 조회는 다음 두 엔드포인트를 사용합니다.
index는 0부터 시작합니다. 목록·단건 응답의 mediaUrl과 수신 webhook의 MediaUrl{i}는 모두 이 엔드포인트를 가리키는 절대 URL입니다 — 저장소 주소가 아닙니다.
이 URL도 API 키 인증이 필요합니다. 첨부는 전부 JPEG으로 변환된 뒤 저장되므로 image/jpeg로 내려옵니다.
대화 스레드 API는 제공하지 않습니다. 상대 번호별로 주고받은 이력을 묶으려면 number로 필터하거나 목록 응답을 from과 to 기준으로 직접 그룹핑하세요.
발송 결과와 수신 문자
발송 결과와 수신 문자는 모두 계정에 등록한 webhook으로 전달됩니다. 메시지마다 콜백 URL을 지정하는 파라미터는 없습니다.
상태는 queued에서 출발해 sent 또는 failed로 끝납니다. 그 사이의 중간 상태는 없습니다. 수신 메시지는 received로 기록됩니다.
페이로드
모든 요청은 Content-Type: application/x-www-form-urlencoded POST입니다.
발송 결과 webhook에는 Subject·Body·NumMedia가 실리지 않습니다. 본문이 필요하면 MessageId로 단건 조회하세요.
MediaUrl{i}는 첨부 조회 엔드포인트를 가리키는 절대 URL입니다. 목록·단건 조회 응답의 mediaUrl과 같은 주소이며, 그대로 요청하면 원본이 내려옵니다.
이 URL도 API 키 인증이 필요합니다. Authorization 헤더 없이 요청하면 401입니다. 공개 저장소 주소가 아닙니다.
요청에는 X-Signature 헤더가 포함됩니다. 계정의 Signing Key로 HMAC-SHA256 서명을 검증한 뒤 처리하세요.
실패 사유는 전달되지 않습니다. message.failed는 실패했다는 사실만 알려주며 통신사 결과코드는 포함되지 않습니다.
제한과 함정
발송이 거절되는 경우
에러 응답의 형태는 어디서 막혔는지에 따라 두 가지입니다.
요청 형식 검증에서 막히면 errors 배열과 code가 함께 옵니다. 어느 필드가 왜 틀렸는지는 errors[].path를 보세요.
형식을 통과한 뒤 업무 규칙에서 막히면 errors 배열 없이 error와 code만 옵니다.
분기는 code로 하세요. error 문구는 안내를 다듬으면서 바뀔 수 있지만 code는 계약입니다. 같은 422라도 recipient_blocked(명단에서 해제해야 나감)와 quota_exceeded(다음 주기에 다시 나감)는 대응이 정반대입니다.
후자에 해당하는 경우는 다음과 같습니다.
첨부 처리 실패는 모두 이미지 처리 실패 (i): <원인> 형태로 옵니다. 위 세 가지 외에 다운로드가 10초를 넘긴 경우도 같은 형태로 반환됩니다. 괄호 안 숫자는 MediaUrl 배열에서 몇 번째 첨부인지를 가리키므로, 여러 장을 보낼 때 어느 URL이 문제인지 이걸로 찾으세요.
월 한도
플랜마다 SMS, LMS, MMS 각각의 월 발송 건수 한도가 있습니다. 한도를 넘겨도 해당 타입의 초과분 부가서비스가 활성이면 발송이 계속되고 초과분이 과금됩니다. 부가서비스가 없으면 422로 거절됩니다.
집계 대상은 발신 중 실패하지 않은 건입니다. queued 상태도 포함되며 failed로 끝난 건은 빠집니다.
한도는 매월 초기화됩니다. 결제 주기가 아니라 월 단위입니다. 여러 달을 선납한 계정도 한도는 매달 리셋됩니다.
수신거부 명단
수신거부 명단에 등록된 번호로는 문자가 나가지 않습니다. 등록 즉시 발신 직전에 막히며 422 recipient_blocked로 거절됩니다. 배치 발신(캠페인)도 같은 관문을 지나므로 해당 대상은 자동으로 건너뜁니다.
해제는 DELETE /v1/accounts/{accountId}/blocked-recipients/{blockId}입니다.
차단된 발송은 메시지 이력에 남지 않습니다. 발송이 일어나지 않았으므로 기록할 것이 없기 때문입니다. 목록 조회에서 찾지 못했다고 다시 보내지 마세요 — 명단에서 해제해야 나갑니다.
등록은 멱등입니다. 이미 차단 중인 (번호, 채널) 조합을 다시 등록하면 에러 없이 기존 항목을 200으로 돌려줍니다. 새로 등록된 경우에만 201입니다.
대상은 이 계정의 발신입니다. 그 번호에서 걸려오거나 도착하는 것은 막지 않습니다.
채널 값은 call과 message입니다. sms가 아닙니다. 전화와 문자를 모두 차단하려면 두 채널로 각각 등록해야 합니다.
광고성 문자
광고성 문자에는 (광고) 표기와 무료 수신거부 방법 안내 등 법적 의무가 따릅니다. 야간 발송 제한(21시~익일 8시)도 적용됩니다.
야간 발송 제한은 플랫폼이 막아주지 않습니다. 배치 발신(전화)에는 시간대 게이트가 걸려 있지만 문자 발송 경로에는 없습니다. 밤에 요청하면 밤에 나갑니다. 발송 시각은 직접 통제하세요.