내 코드에서 보내는 SMS API 문서

워크스페이스와 같은 계정, 가격, 잔액을 쓰는 간단한 REST API예요: 메시지 한 건 또는 최대 5,000개 번호를 한 번에 보내고, 비용을 먼저 계산하고, 메시지마다 상태를 확인하고, 웹훅 URL로 전송 이벤트를 받아요.

기본 URL: https://sendsmsonline.cc/api/v1

빠른 시작

  1. 1. 키를 만드세요. 워크스페이스에서 API 및 웹훅을 여세요. 키는 첫 충전이 반영된 뒤 열리고, 키마다 한 번만 표시되니 복사하거나 텍스트 파일로 저장하세요.
  2. 2. 비용을 확인하세요. 동일한 본문을 /costs에 POST하여 수신자, 세그먼트 및 총 비용을 확인하세요. 발송은 되지 않아요.
  3. 3. 발송하세요. /sends에 POST하세요. 응답에는 발송 ID가 포함되며, 메시지는 즉시 또는 send_at에 발송돼요.
curl https://sendsmsonline.cc/api/v1/sends \
  -H "Authorization: Bearer $SSO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "recipients": [ { "to": "+447700900461", "fields": { "first_name": "Tom" } }, { "to": "+12015550142", "fields": { "first_name": "Ava" } } ], "text": "Hi {{first_name}}, your table is ready at 7:30.", "name": "Friday bookings", "client_ref": "bookings-1107" }'

인증

Authorization 헤더로 키를 보내세요: Authorization: Bearer sso_live_.... 키는 49자이고 sso_live_로 시작해요. 계정당 최대 5개까지 만들 수 있고, 워크스페이스에서 폐기하면 바로 작동이 멈춰요. 가격 목록은 공개라 키가 필요 없어요.

오류

오류는 HTTP 상태 코드와 안정적인 코드, 사람에게 보여줄 수 있는 문장이 포함된 JSON 본문을 사용해요. 요청이 실패하면 비용이 청구되지 않아요.

HTTP/1.1 402 Payment Required
{ "error": { "code": "insufficient_credit", "message": "Short by $3.20. Add funds to send.", "total": "8.62480", "credit": "5.42000" } }
필드의미
400요청에 변경이 필요해요.
401키가 없거나, 틀렸거나, 폐기됐어요.
402총 비용에 대한 잔액이 부족해요.
404해당 발송, 메시지, 국가 또는 엔드포인트가 없어요.
409발송이 이미 시작되었어요.
4291분에 240건을 넘는 요청이에요.

제한

  • 키당 1분에 240개의 요청이 가능하며, X-RateLimit-Limit, X-RateLimit-Remaining 및 X-RateLimit-Reset(유닉스 초)에 표시돼요.
  • 요청당 5,000개의 번호, 텍스트당 2,000자, 메시지당 10개의 세그먼트로 제한돼요.
  • 계정당 1분에 최대 300개의 메시지가 발송돼요.
  • send_at은 10분 후부터 30일 후까지 정할 수 있어요.

가격

GET/api/v1/prices

GET/api/v1/prices/{country}

각 국가별 메시지(한 세그먼트)당 가격을 5자리 소수점 USD로 표시해요. 키가 필요하지 않아요.

{ "price": { "country": "GB", "name": "United Kingdom", "dial_code": "+44", "price_usd": "0.00868" }, "updated": "2026-11-07" }

계정 잔액

GET/api/v1/account

{ "account": "SO8K2M4Q7X9TBA", "credit_usd": "41.27660", "currency": "USD" }

비용 예측

POST/api/v1/costs

발송과 동일한 본문을 받아 비용을 반환하며, 실제로 발송하거나 요금을 청구하지 않아요. 각 수신자의 병합된 텍스트가 세그먼트를 결정하므로 총 비용이 정확해요.

{
  "recipients": 2, "segments_min": 1, "segments_max": 1, "encoding": "gsm7", "messages_billed": 2,
  "total_usd": "0.01426", "credit_usd": "41.27660", "enough_credit": true,
  "countries": [ { "country": "GB", "recipients": 1, "price_usd": "0.00868", "subtotal_usd": "0.00868" },
                 { "country": "US", "recipients": 1, "price_usd": "0.00558", "subtotal_usd": "0.00558" } ],
  "dropped": [], "too_long": null
}

발송 생성

POST/api/v1/sends

하나의 텍스트를 하나 또는 여러 번호로 발송해요. 발송이 승인되면 전체 발송 비용이 잔액에서 차감되며, 실패한 메시지는 하나씩 환불돼요.

필드의미
to국제 형식(+447700900461)의 번호를 사용해요. 이 필드나 recipients 필드를 사용하세요.
recipients병합 필드를 위한 to와 fields가 포함된 객체예요: { "to": "+1...", "fields": { "first_name": "Ava" } }.
text메시지는 최대 2,000자까지 가능해요. {{field}}은 수신자별로 대체되며, 누락된 값은 빈 값이 돼요.
send_at선택적으로 ISO 8601 형식의 시간을 오프셋 또는 Z로 설정할 수 있어요. 설정하지 않으면 즉시 발송이 시작돼요.
name워크스페이스에 표시되는 선택적 라벨이에요(80자).
client_ref웹훅에 반환되는 선택적 참조 값이에요(64자).
opt_out설정에 저장된 수신 거부 안내 문구를 텍스트 끝에 추가해요.
default_country국가 코드 없이 작성된 번호에 사용되는 ISO 코드예요.
HTTP/1.1 201 Created
{
  "send": { "id": "CA7Q29XMAB4DFK", "status": "queued", "source": "api", "recipients": 2, "segments_max": 1,
            "encoding": "gsm7", "cost_usd": "0.01426", "refunded_usd": "0.00000", "client_ref": "bookings-1107",
            "send_at": null, "created_at": "2026-11-07T10:12:03.120Z", "started_at": null, "finished_at": null },
  "dropped": [ { "value": "+4412", "code": "too_short", "message": "Too short" } ]
}

