API 문서
jiema.my에 대한 일괄 / 자동 액세스를 위한 REST API입니다. SMS 인증을 자체 서비스에 통합할 때 사용합니다. 모든 엔드포인트는 JSON을 반환합니다.
인증
모든 요청에는 API key가 포함되어야 합니다:
Authorization: Bearer jm_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
대체 헤더 형식: X-API-Key: jm_xxx…
/account/api-keys에서 key를 생성하고 폐기할 수 있습니다. 사용자당 최대 10개의 활성 key를 보유할 수 있습니다. 전체 token은 생성 시 단 한 번만 표시되므로 안전하게 보관해 주십시오.
오류 및 속도 제한
모든 오류는 JSON을 반환합니다:
{ "ok": false, "code": "RATE_LIMITED", "message": "..." }| HTTP | code | 의미 |
|---|---|---|
| 401 | AUTH_MISSING / AUTH_INVALID | key 없음 또는 폐기됨 |
| 403 | FORBIDDEN | 차단됨 또는 액세스 권한 없음 |
| 400 | BAD_REQUEST / INSUFFICIENT / NO_NUMBERS / … | 검증 또는 비즈니스 오류 |
| 404 | NOT_FOUND | order가 존재하지 않음 (또는 사용자의 것이 아님) |
| 429 | RATE_LIMITED | key별 스로틀링 |
| 500 | INTERNAL | 서버 오류 (자동 보고됨) |
속도 제한: key별 60s 슬라이딩 윈도우입니다. 쓰기 엔드포인트(orders, cancel, next-sms)는 10 req/min, 읽기 엔드포인트(account, services, prices, list)는 60 req/min입니다.
GET /v1/account
현재 잔액과 기본 프로필을 조회합니다.
curl https://jiema.my/api/v1/account \
-H "Authorization: Bearer jm_xxx"
# 200 OK
{ "ok": true, "data": {
"id": "clxxxxxxxxxxx",
"balanceCents": "1200", // string to preserve precision; $12.00
"referralCode": "abc123",
"displayName": "Alice",
"lang": "en"
}}GET /v1/services
재고가 있는 service 목록입니다. 서버 측에서 약 5분간 캐시됩니다.
curl https://jiema.my/api/v1/services -H "Authorization: Bearer jm_xxx"
{ "ok": true, "data": { "items": [
{ "code": "tg", "slug": "telegram", "name": "Telegram",
"minCostUsd": 0.18, "totalCount": 1234, "countryCount": 24 },
...
] }}order를 생성할 때 service 필드에 code(또는 친숙한 slug)를 사용하십시오.
GET /v1/countries
curl https://jiema.my/api/v1/countries -H "Authorization: Bearer jm_xxx"
{ "ok": true, "data": { "items": [
{ "id": 7, "slug": "my", "name": "Malaysia" },
...
] }}id는 upstream의 숫자 country id이고, slug는 SEO용 짧은 코드입니다(예: "my" / "us"). 두 형식 모두 country 필드로 동작합니다.
GET /v1/prices?service=tg
지정된 service의 국가별 가격과 재고입니다. 가격에는 운영팀이 설정한 마크업/할인이 이미 포함되어 있습니다.
curl "https://jiema.my/api/v1/prices?service=tg" -H "Authorization: Bearer jm_xxx"
{ "ok": true, "data": {
"service": "tg",
"items": [
{ "countryId": 7, "countrySlug": "my", "priceCents": "32", "count": 540 },
...
]
}}POST /v1/orders
SMS 인증 order를 생성합니다. 시스템이 upstream에서 전화번호를 확보하고 청구 금액을 잔액에서 차감합니다.
curl -X POST https://jiema.my/api/v1/orders \
-H "Authorization: Bearer jm_xxx" \
-H "content-type: application/json" \
-d '{ "service": "tg", "country": 7 }'
{ "ok": true, "data": {
"id": "ckxxxxx",
"status": "WAITING",
"service": "tg",
"country": "7",
"phone": "+60123456789",
"expiresAt": "2026-05-22T08:30:00.000Z",
"chargedCents": "32"
}}service / country 외에 선택 필드는 없습니다.
일반적인 오류 코드: INSUFFICIENT(충전 필요), NO_NUMBERS, BAD_SERVICE, BAD_COUNTRY.
GET /v1/orders/:id
status가 RECEIVED가 되고 smsBody가 null이 아닐 때까지 폴링하십시오.
curl https://jiema.my/api/v1/orders/ckxxxxx -H "Authorization: Bearer jm_xxx"
{ "ok": true, "data": {
"id": "ckxxxxx",
"status": "RECEIVED",
"phone": "+60123456789",
"smsBody": "Your verification code is 123456",
"smsHistory": [
{ "body": "Your verification code is 123456", "receivedAt": "..." }
],
"smsReceivedAt": "2026-05-22T08:12:34.000Z",
...
}}상태: WAITING(번호 활성, SMS 대기) · RECEIVED(코드 도착) · COMPLETED / FAILED / CANCELLED는 종료 상태입니다.
권장 폴링: 3–5초마다입니다. 이 엔드포인트 내부에서 upstream 동기화가 실행되므로 백그라운드 워커가 바쁘더라도 폴링으로 상태를 진행시킬 수 있습니다.
POST /v1/orders/:id/cancel
아직 코드를 받지 않은 order를 취소합니다. 잔액으로 전액 환불됩니다.
curl -X POST https://jiema.my/api/v1/orders/ckxxxxx/cancel \
-H "Authorization: Bearer jm_xxx"
{ "ok": true, "data": { "id": "ckxxxxx", "status": "CANCELLED" }}번호가 이미 어떤 SMS라도 수신했다면 CODE_RECEIVED, 상태가 동시에 변경되었다면 ALREADY_CHANGED로 거부됩니다.
POST /v1/orders/:id/next-sms
RECEIVED 이후에 호출하면, upstream이 번호를 계속 활성 상태로 유지하면서 다른 SMS를 기다리도록 합니다(활성 윈도우 내에는 추가 요금이 없습니다).
curl -X POST https://jiema.my/api/v1/orders/ckxxxxx/next-sms \
-H "Authorization: Bearer jm_xxx"
{ "ok": true, "data": { "id": "ckxxxxx", "status": "WAITING" }}GET /v1/orders
최근 order의 cursor 페이지네이션 목록입니다.
curl "https://jiema.my/api/v1/orders?limit=20" -H "Authorization: Bearer jm_xxx"
{ "ok": true, "data": {
"items": [ { "id": "ck...", "status": "RECEIVED", "service": "tg", ... }, ... ],
"nextCursor": "ck..." | null
}}선택 쿼리: status · limit 1–100(기본값 20) · cursor = 이전 페이지의 nextCursor.
일반적인 워크플로
- 웹 UI를 통해 충전 → GET /v1/account로 잔액을 확인합니다.
- service 선택: GET /v1/services와 GET /v1/prices?service=tg.
- order 생성: POST /v1/orders에 service + country를 전달합니다.
- data.phone을 읽고 클라이언트에서 upstream SMS를 트리거합니다(예: Telegram 가입에 붙여넣기).
- GET /v1/orders/:id를 3–5초마다 폴링합니다. status === "RECEIVED"가 되면 smsBody를 읽습니다.
- SMS가 잘못되었거나 두 번째 코드가 필요한 경우: POST /v1/orders/:id/next-sms → WAITING으로 돌아갑니다.
- 완료. SMS가 도착하기 전에 취소하려면: POST /v1/orders/:id/cancel로 전액 환불됩니다.