계정마다 발급되는 API 키 하나로 POST /api/send를 호출하면, 로그인된 휴대폰에서 실제로 문자가 발송됩니다.
비밀번호나 PC 웹 로그인 세션과는 무관한 별도 자격증명이며, 이 키로 할 수 있는 일은 "문자 발송" 하나뿐입니다 —
연락처 조회·삭제, 통화기록 열람 등 다른 어떤 기능에도 쓸 수 없습니다.
구독하지 않은 계정은 키를 발급받아도 402 premium_required 오류가 반환됩니다.
API 키는 PC 웹(twophone.co.kr/app)에 로그인한 뒤, 좌측 하단 프로필 버튼을 눌러 열리는 화면에서 확인·복사·재발급할 수 있습니다. 앱에서는 확인할 수 없습니다.
키가 유출됐다고 의심되면 같은 화면에서 재발급하세요 — 기존 키는 그 즉시 무효화됩니다.
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 회선으로 발송됩니다 — 특정 회선을 지정하고 싶다면 반드시 넣어주세요. |
| 값 | 의미 |
|---|---|
PHYSICAL_MAIN | uSIM 메인 |
PHYSICAL_DUAL | uSIM 듀얼(통신사 부가번호) |
ESIM_MAIN | eSIM 메인 |
ESIM_DUAL | eSIM 듀얼(통신사 부가번호) |
{ "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":"..."} | 휴대폰이 발송을 시도했지만 실패(사유 포함) |
curl -X POST https://twophone.co.kr/api/send \
-H "Authorization: Bearer tp_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{"address":"01012345678","body":"안녕하세요"}'
한도는 사용량 배급이 아니라 사고 방지 장치입니다. 오작동한 스크립트나 AI 에이전트가 문자를 무한정 보내는 상황을 끊는 것이 목적이며, 정상적인 사용이 여기에 닿지 않도록 값을 잡았습니다.
| 구분 | 기본값 | 목적 |
|---|---|---|
| 분당 | 200건 | 버스트 허용 — 고객 200명에게 한 번에 보내는 것은 정상입니다. 초당 수십 건씩 쏟아내는 폭주만 걸립니다. |
| 시간당 | 500건 | 지속 발송 억제 — 폭주 루프는 몇 분 만에 여기서 멈추고, 일반적인 배치 작업은 닿지 않습니다. |
| 하루 | 2000건 | 최후 방어선 |
| 중복 차단 | 1분 | 같은 번호 + 같은 내용의 반복. 루프·크론 오설정을 두 번째 발송에서 끊습니다. |
/api/send와 MCP)에만 적용됩니다.
중복 차단은 발송에 성공한 건만 기억합니다. 503·504로 실패한 뒤의 재시도는
중복으로 보지 않으므로 안심하고 재시도하세요. 같은 내용을 의도적으로 한 번 더 보내야 한다면
idempotencyKey에 서로 다른 값을 지정하면 통과합니다.
연동을 개발하다 보면 재시도 로직 실수나 크론 중복 실행으로 같은 문자가 계속 나가는 사고가 생깁니다. 그래서 두 겹으로 막습니다.
알림에는 어떤 번호로 무슨 내용을 보내려 했는지가 담기지 않습니다. 서버가 세는 것은 “차단된 횟수” 하나뿐이며 그마저도 메모리에만 있습니다.
영리 목적의 광고성 정보를 전송할 때는 정보통신망법 제50조가 적용됩니다. 법적 의무의 주체는 전송자, 즉 계정 소유자인 이용자이며, TwoPhone은 이용자의 휴대폰에서 나가는 문자를 중계하는 도구입니다. 어떤 문자가 광고성인지에 대한 최종 판단과 책임은 이용자에게 있습니다 (이용약관 제6조).
(광고) 표기무료수신거부 080-000-0000)요청에 advertisement: true(MCP는 is_advertisement)를 넣으면, 서버가 광고성 문자의
요건을 대신 검사해 회원님이 모르고 위반하는 일을 막아줍니다.
{
"address": "01012345678",
"body": "(광고) 여름 세일 30% 무료수신거부 080-000-0000",
"advertisement": true
}
| 선언했을 때 서버가 하는 일 | 결과 |
|---|---|
(광고) 표기가 없으면 | 400으로 거절하고 고칠 문안을 알려줍니다 |
| 21~08시면 | 403으로 거절하고 다시 보낼 수 있는 시각을 알려줍니다 |
| 무료 수신거부 안내가 없으면 | notice로 알립니다(거절하지 않음 — 문구 형태가 자유로워 오판 위험이 있습니다) |
| 요건을 모두 갖췄으면 | 그대로 발송합니다. 아무 고지도 붙지 않습니다 |
서버는 문자의 내용이 광고인지를 판단하지 않습니다. 그 판단은 자연어 해석이고, 틀리면 정상 문자를 막게 되기 때문입니다. 대신 객관적으로 확인 가능한 표지만 봅니다.
| 상황 | 서버의 처리 |
|---|---|
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항).
Claude·ChatGPT·Gemini 같은 AI 어시스턴트가 대화 중에 직접 문자를 보내게 할 수 있습니다. MCP(Model Context Protocol) 서버를 제공하며, 같은 API 키를 그대로 씁니다 — 새로 발급받을 것이 없습니다.
| 도구 | 하는 일 |
|---|---|
send_sms | 문자 발송 (to, body, line, idempotency_key) |
list_lines | 휴대폰에 켜져 있는 발신 회선과 사용자가 붙인 이름 |
check_device | 휴대폰이 연결돼 있는지 확인 (꺼져 있으면 깨우기 시도) |
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"
{
"mcpServers": {
"twophone": {
"command": "npx",
"args": ["-y", "@twophone/mcp"],
"env": { "TWOPHONE_API_KEY": "tp_xxxxxxxxxxxxxxxxxxxxxxxx" }
}
}
}
/api/send와 완전히 같습니다 — MCP로 우회해서 더 보낼 수 없습니다(6·7항).[사용자에게 반드시 그대로 전달하세요]와 함께 실립니다. 어시스턴트가 이를 요약하거나 생략하지 않도록 지침이 걸려 있습니다.idempotency_key를 처음과 같은 값으로 넘기면 같은 문자가 두 번 나가지 않습니다.