발송 API · MCP 문서 구독 전용

외부 시스템(쇼핑몰, ERP, 자동화 스크립트 등)에서 TwoPhone 계정으로 문자를 자동 발송할 수 있는 서버-투-서버 API입니다.

1. 요약

계정마다 발급되는 API 키 하나로 POST /api/send를 호출하면, 로그인된 휴대폰에서 실제로 문자가 발송됩니다. 비밀번호나 PC 웹 로그인 세션과는 무관한 별도 자격증명이며, 이 키로 할 수 있는 일은 "문자 발송" 하나뿐입니다 — 연락처 조회·삭제, 통화기록 열람 등 다른 어떤 기능에도 쓸 수 없습니다.

구독하지 않은 계정은 키를 발급받아도 402 premium_required 오류가 반환됩니다.

2. API 키 발급

API 키는 PC 웹(twophone.co.kr/app)에 로그인한 뒤, 좌측 하단 프로필 버튼을 눌러 열리는 화면에서 확인·복사·재발급할 수 있습니다. 앱에서는 확인할 수 없습니다.

키가 유출됐다고 의심되면 같은 화면에서 재발급하세요 — 기존 키는 그 즉시 무효화됩니다.

3. 요청

POST https://twophone.co.kr/api/send
Authorization: Bearer <API 키>
Content-Type: application/json

{
  "address": "01012345678",
  "body": "안녕하세요, 주문하신 상품이 발송되었습니다.",
  "lineId": "ESIM_MAIN"
}
필드필수설명
address수신 번호
body문자 내용
advertisement아니오이 문자가 영리 목적의 광고·홍보인지 선언합니다. true로 선언하면 서버가 (광고) 표기와 야간 발송 제한을 검사합니다. 자세한 내용은 아래 7항.
idempotencyKey아니오중복 발송 방지 키. 같은 키로 10분 안에 다시 호출하면 문자를 다시 보내지 않고 첫 번째 결과(messageId 포함)를 그대로 돌려줍니다. 표준 Idempotency-Key 헤더로 넣어도 동일하게 동작합니다. 재시도할 때는 반드시 처음과 같은 값을 쓰세요.
lineId아니오발신 회선. 아래 4개 값 중 하나(대소문자 정확히 일치). 생략하거나 값이 잘못되면 휴대폰의 시스템 기본 SMS 회선으로 발송됩니다 — 특정 회선을 지정하고 싶다면 반드시 넣어주세요.

lineId 값

의미
PHYSICAL_MAINuSIM 메인
PHYSICAL_DUALuSIM 듀얼(통신사 부가번호)
ESIM_MAINeSIM 메인
ESIM_DUALeSIM 듀얼(통신사 부가번호)

4. 응답

성공 — 200

{ "ok": true, "messageId": 123 }

광고 규제 관련 고지가 있으면 성공·실패와 무관하게 notice 배열이 함께 실립니다(아래 6항). 기존 필드는 그대로이므로 이 필드를 모르는 호출자는 무시해도 됩니다.

{ "ok": true, "messageId": 123,
  "notice": ["이 문자에 수신거부 안내가 들어 있습니다. …"] }

실패

상태 코드본문의미
400{"ok":false,"error":"invalid_address"}수신 번호가 전화번호 형식이 아님. 하이픈·공백·+82 표기는 자동으로 정리되지만, 이름이나 메모가 섞이면 거절됩니다.
400{"ok":false,"error":"empty_body"} · "body_too_long" · "unknown_line"내용 누락 / 2000자 초과 / 알 수 없는 lineId
401-API 키가 없거나 잘못됨
402{"ok":false,"error":"premium_required"}구독이 필요한 계정
400{"ok":false,"error":"ad_label_missing"}advertisement: true로 선언했는데 본문이 (광고)로 시작하지 않음. 응답의 detail에 고칠 문안 예시가 들어 있습니다.
403{"ok":false,"error":"ad_night_ban","retryAfterSeconds":…}본문이 (광고)로 시작하는 문자를 21~08시에 보내려 함. 아래 6항 참고.
409{"ok":false,"error":"duplicate_send","retryAfterSeconds":…}같은 번호에 같은 내용을 1분 안에 다시 보냄. 재시도라면 이미 발송된 것이니 다시 보내지 마세요. 의도적 재발송은 idempotencyKey를 지정하면 통과합니다.
429{"ok":false,"error":"rate_limited","retryAfterSeconds":37}계정 발송 한도 초과(기본 분당 200건 / 시간당 500건 / 하루 2000건). Retry-After 헤더에 같은 값이 실립니다.
503{"ok":false,"error":"device_offline"}휴대폰이 꺼져있거나 절전 상태. 서버가 푸시로 깨우기를 시도하지만 이 요청 안에서 깨어난다는 보장은 없습니다 — 잠시 후 재시도하세요.
504{"ok":false,"error":"timeout"}20초 안에 휴대폰이 응답하지 않음. 재시도하세요.
200{"ok":false,"error":"..."}휴대폰이 발송을 시도했지만 실패(사유 포함)
503/504는 일시적인 상태입니다. 배치 작업(예: 하루 1회 정산 문자 발송)이라면 실패한 건을 다음 실행 때 자연스럽게 다시 시도하는 방식을 권장합니다.

