본문 바로가기
smsbulk

대량 문자 API v1

소프트웨어에서 대량 문자를 보내기 위한 작은 REST API예요. JSON으로 입력과 출력을 하고, Bearer 키를 사용하며, 콘솔과 동일한 가격과 확인 기능, 그리고 전송 리포트를 위한 서명된 웹훅을 제공해요. 기본 URL https://smsbulk.cc/rest/v1

개요

한 요청당 최대 8,000개의 번호로 보낼 수 있어요. 각 번호는 자신의 병합 값을 가지고 있으며, API는 각 수신자에게 병합된 텍스트를 기준으로 정확히 콘솔처럼 가격을 책정해요. 비용을 알기 위해 동일한 본문으로 먼저 견적을 요청하세요. 견적은 전송되거나 청구되지 않아요. 시간은 ISO 8601 형식의 UTC로, 금액은 소수점 다섯 자리의 미국 달러 문자열로 표시돼요.

curl https://smsbulk.cc/rest/v1/send \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "recipients": [
      { "to": "+14155550142", "fields": { "name": "Maria" } },
      { "to": "+447400123456", "fields": { "name": "Tom" } }
    ],
    "text": "Hi {{name}}, your order ships today.",
    "client_ref": "order-batch-17"
  }'

인증

콘솔의 API 및 웹훅 섹션에서 키를 생성하세요. 첫 번째 크레딧 충전 후 키가 활성화돼요. 모든 요청에 Bearer 토큰으로 키를 보내세요. 키는 한 번만 표시되며, 최대 5개의 키를 유지하고 언제든지 취소할 수 있어요.

Authorization: Bearer smsb_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

요금

공개적으로, 키가 필요 없어요. 모든 목적지 또는 하나의 ISO 코드에 대한 SMS 세그먼트당 가격을 제공해요.

GEThttps://smsbulk.cc/rest/v1/rates

GEThttps://smsbulk.cc/rest/v1/rates/{ISO}

{
  "country": "US",
  "name": "United States",
  "dial_code": "+1",
  "rate": "0.00485",
  "currency": "USD",
  "per": "message segment"
}

크레딧

GEThttps://smsbulk.cc/rest/v1/credit

{ "credit": "125.40000", "currency": "USD", "account": "A418820037715" }

견적

전송하지 않고 비용을 계산해요. Send와 동일한 본문을 사용해요.

POSThttps://smsbulk.cc/rest/v1/quote

필드의미
to한 개의 번호 또는 목록이에요. +가 포함된 국제 형식이 가장 안전해요. 지역 번호는 default_country가 필요해요.
recipientsto 대신 { to, fields } 목록을 사용해요. 여기서 fields는 {{name}}과 같은 병합 필드를 채워요.
text메시지는 병합 후 최대 2,000자 및 10세그먼트까지 가능해요.
default_country+ 없이 작성된 번호에 사용되는 ISO 코드예요. 예: GB. 설정에 저장된 기본값으로 돌아가요.
opt_out저장된 수신 거부 문구를 마지막 줄에 추가해요 (먼저 설정에 저장하세요).
{
  "recipients": 2,
  "messages": 2,
  "segments": { "min": 1, "max": 1 },
  "encoding": "GSM-7",
  "total": "0.01240",
  "currency": "USD",
  "credit": "125.40000",
  "credit_after": "125.38760",
  "countries": [
    { "country": "US", "recipients": 1, "rate": "0.00485", "segments": 1, "subtotal": "0.00485" },
    { "country": "GB", "recipients": 1, "rate": "0.00755", "segments": 1, "subtotal": "0.00755" }
  ],
  "left_out": []
}

보내기

캠페인을 생성하고, 크레딧에서 총액을 차감한 후 메시지를 큐에 넣어요. 캠페인과 함께 201 응답을 반환해요. 전송할 수 없는 번호는 left_out에 나열되며 청구되지 않아요.

POSThttps://smsbulk.cc/rest/v1/send