발송할 수 없는 번호는 dropped에 나열되며 요금이 청구되지 않아요. 코드: no_country_code, too_short, too_long, not_a_number, wrong_length, not_covered, duplicate 및 suppressed(발송 제외 목록에 있는 경우).

발송 가져오기 또는 목록 보기

GET/api/v1/sends/{id}

GET/api/v1/sends?limit=25&before={id}

단일 발송에는 메시지 상태별 counts가 포함돼요. 목록은 최신순으로 정렬되며, 다음 페이지를 보려면 next_before를 before로 전달하세요. 워크스페이스에서의 발송도 여기에 표시되며, source: "web"로 표시돼요.

메시지

GET/api/v1/sends/{id}/messages?status=failed&limit=100

GET/api/v1/messages/{id}

{ "message": { "id": "MS3D8RW0K2QJ7N", "send_id": "CA7Q29XMAB4DFK", "to": "+447700900461", "country": "GB",
  "segments": 1, "price_usd": "0.00868", "status": "delivered", "reason": null, "reason_text": null,
  "created_at": "2026-11-07T10:12:03.120Z", "sent_at": "2026-11-07T10:12:04.010Z", "done_at": "2026-11-07T10:12:09.400Z" } }

발송 취소

POST/api/v1/sends/{id}/cancel

시작하지 않은 예약 발송만 취소할 수 있어요. 전체 비용이 잔액으로 환불되며 응답에 refunded_usd가 포함돼요.

웹훅

워크스페이스(API 및 웹훅)에 https URL을 설정하세요. 메시지가 전달되거나 실패할 때 이벤트를 POST로 보내요. API와 워크스페이스에서의 발송 모두에 적용돼요. 10초 이내에 2xx 상태로 응답하세요. 실패한 전송은 총 7번 재시도돼요: 즉시, 1분 후, 5분 후, 30분 후, 1시간 후, 2시간 후, 3시간 후. 워크스페이스는 각 시도와 그 상태 및 응답을 표시해요.

POST https://your-server.example/sms-events
X-SSO-Event: message.delivered
X-SSO-Delivery: evt_5a0c9e31d2b7f4a68c
X-SSO-Timestamp: 1794132729
X-SSO-Signature: sha256=8f1c...e42a

{ "id": "evt_5a0c9e31d2b7f4a68c", "event": "message.delivered", "created": "2026-11-07T10:12:09.000Z",
  "data": { "message": { "id": "MS3D8RW0K2QJ7N", "send_id": "CA7Q29XMAB4DFK", "to": "+447700900461", "country": "GB",
  "segments": 1, "price_usd": "0.00868", "status": "delivered", "reason": null, "reason_text": null,
  "client_ref": "bookings-1107", "created_at": "...", "sent_at": "...", "done_at": "..." } } }

서명은 timestamp + "." + raw body를 서명 비밀 키(sso_whk_로 시작)로 HMAC-SHA256한 값이에요. 파싱 전에 원본 본문으로 확인하고, 5분보다 오래된 타임스탬프는 거부하고, 전송 ID로 중복을 걸러 내세요.

import crypto from "node:crypto";

// rawBody: the request body exactly as received (a string), before JSON.parse
export function verify(rawBody, headers, secret) {
  const ts = headers["x-sso-timestamp"];
  const sig = headers["x-sso-signature"] || "";
  const want = "sha256=" + crypto.createHmac("sha256", secret).update(`${ts}.${rawBody}`).digest("hex");
  const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300;
  return fresh && sig.length === want.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(want));
}

이벤트: message.delivered, message.failed, 그리고 Send test event 버튼의 test가 있어요.

상태 및 이유

필드의미
queued발송 대기열에서 기다리고 있어요.
sent네트워크로 전달되었고, 전송 리포트를 기다리고 있어요. 대부분의 리포트는 몇 분 내에 도착하지만, 일부 네트워크는 최대 24시간이 걸릴 수 있어요.
delivered네트워크에서 휴대폰이 메시지를 받았다고 확인했어요.
failed전송되지 않았어요. 가격이 잔액으로 환불되었어요.
canceled취소해서 전송되지 않았어요. 환불되었어요.

실패 이유 (reason 필드):

필드의미
network_error네트워크 오류
absent휴대폰이 꺼져 있거나 서비스 영역 밖
not_in_service서비스 중이 아닌 번호
blocked네트워크나 수신자에 의해 차단됨
landlineSMS를 받을 수 없는 유선전화 또는 번호
content_filter네트워크의 콘텐츠 필터에 의해 거부됨
expired만료: 네트워크의 시간 제한 내에 리포트 없음
canceled전송 전 취소됨
unknown알 수 없는 네트워크 응답

서비스 점검은 상태 페이지에서 확인할 수 있고, API 변경 사항은 업데이트 내역에 있어요.

API 키 받기

계정을 만들고, 한 번 충전한 후 워크스페이스에서 API와 웹훅을 열어요. 동일한 키가 모든 국가에서 작동해요.

무료 계정 만들기
SMS API 문서: 코드로 대량 SMS 보내기 | SendSMSOnline