5. 예제 (curl)

curl -X POST https://twophone.co.kr/api/send \
  -H "Authorization: Bearer tp_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"address":"01012345678","body":"안녕하세요"}'

6. 발송 한도와 중복 차단

한도는 사용량 배급이 아니라 사고 방지 장치입니다. 오작동한 스크립트나 AI 에이전트가 문자를 무한정 보내는 상황을 끊는 것이 목적이며, 정상적인 사용이 여기에 닿지 않도록 값을 잡았습니다.

구분기본값목적
분당200건버스트 허용 — 고객 200명에게 한 번에 보내는 것은 정상입니다. 초당 수십 건씩 쏟아내는 폭주만 걸립니다.
시간당500건지속 발송 억제 — 폭주 루프는 몇 분 만에 여기서 멈추고, 일반적인 배치 작업은 닿지 않습니다.
하루2000건최후 방어선
중복 차단1분같은 번호 + 같은 내용의 반복. 루프·크론 오설정을 두 번째 발송에서 끊습니다.
휴대폰 앱과 PC 웹에서 직접 보내는 문자에는 이 한도가 적용되지 않습니다. 단체(대량) 문자 전송도 마찬가지입니다. 한도는 API 키를 사용하는 자동화 경로 (/api/send와 MCP)에만 적용됩니다.

중복 차단발송에 성공한 건만 기억합니다. 503·504로 실패한 뒤의 재시도는 중복으로 보지 않으므로 안심하고 재시도하세요. 같은 내용을 의도적으로 한 번 더 보내야 한다면 idempotencyKey에 서로 다른 값을 지정하면 통과합니다.

개발 중 루프가 돌면

연동을 개발하다 보면 재시도 로직 실수나 크론 중복 실행으로 같은 문자가 계속 나가는 사고가 생깁니다. 그래서 두 겹으로 막습니다.

  1. 두 번째 발송에서 차단 — 같은 번호에 같은 내용이면 409로 끊깁니다. 실제로 나간 문자는 1통이고 요금도 1통입니다.
  2. 휴대폰으로 경고 알림 — 차단이 10분 안에 5회 넘게 반복되면, 계정 주인의 휴대폰에 알림이 뜹니다. “같은 내용의 문자가 반복 발송되고 있습니다 … 차단된 문자는 발송되지 않았습니다”
두 번째가 특히 중요합니다. 409·429는 호출한 코드에게만 전달되는데, 사고가 나는 상황은 대개 그 코드가 응답을 확인하지 않고 계속 도는 상황입니다. 그러면 차단은 제대로 동작하는데 정작 요금을 내는 사람만 아무것도 모르게 됩니다. 휴대폰 알림은 그 경우에도 사용자에게 닿는 유일한 경로입니다.

알림에는 어떤 번호로 무슨 내용을 보내려 했는지가 담기지 않습니다. 서버가 세는 것은 “차단된 횟수” 하나뿐이며 그마저도 메모리에만 있습니다.

7. 광고성 문자 (정보통신망법)

영리 목적의 광고성 정보를 전송할 때는 정보통신망법 제50조가 적용됩니다. 법적 의무의 주체는 전송자, 즉 계정 소유자인 이용자이며, TwoPhone은 이용자의 휴대폰에서 나가는 문자를 중계하는 도구입니다. 어떤 문자가 광고성인지에 대한 최종 판단과 책임은 이용자에게 있습니다 (이용약관 제6조).

지켜야 할 것

광고성 문자를 보낼 때는 선언해 주세요

요청에 advertisement: true(MCP는 is_advertisement)를 넣으면, 서버가 광고성 문자의 요건을 대신 검사해 회원님이 모르고 위반하는 일을 막아줍니다.

{
  "address": "01012345678",
  "body": "(광고) 여름 세일 30% 무료수신거부 080-000-0000",
  "advertisement": true
}
선언했을 때 서버가 하는 일결과
(광고) 표기가 없으면400으로 거절하고 고칠 문안을 알려줍니다
21~08시면403으로 거절하고 다시 보낼 수 있는 시각을 알려줍니다
무료 수신거부 안내가 없으면notice로 알립니다(거절하지 않음 — 문구 형태가 자유로워 오판 위험이 있습니다)
요건을 모두 갖췄으면그대로 발송합니다. 아무 고지도 붙지 않습니다
선언하지 않은 문자에 대해서는 서버가 광고성 여부를 판단하지 않습니다. 선언을 누락해 발생한 법 위반의 책임은 전송자인 회원에게 있습니다 (이용약관 제6조의2).