필드의미
to / recipients / text / default_country / opt_outQuote와 같아요.
send_at시작할 ISO 8601 시간이에요. 5분에서 90일 앞당길 수 있어요. 생략하면 지금 보내요.
name캠페인의 레이블이에요. 최대 80자까지 가능해요.
client_ref자신의 참조값이에요. 최대 64자까지 가능하며 모든 메시지와 웹훅에 반환돼요.
HTTP/1.1 201 Created
{
  "campaign": {
    "id": "C507318264409",
    "name": null,
    "status": "sending",
    "source": "api",
    "test": false,
    "recipients": 2,
    "cost": "0.01240",
    "refunded": "0.00000",
    "net_cost": "0.01240",
    "currency": "USD",
    "send_at": null,
    "created_at": "2026-11-07T14:02:11.000Z",
    "started_at": "2026-11-07T14:02:11.000Z",
    "finished_at": null,
    "client_ref": "order-batch-17"
  },
  "messages": 2,
  "left_out": []
}

캠페인

GEThttps://smsbulk.cc/rest/v1/campaigns?limit=25&before={id}

GEThttps://smsbulk.cc/rest/v1/campaigns/{id}

GEThttps://smsbulk.cc/rest/v1/campaigns/{id}/messages?status=failed&limit=100&after={message id}

단일 캠페인에는 큐 대기, 전송, 전달, 실패, 취소 상태별 카운트가 포함돼요. after와 has_more가 있는 메시지 목록 페이지는 페이지당 최대 1,000개까지 표시돼요.

메시지

GEThttps://smsbulk.cc/rest/v1/messages/{id}

GEThttps://smsbulk.cc/rest/v1/messages?to=%2B14155550142&status=delivered&client_ref=order-batch-17&limit=50

{
  "id": "M220947781305",
  "campaign": "C507318264409",
  "to": "+14155550142",
  "country": "US",
  "segments": 1,
  "encoding": "GSM-7",
  "price": "0.00485",
  "status": "delivered",
  "reason": null,
  "reason_text": null,
  "refunded": false,
  "client_ref": "order-batch-17",
  "created_at": "2026-11-07T14:02:11.000Z",
  "sent_at": "2026-11-07T14:02:12.000Z",
  "finished_at": "2026-11-07T14:02:16.000Z"
}

예약된 캠페인 취소

POSThttps://smsbulk.cc/rest/v1/campaigns/{id}/cancel

시작 시간까지 작동해요. 전체 비용이 하나의 원장 항목으로 크레딧에 반환되고 캠페인 상태가 취소로 변경돼요.

웹훅

콘솔에서 엔드포인트 URL을 설정하세요. message.delivered, message.failed, campaign.done에 대해 JSON을 POST하고, Send test event를 누르면 테스트도 함께 보내요. 각 이벤트는 네 개의 헤더를 가지고 있으며, 10초 이내의 2xx 응답은 수신된 것으로 간주돼요.

필드의미
Smsbulk-Event이벤트 이름이에요. 예: message.delivered.
Smsbulk-Delivery배송 ID예요. 하나의 이벤트를 재시도할 때마다 동일해요.
Smsbulk-Timestamp이 시도가 서명된 Unix 초예요.
Smsbulk-Signaturesha256= 뒤에 타임스탬프, 줄 바꿈 및 원본 본문의 base64 HMAC-SHA256이 와요. sbw_ 비밀 키로 서명돼요.

전송은 8번 시도돼요: 즉시, 그리고 1분, 5분, 15분, 1시간, 3시간, 6시간, 12시간 후에요. 각 시도는 콘솔 전송 로그에 HTTP 상태와 응답의 시작 부분과 함께 표시돼요.

{
  "id": "dlv_3f9a1c07b2e44d58a6c1",
  "event": "message.failed",
  "created": "2026-11-07T14:02:16.000Z",
  "data": {
    "message": "M220947781305",
    "campaign": "C507318264409",
    "to": "+447400123456",
    "country": "GB",
    "status": "failed",
    "reason": "not_in_service",
    "segments": 1,
    "price": "0.00755",
    "client_ref": "order-batch-17"
  }
}

