전자서명 API,
기존 시스템에 연동
문서 생성 → 서명 링크 발급 → 완료 이벤트 수신.
단 3번의 API 호출로 전자서명 흐름이 완성됩니다.
5분 만에 첫 서명 문서 만들기 (Hosted 플로우)
고객사 백엔드는 API Key로 호스티드 세션만 발급하고, 사용자가 SignPlug의 호스티드 페이지에서 PDF 업로드 · 서명 좌표 지정 · 발송까지 진행합니다. 좌표 계산이나 base64 인코딩이 필요 없습니다.
POST /documents를 참고하세요. Hosted 플로우와 동일하게 모든 환경에서 사용할 수 있습니다.
recipient_consent_confirmed: true와 send_notification: true가 필요합니다. email 또는 phone 중 하나 이상 필수입니다. 휴대폰만 등록한 열람자·검토자는 자동 알림이 없으므로 응답의 참여자 링크를 직접 전달해야 합니다. 참여자별 require_identity_verification으로 본인인증 여부를 지정합니다. 인증을 사용하는 서명자에게만 휴대폰 번호가 필요합니다. 이름 도장 생성은 수신자가 SignPlug 서명 앱에서 선택하는 기능이므로 문서 생성 API 요청 형식은 변경되지 않습니다.
sp_test_* 테스트 키는 KG이니시스 개발 본인확인 채널을 사용하며, 전자서명 생성은 무제한입니다. sp_live_* 라이브 키와 SignPlug 웹은 KG이니시스 운영 본인확인 채널을 사용합니다.
API Key 발급
이 페이지 상단의 API Key 발급 버튼을 클릭하면 누락된 키가 생성됩니다. SignPlug 로그인 후 테스트 키를 발급할 수 있으며, 라이브 키는 활성 결제수단 등록 후 발급됩니다. 키는 고객사 백엔드 서버 사이드에서만 사용하고 브라우저에 노출하지 마세요.
호스티드 세션 발급
고객사 백엔드에서 POST /hosted-sessions를 호출해 짧은 수명(기본 1시간) 세션 토큰과 hosted_url을 받습니다.
curl -X POST https://api.moram.net/signplug/v1/hosted-sessions \
-H "Authorization: Bearer sp_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"return_url": "https://your-app.com/contracts/12345/done",
"source_object_key": "pending-sources/018f6f3a-5f7e-7c9a-b111-6b4a7f3c9d22.pdf",
"source_file_type": "pdf",
"metadata": { "order_id": "12345" },
"defaults": {
"title": "공급계약서 2026-04",
"participants": [
{ "name": "홍길동", "phone": "010-1234-5678", "email": "hong@example.com", "role": "signer", "signing_order": 1 }
],
"options": {
"require_sms_verification": true,
"recipient_consent_confirmed": true,
"recipient_consent_note": "회원가입 약관 동의",
"send_notification": true
}
}
}'
응답 예시
{
"session_token": "wsh_Us0GyhIY4IrAXfwlO1uUZhnclHTboxGP",
"hosted_url": "https://signplug.moram.net/start?session_token=wsh_Us0GyhIY4IrAXfwlO1uUZhnclHTboxGP",
"environment": "test",
"expires_at": "2026-05-15T13:00:00Z"
}
사용자를 hosted_url로 리다이렉트
발급받은 hosted_url로 사용자를 보냅니다. 사용자는 SignPlug 호스티드 페이지에서 PDF를 업로드하고 서명 필드를 마우스로 배치한 뒤 발송합니다. 별도 Moram 로그인은 필요 없습니다.
session_token은 URL의 쿼리 파라미터로 전달되므로, 만료 시간이 지나면 자동 무효화됩니다. 동일 토큰으로 여러 문서를 만들 수도 있으니, 사용자별/거래별로 새 세션을 발급하는 것을 권장합니다.
return_url 콜백 처리
문서가 생성되면 SignPlug가 사용자 브라우저를 return_url로 돌려보내며, document_id와 status 쿼리 파라미터를 덧붙입니다.
https://your-app.com/contracts/12345/done
?document_id=a1b2c3d4-e5f6-7890-abcd-ef1234567890
&status=waiting
고객사는 이 콜백에서 document_id를 저장하고, 이후 서명 완료 등은 계정에 등록된 webhook으로 수신합니다. GET /documents/:document_id로 폴링해도 됩니다.
Base URL · 환경 구분
모든 환경이 동일한 Base URL을 사용하고, 환경은 요청 시 사용하는 API Key 프리픽스로만 구분됩니다.
https://api.moram.net/signplug/v1
| 키 프리픽스 | 환경 | 본인확인 | 과금 |
|---|---|---|---|
sp_test_* |
개발 · QA | 실제 발송 | 없음 (테스트 환경 본인확인 시도는 내부 정책 한도 내에서 제공) |
sp_live_* |
운영 | 실제 발송 | 발생. 활성 결제수단 등록 후 발급 및 사용 가능 |
관련 서비스 URL
| 서비스 | URL | 용도 |
|---|---|---|
| API | https://api.moram.net/signplug/v1 | 모든 REST API 호출 |
| 서명 앱 | https://sign.moram.net/signplug | 참여자가 서명을 진행하는 페이지 (서명 URL의 호스트) |
| 대시보드 | https://signplug.moram.net/dashboard | 키 발급, 결제 수단 등록, 사용량 확인 |
| 랜딩 | https://signplug.moram.net | 제품 소개, 가격, 문의 |
API Key 인증
모든 요청에 Authorization 헤더로 Bearer 토큰을 전달합니다.
# 개발/QA (KG이니시스 본인확인 테스트, 과금 없음)
Authorization: Bearer sp_test_xxxxxxxxxxxxxxxxxxxxx
# 운영 (KG이니시스 본인확인, 과금)
Authorization: Bearer sp_live_xxxxxxxxxxxxxxxxxxxxx
호스티드 세션 발급
고객사 백엔드가 API Key로 짧은 수명의 세션 토큰을 발급받고, 사용자를 SignPlug 호스티드 페이지로 리다이렉트합니다. 사용자는 그 페이지에서 PDF 업로드 · 좌표 지정 · 발송을 진행하며, 완료되면 return_url로 돌아옵니다.
/hosted-sessions
Request Body (모두 선택)
| 필드 | 타입 | 설명 |
|---|---|---|
return_url | string | 문서 생성 완료 후 사용자를 돌려보낼 URL. https://만 허용 (개발 시 localhost 예외). 비우면 호스티드 페이지에 머무름 |
metadata | object | 호스티드 세션 식별용 메타데이터. 생성된 문서의 metadata에 병합되어 webhook 등에서 활용 가능 |
source_object_key | string | POST /internal/source-upload로 먼저 업로드한 PDF 키. 전달하면 사용자가 hosted_url에 접속할 때 PDF가 자동 로드됩니다. |
source_file_type | string | 현재 개선형 Hosted 자동 로드는 pdf만 지원합니다. |
expires_in_minutes | integer | 세션 만료 시간(분). 5~240, 기본 60 |
defaults.title | string | 호스티드 페이지의 문서 제목 입력란 기본값 |
defaults.participants | array | 참여자 카드를 미리 채울 값. { name, phone, email, role, signing_order } 형식 |
defaults.options | object | expiration_hours, require_sms_verification, recipient_consent_confirmed, send_notification 등의 기본값. require_sms_verification은 호환용 필드명이며 참여자의 require_identity_verification 생략 시 적용되는 기본 인증 정책입니다. |
Response — 201 Created
{
"session_token": "wsh_Us0GyhIY4IrAXfwlO1uUZhnclHTboxGP",
"hosted_url": "https://signplug.moram.net/start?session_token=wsh_Us0GyhIY4IrAXfwlO1uUZhnclHTboxGP",
"environment": "test",
"expires_at": "2026-05-15T13:00:00.000Z"
}
호스티드 페이지에서의 동작
- 사용자가
hosted_url로 진입하면 자동으로 세션 토큰을 인식해 Moram SSO 없이 접근 가능합니다. defaults에 전달한 값이 문서 제목 · 참여자 카드에 미리 채워집니다.source_object_key가 있으면 PDF가 자동으로 미리보기 화면에 로드되고, 사용자는 업로드 없이 서명 위치만 배치합니다.source_object_key가 없으면 사용자가 PDF 업로드 후 서명 위치를 마우스로 배치하고 "전자서명 시작"을 누르면 문서가 생성됩니다.- 발송 전 수신자의 전자문서 수신 및 전자서명 진행 사전 동의 확인이 필요하며, 확인값은 문서 메타데이터에 기록됩니다.
- 생성 성공 시 SignPlug가 사용자 브라우저를
return_url?document_id=...&status=...로 자동 이동시킵니다. - 이 흐름으로 생성된 문서의 사용량/과금은 세션을 발급한 API Key 환경(test/live)을 따릅니다.
세션 정보 조회 (호스티드 페이지 내부 호출)
/hosted-sessions/current
호스티드 페이지가 세션 토큰을 직접 검증하고 defaults · return_url을 읽어오는 데 사용됩니다. Authorization: Bearer <session_token> 헤더로 호출합니다. 일반적으로 통합 코드에서 직접 호출할 필요는 없습니다.
연결된 PDF 조회 (호스티드 페이지 내부 호출)
/hosted-sessions/current/source.pdf
source_object_key가 연결된 호스티드 세션에서 PDF 미리보기를 렌더링하기 위해 사용합니다. Authorization: Bearer <session_token> 헤더가 필요하며, 연결된 PDF가 없으면 404 source_not_found를 반환합니다.
주요 오류
| HTTP | 코드 | 원인 |
|---|---|---|
400 | invalid_request | return_url이 https가 아니거나 URL 형식 오류 |
400 | unsupported_source_file_type | 호스티드 자동 로드가 지원하지 않는 파일 형식. 현재 pdf만 지원 |
401 | invalid_api_key | 세션 발급은 정식 API Key(sp_test_* 또는 sp_live_*)로만 가능 |
401 | invalid_hosted_session | 호스티드 세션 토큰이 만료/폐기된 경우 (호스티드 페이지 측 오류) |
403 | source_owner_mismatch | source_object_key가 다른 계정 또는 다른 환경에서 생성됨 |
409 | source_already_attached | 이미 다른 호스티드 세션에 연결된 업로드 키 |
409 | source_already_used | 이미 문서 생성에 사용된 업로드 키 |
410 | source_expired | 선업로드된 PDF가 만료됨. 다시 업로드 필요 |
문서 생성 (직접 API 호출)
서버 단일 호출로 PDF · 참여자 · 서명 좌표까지 한 번에 만들고 싶을 때 사용합니다. 좌표 계산을 직접 제어해야 하는 자동화 흐름에 적합합니다. UI 없이 즉시 문서를 만들고 싶지 않다면 위의 호스티드 세션이 더 간단합니다.
/documents
문서 파일, 참여자, 서명 필드를 한 번에 전달해 전자서명 문서를 생성합니다. 성공 시 참여자별 서명 URL을 즉시 반환합니다.
Request Body
| 필드 | 타입 | 설명 | |
|---|---|---|---|
title | string | 필수 | 문서 제목 (예: "공급계약서 2026-04") |
file_base64 | string | 둘 중 하나 | PDF 파일을 base64 인코딩한 문자열 (PDF 파일만 지원) |
source_object_key | string | 둘 중 하나 | POST /internal/source-upload로 먼저 업로드 후 받은 키 |
file_type | string | 필수 | pdf 고정. 다른 값은 400 unsupported_file_type |
participants | array | 필수 | 서명 참여자 목록 (아래 participants 참고). 서명자는 문서당 최대 10명 |
signature_fields | array | 필수 | PDF 내 서명 위치 목록 (아래 signature_fields 참고) |
coordinate_origin | string | 선택 | 좌표 원점. 기본값은 top_left이며 기존 좌하단 좌표 연동은 bottom_left 지정. |
options | object | 필수 | 만료 시간, 리마인더, 본인확인, 수신자 사전 동의 확인 등 옵션. recipient_consent_confirmed는 true가 필요합니다. |
metadata | object | 선택 | 내부 참조용 메타데이터. 생략하면 {}로 처리 |
participants[ ] 필드
role이 signer인 3명째 서명자부터 1명당 100원(VAT 포함)이 추가되며, 한 문서의 서명자는 최대 10명입니다.
| 필드 | 타입 | 설명 | |
|---|---|---|---|
name | string | 필수 | 참여자 표시 이름 |
require_identity_verification | boolean | 선택 | 서명자별 본인인증. false이면 이메일 링크로 본인인증 없이 서명합니다. 생략 시 options.require_sms_verification(기본 true)을 상속합니다. 요청자가 발송 전에 정하며 수신자가 변경할 수 없습니다. 인증 미실시는 감사기록과 인증서에 구분됩니다. |
phone | string|null | 조건부 필수 | 휴대폰 번호. 이메일이 없거나 본인인증을 사용하는 서명자는 필수입니다. 국내 휴대폰 번호를 입력하세요. 번호가 없으면 알림톡 작업을 생성하지 않습니다. |
email | string|null | 조건부 필수 | 휴대폰과 둘 중 하나 이상 필수. 이메일을 등록하면 요청·완료 안내와 완료 문서·감사추적 인증서를 이메일로 받습니다. |
role | string | 필수 | signer · viewer · approver. signer 역할만 추가 서명자 과금과 최대 10명 제한 대상입니다. |
signing_order | integer | 필수 | 서명 순서 (1부터 시작). 동시 서명은 동일 숫자 지정 |
signature_fields[ ] 필드
| 필드 | 타입 | 설명 | |
|---|---|---|---|
participant_index | integer | 필수 | participants 배열 기준 인덱스 (0부터 시작) |
page | integer | 필수 | 페이지 번호 (1부터 시작) |
x, y | number | 필수 | PDF 기준 좌표 (포인트 단위, 기본 좌상단 원점). 변환 문서는 변환된 PDF 페이지 기준 |
width, height | number | 필수 | 필드 크기 (포인트 단위) |
type | string | 필수 | signature · name · date |
options 필드
| 필드 | 타입 | 예시값 | 설명 |
|---|---|---|---|
expiration_hours | integer | 72 | 서명 링크 만료 시간 (시간 단위) |
reminder_intervals_hours | array | [] | 리마인더 발송 시점 (시간 단위 배열). 리마인더가 없으면 생략하거나 [] 전달 |
require_sms_verification | boolean | true | 문서의 기본 본인인증 정책. 기본값 true이며 false도 허용합니다. 참여자의 require_identity_verification 값이 우선합니다. 기존 문서에는 기존 정책이 유지됩니다. |
recipient_consent_confirmed | boolean | true | 서명 요청 수신자가 전자문서 수신 및 전자서명 진행에 사전 동의했음을 발송자가 확인했는지 여부. 문서 생성 시 true가 필요합니다. |
recipient_consent_note | string|null | null | 사전 동의 확인 경로나 근거 메모. 예: "회원가입 약관 동의", "계약 담당자 이메일 회신". |
send_notification | boolean | true | 이메일 안내 발송 여부. 이메일이 있으면 이메일을, 휴대폰을 입력한 서명자에게는 알림톡을 발송합니다. 열람자·검토자는 이메일이 없으면 자동 알림이 없습니다. false는 허용되지 않습니다. |
Response — 201 Created
{
"document_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "waiting",
"created_at": "2026-04-23T10:00:00Z",
"expires_at": "2026-04-26T10:00:00Z",
"converted_pdf_url": null,
"signing_urls": [
{
"name": "홍길동",
"participant_index": 0,
"url": "https://sign.moram.net/signplug/s/TOKEN"
}
]
}
파일 업로드 (대용량)
파일이 크거나 base64 변환이 불편할 때는 먼저 파일을 업로드하고 반환된 키를 POST /documents에 사용합니다. multipart/form-data의 file 필드로 최대 50MB까지 업로드할 수 있습니다.
/internal/source-upload
curl -X POST https://api.moram.net/signplug/v1/internal/source-upload \
-H "Authorization: Bearer sp_live_YOUR_KEY" \
-F "file=@contract.pdf" \
-F "file_type=pdf"
Request
| 필드 | 위치 | 설명 | |
|---|---|---|---|
Authorization | header | 필수 | Bearer sp_test_* 또는 Bearer sp_live_* |
file | multipart | 필수 | 업로드할 파일. 최대 50MB |
file_type | multipart | 선택 | pdf 고정. 생략하면 파일명 확장자(.pdf)로 자동 추론. 다른 형식은 400 unsupported_file_type |
Response — 201 Created
{
"account_id": "9f2c7c3e-1111-4444-9999-1c4f7a8b9d10",
"source_object_key": "pending-sources/018f6f3a-5f7e-7c9a-b111-6b4a7f3c9d22.pdf",
"bucket_name": "signplug-documents",
"file_type": "pdf",
"original_filename": "contract.pdf",
"storage_provider": "s3",
"expires_at": "2026-05-20T10:00:00.000Z",
"size": 123456,
"sha256": "e3b0c44298fc1c149afb..."
}
응답 필드
| 필드 | 설명 |
|---|---|
source_object_key | POST /hosted-sessions 또는 POST /documents의 source_object_key 필드에 그대로 전달 |
expires_at | 선업로드 키 만료 시각. 만료 후에는 다시 업로드해야 합니다. |
sha256 | 업로드된 파일 원본의 SHA-256 해시. 클라이언트에서 무결성을 검증할 때 사용 |
size | 바이트 단위 파일 크기 |
storage_provider | s3 또는 minio. 디버깅용 정보 |
주요 오류
| HTTP | 코드 | 원인 |
|---|---|---|
400 | invalid_request | file 필드 없음 · 빈 파일 · file_type이 pdf가 아님(비-PDF 파일은 unsupported_file_type) |
413 | FST_REQ_FILE_TOO_LARGE | 업로드 파일이 50MB(52,428,800 bytes)를 초과 |
401 | invalid_api_key | API Key 누락 또는 잘못된 키 |
source_object_key는 동일 계정의 POST /hosted-sessions에 연결하거나 POST /documents 호출에서 사용할 수 있습니다. 한 번 문서 생성에 사용되면 정식 문서 스토리지로 이동합니다. 사용되지 않은 키는 기본 24시간 후 만료되므로 발급 후 가급적 빠르게 사용하세요.
AI 문서 검토
문서 생성 전에 요약·위험 요소·참여자별 의무를 검토합니다. 문서·서명 링크를 만들거나 결제·알림을 발송하지 않습니다. AI 응답은 참고 자료이며 법률 자문이나 정확성 보증이 아닙니다.
/internal/document-reviewAI 검토는 Codex CLI를 통해 OpenAI에서 처리됩니다. 고객사는 호출 전에 문서 텍스트·제목·참여자 이름과 역할의 외부 전송을 이용자에게 안내하고 필요한 동의를 확보해야 합니다. 원본 파일과 참여자 연락처 필드는 AI 제공자에게 전송하지 않습니다.
고객사 서버에서 Authorization: Bearer sp_test_... 또는 sp_live_...로 호출합니다. 일반 호스티드 세션도 허용되지만 체험 전용 세션은 사용할 수 없습니다. 요청 형식은 multipart/form-data, 최대 파일 크기는 50MB입니다.
| 필드 | 설명 |
|---|---|
file (필수) | 검토 파일. 현재 웹 업로드·문서 생성과 같은 PDF 사용을 권장합니다. |
file_type | pdf 사용, 생략하면 확장자로 추론합니다. 검토 경로는 호환 목적으로 doc, docx, hwp, hwpx도 인식하지만 추출·변환 구성에 따라 실패할 수 있습니다. 문서 생성·선업로드 API는 PDF만 지원합니다. |
title | 검토 제목. 생략하면 파일명 사용. |
participants (필수) | JSON 배열 문자열. 비어 있지 않은 name이 필요합니다. role, email, phone, signing_order는 선택입니다. 앞의 유효한 참여자 최대 10명을 검토합니다. |
curl -X POST https://api.moram.net/signplug/v1/internal/document-review \
-H "Authorization: Bearer sp_test_YOUR_KEY" \
-F "file=@contract.pdf" \
-F "file_type=pdf" \
-F "title=공급계약서" \
-F 'participants=[{"name":"담당자","role":"signer"}]'응답 — 200 OK
{
"status": "completed",
"generated_at": "2026-09-26T00:00:00.000Z",
"model": "gpt-6.1-sol", "provider": "codex-cli",
"document": { "title": "공급계약서", "file_type": "pdf", "original_filename": "contract.pdf", "extracted_characters": 1200, "truncated": false },
"review": {
"summary": ["계약 내용 요약"], "key_terms": ["지급 기한"],
"risks": ["지연 책임 확인 필요"], "checklist": ["금액과 지급일 확인"],
"participant_reviews": [{ "participant_index": 0, "name": "담당자", "role": "signer", "summary": "당사자 관점 요약", "obligations": [], "concerns": [], "questions": [] }],
"privacy_note": "문서 내용의 일시 처리에 관한 안내"
}
}model은 운영 설정에 따라 달라집니다. 긴 문서는 일부만 검토하므로 document.truncated를 확인하세요. 여기서 completed는 AI 검토 완료이며 전자서명 완료가 아닙니다.
| HTTP | 코드 | 처리 |
|---|---|---|
| 400 | invalid_request / invalid_participants | 파일·형식·참여자 JSON 확인 |
| 401 / 403 | invalid_api_key / trial_scope_restricted | 유효한 일반 API Key 사용 |
| 413 | FST_REQ_FILE_TOO_LARGE | 50MB 이하 파일 사용 |
| 422 | document_text_not_available / ai_review_timeout | 텍스트를 읽을 수 있는 PDF 사용 또는 문서 분할 |
| 503 | ai_review_disabled / ai_review_unconfigured / ai_review_auth_unavailable | 현재 AI 검토 기능 비활성 또는 인증 확인 필요 |
| 429 | ai_review_busy | 검토 대기열이 가득 찼습니다. 잠시 후 재시도 |
| 502 / 503 | ai_review_provider_error / ai_review_invalid_response | 잠시 후 재시도. 반복되면 request_id와 함께 문의 |
문서 목록 조회
계정의 문서를 최신 생성순으로 조회합니다. 대시보드와 운영 모니터링에서 사용하는 API입니다.
/documents?page=1&limit=20&status=completed
쿼리 파라미터
| 쿼리 | 타입 | 기본값 | 설명 |
|---|---|---|---|
page | integer | 1 | 1부터 시작하는 페이지 번호 |
limit | integer | 20 | 페이지당 항목 수. 1~100 사이 |
status | string | - | 특정 상태만 필터. 가능한 값: draft, waiting, in_progress, completed, rejected, expired, cancelled |
curl "https://api.moram.net/signplug/v1/documents?page=1&limit=20&status=completed" \
-H "Authorization: Bearer sp_live_YOUR_KEY"
Response — 200 OK
{
"documents": [
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"title": "공급계약서 2026-04",
"status": "completed",
"environment": "live",
"source_file_type": "pdf",
"created_at": "2026-04-23T10:00:00.000Z",
"expires_at": "2026-04-26T10:00:00.000Z",
"completed_at": "2026-04-23T11:42:10.000Z"
},
{
"id": "0b1c2d3e-4f56-7890-abcd-ef1234567891",
"title": "비밀유지서약서",
"status": "waiting",
"environment": "live",
"source_file_type": "pdf",
"created_at": "2026-04-22T09:00:00.000Z",
"expires_at": "2026-04-25T09:00:00.000Z",
"completed_at": null
}
],
"total": 47,
"page": 1,
"limit": 20
}
(page * limit) < total로 판단합니다. 대시보드용 무한 스크롤은 page를 증가시키며 호출하세요. environment 필드로 test/live 문서를 구분할 수 있습니다.
문서 상태 조회
/documents/:document_id
curl https://api.moram.net/signplug/v1/documents/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
-H "Authorization: Bearer sp_live_YOUR_KEY"
Response — 200 OK
{
"document_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"title": "공급계약서 2026-04",
"status": "in_progress",
"created_at": "2026-04-23T10:00:00.000Z",
"completed_at": null,
"metadata": { "order_id": "12345" },
"participants": [
{ "name": "홍길동", "status": "signed", "signed_at": "2026-04-23T10:14:32.000Z" },
{ "name": "김영희", "status": "verified", "signed_at": null }
]
}
참여자별 status는 pending · viewed · verified · signed · rejected · expired 중 하나이며, 문서의 status와는 별도 흐름입니다.
/documents/:document_id/pdf
완료된 문서의 최종 PDF를 application/pdf로 내려받습니다. 브라우저 표시용 Content-Disposition: inline 헤더가 포함되어 있어, 그대로 <iframe>이나 새 탭에 표시할 수 있습니다. 파일을 강제로 저장하게 하려면 응답을 그대로 전달하지 말고 자체 프록시에서 attachment로 다시 내려주세요.
문서가 completed 상태가 아니거나 최종 PDF가 없으면 409 document_pdf_not_ready가 반환됩니다. 원본·중간 PDF로 대체하지 않습니다. 서명 중 미리보기는 서명 앱의 /public/signing-links/:token/pdf를 사용합니다.
/documents/:document_id/audit-log
문서 해시와 열람·본인확인·서명·거절·취소 이벤트를 시간순으로 반환합니다.
Response — 200 OK
{
"document_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"document_hash": "8b1a9953c4611296a827abf8c47804d7...",
"events": [
{
"event_type": "document.viewed",
"timestamp": "2026-04-23T10:08:15.000Z",
"participant_name": "홍길동",
"ip_address": "203.0.113.42",
"user_agent": "Mozilla/5.0 ...",
"verification_method": null
},
{
"event_type": "document.verified",
"timestamp": "2026-04-23T10:09:01.000Z",
"participant_name": "홍길동",
"ip_address": "203.0.113.42",
"user_agent": "Mozilla/5.0 ...",
"verification_method": "portone_inicis_unified"
},
{
"event_type": "document.signed",
"timestamp": "2026-04-23T10:14:32.000Z",
"participant_name": "홍길동",
"ip_address": "203.0.113.42",
"user_agent": "Mozilla/5.0 ...",
"verification_method": "canvas"
}
]
}
/documents/:document_id/audit-certificate.pdf
완료된 문서의 감사추적 인증서를 PDF로 내려받습니다. 인증서에는 문서 해시(원본·변환·최종), 참여자별 본인확인/서명 일시, IP, 본인확인 방법이 포함되어 분쟁 대응 자료로 활용할 수 있습니다. 문서가 completed 상태가 아니면 인증서가 생성되지 않습니다.
/documents/:document_id
아직 완료되지 않은 문서를 취소합니다. 진행 중인 서명 링크는 즉시 무효화되며, 등록된 webhook에 document.cancelled 이벤트가 발송됩니다. completed · expired · rejected · 이미 cancelled 상태인 문서에 호출하면 409 invalid_state가 반환됩니다.
Response — 200 OK
{
"document_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "cancelled",
"cancelled_at": "2026-04-23T15:42:10.000Z"
}
status 값
| 값 | 설명 |
|---|---|
draft | 문서 준비 중(현재 흐름에서는 사용되지 않음). 이 상태에서는 /pdf 호출이 409 |
waiting | 모든 참여자가 아직 서명하지 않음. 서명 링크 유효 |
in_progress | 일부 참여자가 서명 완료. 순차 서명일 경우 다음 차례 참여자에게 알림 발송 |
completed | 모든 참여자 서명 완료. /pdf, /audit-certificate.pdf 모두 다운로드 가능 |
rejected | 참여자 중 한 명이 서명을 거절. 후속 참여자 알림은 발송하지 않음 |
expired | 만료 기간 초과. 서명 링크 사용 불가 |
cancelled | DELETE 호출로 취소됨 |
서명 링크 · 참여자 경험
문서 생성 응답의 signing_urls[].url을 참여자에게 그대로 전달하면 SignPlug 서명 앱이 설정에 따른 본인인증과 서명 제출, 완료 안내까지 전 과정을 처리합니다. 아래 /public/signing-links/* 엔드포인트는 서명 앱이 내부적으로 사용하므로 일반 통합에서는 직접 호출할 필요가 없습니다.
서명 URL 형식
https://sign.moram.net/signplug/s/<TOKEN>
토큰은 문서 생성 시 참여자별로 발급되며, 만료 시간이 지나면 자동 무효화됩니다. SignPlug 서명 앱은 모바일·태블릿·데스크탑 화면과 언어·테마 선택을 지원합니다. 본인인증을 사용하는 서명자에게만 서버에서 인증 결과를 확인한 뒤 서명을 허용합니다. 참여자는 서명 입력 시 직접 서명, 도장/이미지 업로드, 이름 도장 생성 중 하나를 선택할 수 있습니다.
참여자에게 안내 메시지를 직접 보낼 때 예시
안녕하세요, [회사명]입니다.
"[문서 제목]" 전자서명 요청을 보내드립니다.
▶ 서명하러 가기: https://sign.moram.net/signplug/s/abcd1234...
· 본인인증 사용 설정인 서명자는 인증 완료 후, 미사용 설정인 서명자는 동의 후 서명할 수 있습니다.
· 링크는 발송일로부터 72시간 동안 유효합니다.
서명 앱 내부 엔드포인트 (참고용)
자체 서명 UI를 구축하는 특수한 케이스가 아니라면 호출할 필요가 없습니다. 인증은 token 기반이며 별도의 API Key를 요구하지 않습니다.
| 엔드포인트 | 용도 |
|---|---|
GET /public/signing-links/:token | 문서 정보, 참여자 상태·역할(participant.role), 서명 필드, 워크플로 상태(is_current_turn, is_signer 등) 조회. 열람자·검토자는 is_signer=false 이며 본인확인·서명·거절이 제공되지 않습니다 |
GET /public/signing-links/:token/pdf | 서명 대상 PDF 다운로드 (inline) |
GET /public/signing-links/:token/preview-pages | 모바일 미리보기용 이미지 페이지 생성 |
POST /public/signing-links/:token/identity-verification/prepare | KG이니시스 본인확인 요청에 필요한 store_id, channel_key, identity_verification_id를 발급 |
POST /public/signing-links/:token/identity-verification/confirm | KG이니시스 본인확인 결과를 서버에서 조회하고 participant.status = verified 처리 |
POST /public/signing-links/:token/signature | 서명 이미지 제출 및 문서 완료. body: { agree_terms, method, signature_base64, signer_name } |
POST /public/signing-links/:token/reject | 서명 거절. body: { reason } |
서명 입력 방식
method | 설명 |
|---|---|
canvas | 참여자가 서명 앱에서 직접 그린 서명 |
image_upload | 참여자가 업로드한 도장 또는 서명 이미지 |
generated_stamp | 참여자가 이름을 입력해 서명 앱에서 생성한 전자도장. 일반 문서 생성 API 요청 형식은 변경되지 않습니다. |
window.open(url, '_blank')으로 새 창에 띄우거나 사용자를 해당 URL로 리다이렉트하는 패턴을 권장합니다.
서명자 연락처 정정
같은 서명자의 잘못 입력된 연락처를 정정하고 새 서명 요청을 발송합니다. API Key의 계정과 test/live 환경이 문서와 일치해야 합니다. 체험 세션은 사용할 수 없습니다. 다른 사람으로 서명자를 교체하는 용도가 아닙니다.
/documents/:document_id/details문서 상태와 participants의 현재 email·phone·phone_corrected·email_corrected·can_correct_phone·can_send, contact_corrections 정정 이력 및 additional_emails 기존 발송 이력을 조회합니다. 서명자 ID는 participants[].id를 사용합니다. can_send는 서명 전 상태인지 나타내며 이메일 발송 한도·대기 시간 충족을 보장하지 않습니다. can_correct_phone도 발송 중 충돌까지 보장하지 않으므로 POST 결과를 확인하세요.
/documents/:document_id/participants/:participant_id/correct-emailcurl -X POST https://api.moram.net/signplug/v1/documents/DOCUMENT_ID/participants/PARTICIPANT_ID/correct-email \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"email":"signer@example.com","request_id":"a0ca078e-8069-4fe1-8f22-48e0b4aa84b0","confirm_recipient":true}'등록된 참여자 이메일도 함께 정정됩니다. 서명자당 최소 60초 간격, 최근 24시간 5회·계정 전체 20회까지 허용됩니다. 기존 추가 이메일 발송도 한도에 포함됩니다. 이메일 정정은 추가 유료 알림톡을 발송하지 않습니다.
/documents/:document_id/participants/:participant_id/correct-phonecurl -X POST https://api.moram.net/signplug/v1/documents/DOCUMENT_ID/participants/PARTICIPANT_ID/correct-phone \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"phone":"01012345678","request_id":"bf817ac0-eb39-467c-821a-19c65f2a8726","confirm_recipient":true}'휴대폰 번호는 서명자별 서명 전 1회만 정정할 수 있으며 24시간 후에도 초기화되지 않습니다. 등록 번호와 본인인증 시 비교하는 번호가 함께 바뀝니다. 국내 휴대폰 번호를 사용합니다. 새 번호 알림톡 발송을 예약하며 등록 이메일이 있으면 새 링크도 이메일로 발송합니다. 순차 서명에서는 알림톡이 해당 서명자 차례까지 대기합니다. 발송 실패·결과 불명이어도 1회 정정은 소모됩니다.
요청과 응답
각 요청은 email 또는 phone 하나와 UUID 형식 request_id, JSON boolean confirm_recipient: true만 보냅니다. 동일 서명자의 연락처임을 확인한 뒤 호출하세요. 추가 필드는 거절합니다. 성공은 201, 동일 UUID·동일 내용의 재요청은 추가 발송 없이 200을 반환합니다. 동일 UUID를 다른 내용에 재사용하면 409 request_conflict입니다.
{"correction_id":"5ea86f77-c889-4f39-a90f-bdb89d7c84fb","email_status":"accepted","duplicate":false}email_status: accepted는 메일 서버 접수이며 실제 수신·열람을 보장하지 않습니다. mocked는 테스트 발송, not_applicable은 등록 이메일 없음, unknown은 메일 발송 결과 불명입니다. 이는 알림톡의 수신 상태가 아닙니다. 결과 불명이나 타임아웃에는 새 UUID로 자동 재발송하지 말고 같은 UUID로 결과 확인 후 상세 이력을 조회하세요.
정정 조건과 서명 링크
만료되지 않은 waiting·in_progress 문서의 signer이며 pending·viewed·verified 상태인 참여자만 가능합니다. 완료·취소·거절·만료 문서나 이미 서명한 참여자는 정정할 수 없습니다. PDF 원문·문서 해시·서명자 이름·본인인증 필요 여부는 유지됩니다.
정정하면 해당 서명자의 기존 활성 링크와 OTP가 무효화되고 인증 상태가 초기화됩니다. 서명자는 새 링크에서 필요 시 다시 본인인증해야 합니다. 이미 보낸 메시지는 회수되지 않습니다. 원래 링크를 저장한 연동 화면도 새 링크로 진행하도록 안내하세요.
오류와 이전 API 전환
400: 입력 오류·동일 연락처. 401: 인증 실패. 403: 체험 세션 제한. 404: 문서·서명자 없음 또는 다른 계정/환경. 409 document_not_active·participant_not_pending: 정정 불가 상태. 409 contact_correction_used: 휴대폰 1회 사용. 409 notification_in_flight: 알림톡 발송 중. 429 email_send_limit: 이메일 간격 또는 24시간 한도 초과. 429에 고정 Retry-After 값은 제공하지 않으며 즉시 반복 호출하지 마세요.
이전 POST .../request-email은 연락처를 변경하지 않고 다른 주소에 발송하는 기능이었습니다. 현재는 데이터 변경 없이 409 contact_correction_refresh_required를 반환합니다. 정정에 대한 사용자 확인을 받은 뒤 correct-email로 전환하세요. 경로만 자동 치환하면 안 됩니다. 전용 연락처 정정 웹훅은 제공하지 않으며 필요하면 상세 조회와 감사 로그를 사용하세요.
서명 진행 이벤트 수신
문서 생성, 열람, 본인확인, 서명, 완료, 거절, 만료, 취소 이벤트를 등록한 URL로 수신합니다.
/webhooks
Webhook 등록
curl -X POST https://api.moram.net/signplug/v1/webhooks \
-H "Authorization: Bearer sp_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://yourapp.com/webhooks/signplug",
"events": ["document.created", "document.signed", "document.completed"],
"timeout_seconds": 10
}'
운영 환경에서는 https:// URL만 등록할 수 있습니다. secret을 생략하면 whsec_* 값이 자동 발급되어 응답에 한 번 표시됩니다.
동일 계정에 같은 URL의 활성 webhook이 이미 있으면 409 webhook_already_exists가 반환됩니다.
Webhook 비활성화
/webhooks/:webhook_id
curl -X DELETE https://api.moram.net/signplug/v1/webhooks/WEBHOOK_ID \
-H "Authorization: Bearer sp_live_YOUR_KEY"
Webhook은 이력 보존을 위해 실제 삭제하지 않고 비활성화됩니다. 비활성화된 webhook에는 새 이벤트가 발송되지 않습니다.
수신 Payload 예시
POST https://yourapp.com/webhooks/signplug
Content-Type: application/json
X-SignPlug-Delivery-Id: 018f6f3a-5f7e-7c9a-b111-6b4a7f3c9d22
X-SignPlug-Event: document.completed
X-SignPlug-Signature: t=2026-04-23T15:42:10.000Z,v1=...
X-SignPlug-Timestamp: 2026-04-23T15:42:10.000Z
{
"event_type": "document.completed",
"event_key": "document.completed:a1b2c3d4-e5f6-7890-abcd-ef1234567890:018f6f3a",
"occurred_at": "2026-04-23T15:42:10.000Z",
"data": {
"document": {
"document_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"environment": "live",
"metadata": { "source": "portal" },
"status": "completed",
"title": "테스트 계약서"
},
"participant": null,
"event_data": {
"signed_participants": 2
}
}
}
이벤트 종류
| 이벤트 | 발생 시점 |
|---|---|
document.created | 문서 생성 완료 |
document.viewed | 참여자가 서명 링크 열람 |
document.verified | 참여자가 본인확인 완료 |
document.signed | 참여자 한 명이 서명 완료. data.event_data.method는 canvas, image_upload, generated_stamp 중 하나입니다. |
document.completed | 모든 참여자 서명 완료 |
document.rejected | 참여자가 서명 거절 |
document.expired | 서명 링크 만료 |
document.reminded | 리마인더 발송 |
document.cancelled | 문서 취소 |
HMAC 서명 검증 (Python)
import hashlib, hmac
def verify_webhook(payload_bytes: bytes, signature_header: str, secret: str) -> bool:
parts = dict(item.split("=", 1) for item in signature_header.split(",") if "=" in item)
timestamp = parts.get("t")
received = parts.get("v1")
if not timestamp or not received:
return False
signed_payload = timestamp.encode() + b"." + payload_bytes
expected = hmac.new(secret.encode(), signed_payload, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, received)
# Flask 예시
@app.route("/webhooks/signplug", methods=["POST"])
def webhook():
sig = request.headers.get("X-SignPlug-Signature", "")
if not verify_webhook(request.get_data(), sig, WEBHOOK_SECRET):
return "Unauthorized", 401
event = request.json
if event["event_type"] == "document.completed":
# 완료 처리 로직
pass
return "OK", 200
운영 · 과금 · 키 관리
테스트 키는 카드 없이 사용할 수 있습니다. 라이브 키는 활성 결제수단 등록 후 발급·재발급할 수 있으며, 미등록 상태에서 라이브 문서 생성을 시도하면 402 payment_method_required가 반환됩니다. 결제수단이 있어도 문서 생성 시점의 카드 승인이 거절되면 402 card_declined가 반환되고 문서는 생성되지 않습니다.
API Key 관리
| 엔드포인트 | 용도 |
|---|---|
POST /api-keys | 누락된 API Key 발급. 테스트 키는 카드 없이 발급되며, 라이브 키는 활성 결제수단 등록 후 발급 |
GET /api-keys | 계정의 test/live API Key 목록 조회. 키 원문은 재표시되지 않으며 key_prefix(앞 7~12자)로 구분합니다. |
POST /api-keys/:api_key_id/rotate | 기존 키를 비활성화하고 같은 환경의 새 키를 발급. 라이브 키 재발급은 활성 결제수단 필요 |
POST /api-keys/:api_key_id/disable | 키 비활성화. 환경별 마지막 활성 키는 먼저 rotate 해야 함 |
GET /api-keys 응답 예시
{
"api_keys": [
{
"id": "5f7a1c2b-1111-4444-9999-1c4f7a8b9d10",
"name": "Default Test Key",
"environment": "test",
"key_prefix": "sp_test_",
"is_active": true,
"is_current": true,
"created_at": "2026-04-23T10:00:00.000Z",
"last_used_at": "2026-04-23T15:42:10.000Z",
"expires_at": null
},
{
"id": "6a8b2d3c-2222-5555-aaaa-2d5f8b9caef1",
"name": "Default Live Key",
"environment": "live",
"key_prefix": "sp_live_",
"is_active": true,
"is_current": true,
"created_at": "2026-04-23T10:00:00.000Z",
"last_used_at": null,
"expires_at": null
}
]
}
POST /api-keys/:id/rotate 응답 예시
{
"api_key_id": "8c9d4e5f-3333-6666-bbbb-3e6f9cadef02",
"api_key": "sp_live_AbCdEf...전체키...XyZ",
"name": "Default Live Key",
"environment": "live",
"created_at": "2026-05-15T10:00:00.000Z"
}
api_key 원문은 이 응답에서만 확인할 수 있으므로 즉시 안전한 저장소(서버 시크릿 매니저 등)에 보관하세요. 회전 후 기존 키는 자동으로 비활성화됩니다.
결제 · 사용량
| 엔드포인트 | 용도 |
|---|---|
GET /billing/method | 현재 활성 결제 수단 조회 (없으면 null) |
PATCH /billing/method/card-display | 기존 국내 카드의 표시용 끝 4자리 저장 |
GET /billing/usage?year=YYYY&month=M | 해당 월의 문서 수, API/웹 분리 카운트, 청구 금액, 청구 상태 조회 |
GET /billing/outstanding | 아직 청구되지 않은 누적 사용량과 금액. 문서마다 서명 시작 때 결제되므로 항상 0입니다(이전 버전 호환용) |
GET /billing/web-entitlement | 결제 수단 등록 필요 여부와 웹 문서 사용 건수 |
GET /billing/config | KG이니시스/Paddle 결제 설정 가능 여부 조회 (인증 불필요) |
GET /billing/usage 응답 예시
{
"doc_count": 23,
"api_doc_count": 18,
"web_doc_count": 5,
"free_doc_count": 5,
"amount_krw": 8400,
"billing_status": "pending",
"billed_at": null
}
billing_status는 pending · billed · failed 중 하나입니다. free_doc_count는 무료 집계분이므로 amount_krw 산정에서 제외됩니다.
GET /billing/method 응답 예시
{
"provider": "portone",
"card_company": "하나카드",
"card_number_masked": "****-****-****-1234",
"customer_key": "sp_customer...",
"subscription_id": null,
"is_active": true
}
POST /documents 처리 중 등록된 결제수단에 표시 금액 그대로(VAT 포함) 승인되며, 승인이 거절되면 문서가 생성되지 않고 알림도 발송되지 않습니다. 월별 합산 후불 청구는 하지 않습니다. test 환경은 과금하지 않고 실제 알림톡도 발송하지 않습니다. live 환경의 서명 요청·완료 알림톡은 signer 역할 참여자에게만 비동기로 발송되며, 통신사·카카오 수신 상태에 따라 전달되지 않을 수 있습니다. viewer·approver 역할에는 이메일만 발송되고 과금 대상도 아닙니다. 이메일 안내는 모든 참여자에게 유지됩니다.
오류 코드
모든 오류는 동일한 형식으로 반환됩니다.
// 오류 응답 형식
{
"error": "invalid_request",
"message": "The request body is invalid.",
"details": null,
"request_id": "req-13m",
"responsibility": "client",
"retryable": false,
"field_errors": [
{
"field": "options.reminder_intervals_hours",
"message": "This field must be an array. Use [] when no reminders are needed."
}
]
}
responsibility가 client이면 요청 값을 수정해야 하고, signplug이면 request_id와 함께 문의해 주세요. 공개 응답에는 내부 스택·저장소·DB 등 서비스 구조 정보가 포함되지 않습니다.
| HTTP | 코드 | 원인 |
|---|---|---|
400 | invalid_request | 필수 필드 누락, 잘못된 파일 형식, 서명자 10명 초과, 잘못된 Webhook URL 등 |
400 | recipient_consent_required | 문서 생성 전 수신자의 전자문서 수신 및 전자서명 진행 사전 동의 확인이 누락됨 |
400 | identity_verification_required | 폐기된 오류: require_sms_verification=false 및 참여자별 인증 생략을 허용합니다. |
400 | notification_required | 알림톡·이메일 안내 필수 정책상 send_notification을 false로 보낼 수 없음 |
400 | signer_phone_required | 본인인증을 사용하는 서명자의 휴대폰 번호 누락 |
400 | participant_contact_required | 참여자의 이메일과 휴대폰 번호가 모두 누락됨 |
400 | source_not_available | source_object_key를 현재 요청에서 사용할 수 없음. 다시 업로드 후 재시도 |
400 | agreement_required | 서명 제출 시 약관 동의 누락 |
401 | missing_authorization | Authorization 헤더 없음 |
401 | invalid_authorization | Bearer 형식이 아님 |
401 | invalid_api_key | API Key가 잘못되었거나 비활성화됨 |
402 | payment_method_required | live 문서 생성에 필요한 결제 수단 없음. 대시보드에서 카드 등록 후 재시도 |
402 | card_declined | 문서 생성 시점의 카드 승인 거절. 문서는 생성되지 않음. details.reason 에 카드사 사유, details.temporary 가 true 면 같은 카드로 나중에 재시도 가능(한도 초과·점검 등), false 면 다른 카드 등록 필요 |
404 | document_not_found | 문서 ID가 없거나, 다른 계정의 문서라서 권한이 없음 |
404 | signing_link_not_found | 서명 토큰을 찾을 수 없음. 잘못된 토큰이거나 이미 폐기됨 |
409 | document_pdf_not_ready | 변환 또는 최종 PDF 생성이 아직 완료되지 않음. 잠시 후 재시도 |
409 | participant_not_verified | 본인확인 전 서명 제출. 인증 사용 설정일 때 /identity-verification/prepare 및 /identity-verification/confirm 흐름 완료 필요 |
409 | invalid_state | 완료/만료/취소된 문서에 DELETE를 호출하는 등 상태가 맞지 않음 |
409 | webhook_already_exists | 동일 계정에 같은 URL의 활성 webhook이 이미 등록됨 |
410 | document_expired | 서명 가능 기간 만료 |
413 | FST_REQ_FILE_TOO_LARGE | 업로드 파일이 50MB 한도를 초과. multipart 응답 헤더로 반환 |
429 | test_quota_exhausted | 테스트 환경 본인확인 누적 시도 한도 초과. 한도 증가는 contact@moram.net으로 문의. details.sent·details.limit으로 현재 사용량 확인 가능 |
500 | internal_server_error | SignPlug 서비스 오류. 응답의 request_id와 함께 문의 |
409 document_pdf_not_ready처럼 일시적인 상태 오류는 지수 백오프(예: 1s → 2s → 4s)로 재시도하면 됩니다. 4xx 오류 중 invalid_request, invalid_api_key, document_not_found 등은 클라이언트 측 입력 문제이므로 재시도하지 마세요.
자주 묻는 질문 · 통합 팁
PDF 좌표를 어떻게 알 수 있나요?
웹 서명 도구에서 마우스로 필드를 배치하면 정확한 x, y, width, height 값을 확인할 수 있습니다. 이 값을 그대로 API에 넘기세요.
순차 서명 vs 동시 서명
signing_order를 서로 다르게 지정하면 순차 서명, 동일하게 지정하면 동시 서명입니다. 예: [1, 1, 2]이면 앞의 두 명이 함께 진행하고, 두 명 모두 서명한 뒤 세 번째 사람이 진행합니다. 이메일은 생성 시 전원에게 발송하고, live 알림톡은 현재 순서 그룹의 서명자에게 비동기로 발송합니다. 리마인더 이메일도 현재 순서 그룹이 대상입니다.
파일이 크거나 base64가 불편할 때
POST /internal/source-upload로 먼저 파일을 업로드하고, 받은 source_object_key를 POST /hosted-sessions 또는 POST /documents에 전달하면 base64 인코딩 없이 대용량 파일을 처리할 수 있습니다.
Test 환경에서 본인확인 확인
Test 키로 발급된 문서도 KG이니시스 본인확인 창을 호출합니다. KG이니시스 통합본인인증은 참여자의 name, phone, email 정보를 사용하므로 정확한 정보를 전달해 주세요.
Webhook을 받지 못할 때
서버가 설정된 제한 시간 안에 2xx를 응답하지 않으면 재시도됩니다. 운영 환경에서는 https:// URL만 등록할 수 있으므로 로컬 개발 시에는 ngrok이나 유사 터널링 도구로 임시 URL을 생성하세요.