서버가 판정하는 기준

서버는 문자의 내용이 광고인지를 판단하지 않습니다. 그 판단은 자연어 해석이고, 틀리면 정상 문자를 막게 되기 때문입니다. 대신 객관적으로 확인 가능한 표지만 봅니다.

상황서버의 처리
advertisement: true 선언 + (광고) 표기 없음 거절 (400) — 회원님이 광고라고 밝혔는데 법이 요구하는 표기가 없는 상태입니다. 고칠 문안을 함께 드립니다.
advertisement: true 선언 + 21~08시 거절 (403) — 표기 유무와 무관하게 적용됩니다.
본문이 (광고)·[광고]로 시작 + 21~08시 거절 (403) — 이용자가 스스로 광고라고 밝힌 경우라 오판의 여지가 없습니다. 다시 보낼 수 있는 시각을 함께 안내합니다.
본문이 (광고)로 시작 + 주간발송. 수신거부 안내가 없으면 notice로 알립니다.
표기는 없는데 수신거부 안내가 들어 있음 발송 + notice. 광고인데 표기를 빠뜨렸을 가능성이 높지만 단정할 수 없어 막지 않습니다. 야간이면 위법 가능성을 함께 알립니다.
그 외 모든 문자발송. 아무 고지도 붙지 않습니다.
(광고) 표기를 빼면 야간 거절을 피할 수 있지만, 표기를 빠뜨리는 것 자체가 위반입니다 (제50조 제4항). 표기를 생략해 우회하지 마세요. MCP를 통해 연결된 AI 어시스턴트에게도 같은 내용이 지침으로 전달됩니다.

어디서 어떻게 안내되나

발송 경로마다 안내가 닿는 방식이 다르지만, 기준과 문구는 모두 같습니다.

경로(광고) 표기 유도야간 위법 가능성
PC 웹 단체 문자 문자 내용을 입력하면 화면에서 바로 안내하고, 「맨 앞에 (광고) 붙이기」 버튼을 제공합니다. 야간에는 화면에 경고가 표시되고, (광고) 표기가 있으면 발송 버튼이 잠깁니다.
발송 API 키 발급 시 이용 고지에 동의를 받고, 응답의 notice 배열로 전달됩니다(성공·실패 모두). advertisement 선언 시 표기가 없으면 고칠 문안과 함께 거절합니다. (광고) 표기가 있으면 403으로 거절하고, 표기가 없어도 광고로 보이면 notice로 알립니다.
MCP (AI 어시스턴트) 도구 결과에 [사용자에게 반드시 그대로 전달하세요]로 실려 어시스턴트가 사용자에게 전달합니다. 연결 시 지침으로도 전달되며, 어시스턴트는 광고성 문자에 is_advertisement를 선언하고 (광고) 문안을 먼저 제안하도록 지시받습니다. API와 동일하게 거절·고지하며, 어시스턴트에게 “표기를 빼서 우회하지 말라”는 지침이 함께 걸려 있습니다.

TwoPhone은 발송 내용을 저장하지 않습니다. 위 판정은 전송되는 순간 메모리에서만 이뤄지며 기록으로 남지 않습니다(개인정보처리방침 4항).

8. AI 어시스턴트 연결 (MCP)구독 전용

Claude·ChatGPT·Gemini 같은 AI 어시스턴트가 대화 중에 직접 문자를 보내게 할 수 있습니다. MCP(Model Context Protocol) 서버를 제공하며, 같은 API 키를 그대로 씁니다 — 새로 발급받을 것이 없습니다.

도구

도구하는 일
send_sms문자 발송 (to, body, line, idempotency_key)
list_lines휴대폰에 켜져 있는 발신 회선과 사용자가 붙인 이름
check_device휴대폰이 연결돼 있는지 확인 (꺼져 있으면 깨우기 시도)
이 키로 할 수 있는 일은 문자 발송뿐입니다. 대화 내용·연락처·통화기록은 AI가 읽을 수 없습니다.

연결 (원격 URL을 지원하는 클라이언트)

URL:  https://twophone.co.kr/mcp
헤더: Authorization: Bearer <API 키>

Claude Code 예:

claude mcp add --transport http twophone https://twophone.co.kr/mcp \
  --header "Authorization: Bearer tp_xxxxxxxxxxxxxxxxxxxxxxxx"

연결 (stdio만 지원하는 클라이언트)

{
  "mcpServers": {
    "twophone": {
      "command": "npx",
      "args": ["-y", "@twophone/mcp"],
      "env": { "TWOPHONE_API_KEY": "tp_xxxxxxxxxxxxxxxxxxxxxxxx" }
    }
  }
}

알아두세요