서명을 확인해요

import crypto from "node:crypto";

// raw = the request body exactly as received (string), secret = your sbw_ signing secret
export function verify(raw, headers, secret) {
  const ts = headers["smsbulk-timestamp"];
  const want = "sha256=" + crypto.createHmac("sha256", secret).update(ts + "\n" + raw).digest("base64");
  const got = headers["smsbulk-signature"] || "";
  const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300;
  return fresh && got.length === want.length && crypto.timingSafeEqual(Buffer.from(got), Buffer.from(want));
}

오류 및 제한

에러는 HTTP 상태 코드와 JSON 본문으로 표시돼요: {"error":{"code":"insufficient_credit","message":"..."}}. 요청이 실패하면 요금이 청구되지 않아요.

필드의미
400 bad_json본문이 JSON이 아니에요.
401 unauthorized누락되었거나 잘못된 형식이거나 취소된 키예요.
402 insufficient_credit총 금액이 크레딧보다 많아요. 콘솔에서 충전해 주세요.
404 not_found이 계정에는 해당 엔드포인트, 캠페인, 메시지 또는 ISO 코드가 없어요.
409 not_scheduled이미 시작된 캠페인에 대해 취소 요청이 있었어요.
422 text_missing, text_too_long, too_many_segments메시지가 비어 있거나, 2,000자를 초과했거나, 수신자당 10 세그먼트를 초과했어요.
422 no_recipients, too_many, recipients_missing발송 가능한 번호가 없거나, 8,000개 이상의 번호가 있거나, to나 recipients 필드가 없어요.
422 schedule_range, bad_send_atsend_at이 5분에서 90일 사이가 아니거나 유효한 시간이 아니에요.
422 opt_out_unsetopt_out이 true지만 설정에 수신 거부 문구가 저장되지 않았어요.
429 rate_limited이 키로 분당 300회 이상의 요청이 있었어요.

모든 응답에는 X-RateLimit-Limit, X-RateLimit-Remaining 및 X-RateLimit-Reset이 포함돼요.

제외된 코드

가격 책정 전에 제외된 번호는 left_out에 반환돼요. 여기에는 목록에서의 위치, 보낸 값, 코드 및 간단한 이유가 포함돼요.

필드의미
no_country_code+가 없고 기본 국가도 설정되지 않았어요.
too_short / too_long8자 미만이거나 15자를 초과했어요.
not_a_number숫자가 아닌 글자나 기호가 포함되어 있어요.
nanp_length11자리가 아닌 +1 번호예요.
not_covered우리 목록에 없는 목적지예요.
duplicate같은 번호가 요청에 이미 있었어요.
suppressed이 번호는 발송 제외 목록에 있어요.

상태 및 이유

필드의미
queued큐에서 나가기를 기다리고 있어요.
sent네트워크에 전달되었고, 전송 리포트를 기다리고 있어요. 대부분 몇 분 내에 리포트가 도착하지만, 일부 네트워크는 최대 24시간이 걸릴 수 있어요.
delivered네트워크에서 휴대폰 수신을 확인했어요.
failed전송되지 않았어요. 가격이 크레딧으로 환불되었어요.
canceled취소해서 전송되지 않았어요. 환불되었어요.
필드의미
network_error네트워크 오류
switched_off휴대폰이 꺼져 있거나 커버리지 밖이에요
not_in_service서비스 중이 아닌 번호예요
blocked네트워크 또는 수신자에 의해 차단됨
landline문자를 받을 수 없는 유선전화 또는 번호
content_filter네트워크의 콘텐츠 필터에 의해 거부됨
expired만료됨: 네트워크 시간 제한 전에 리포트 없음
unknown알 수 없는 네트워크 응답
canceled발송 전 취소됨

API에 대해 궁금한 점이 있나요? 콘솔에서 티켓을 열거나 FAQ를 읽어보세요.

대량 문자 API v1 문서: 발송, 견적, 웹훅 | SMSBulk