전자서명 API,
5분 만에 연동
문서 생성 → 서명 링크 발급 → 완료 이벤트 수신.
단 3번의 API 호출로 전자서명 흐름이 완성됩니다.
5분 만에 첫 서명 문서 만들기 (Hosted 플로우)
고객사 백엔드는 API Key로 호스티드 세션만 발급하고, 사용자가 SignPlug의 호스티드 페이지에서 PDF 업로드 · 서명 좌표 지정 · 발송까지 진행합니다. 좌표 계산이나 base64 인코딩이 필요 없습니다.
POST /documents를 참고하세요. Hosted 플로우와 동일하게 모든 환경에서 사용할 수 있습니다.
recipient_consent_confirmed: true, require_sms_verification: true, send_notification: true가 필요합니다. 서명자에게는 알림톡 발송과 KG이니시스 본인확인을 위해 phone과 email을 함께 전달해 주세요. 이름 도장 생성은 수신자가 SignPlug 서명 앱에서 선택하는 기능이므로 문서 생성 API 요청 형식은 변경되지 않습니다.
sp_test_* 테스트 키는 KG이니시스 개발 본인확인 채널을 사용하며, 전자서명 생성은 무제한입니다. 단 테스트 키의 알림톡은 계정당 최대 50건까지 발송되고, 한도 초과 후에도 문서와 서명 URL은 생성되며 응답의 warnings에 알림톡 미발송 안내가 포함됩니다. sp_live_* 라이브 키와 SignPlug 웹은 KG이니시스 운영 본인확인 채널과 운영 알림톡 발송 기준을 사용합니다.
API Key 발급
이 페이지 상단의 API Key 발급 버튼을 클릭하면 test · live 키 쌍이 즉시 생성됩니다. Moram 계정으로 로그인되어 있어야 합니다. 키는 고객사 백엔드 서버 사이드에서만 사용하고 브라우저에 노출하지 마세요.
호스티드 세션 발급
고객사 백엔드에서 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은 호환용 필드명이며 현재 모든 서명 요청에서 KG이니시스 본인확인이 필수입니다. |
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/DOCX/HWPX 파일을 base64 인코딩한 문자열 |
source_object_key | string | 둘 중 하나 | POST /internal/source-upload로 먼저 업로드 후 받은 키 |
file_type | string | 필수 | pdf · docx · hwpx |
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 | 필수 | 참여자 표시 이름 |
phone | string|null | 필수 | 휴대폰 번호. 서명자는 알림톡 발송과 KG이니시스 본인확인을 위해 반드시 필요합니다. |
email | string|null | 필수 | 이메일 보조 알림과 인증서 발송에 사용합니다. 서명자는 KG이니시스 본인확인을 위해 반드시 필요합니다. |
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 기준 좌표 (포인트 단위, 기본 좌상단 원점) |
width, height | number | 필수 | 필드 크기 (포인트 단위) |
type | string | 필수 | signature · name · date |
options 필드
| 필드 | 타입 | 예시값 | 설명 |
|---|---|---|---|
expiration_hours | integer | 72 | 서명 링크 만료 시간 (시간 단위) |
reminder_intervals_hours | array | [] | 리마인더 발송 시점 (시간 단위 배열). 리마인더가 없으면 생략하거나 [] 전달 |
require_sms_verification | boolean | true | KG이니시스 본인확인 필수 여부. 필드명은 기존 연동 호환을 위해 유지되며 false는 허용되지 않습니다. |
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 · docx · hwpx. 생략하면 파일명 확장자로 자동 추론 |
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/docx/hwpx가 아님 |
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시간 후 만료되므로 발급 후 가급적 빠르게 사용하세요.
문서 목록 조회
계정의 문서를 최신 생성순으로 조회합니다. 대시보드와 운영 모니터링에서 사용하는 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": "docx",
"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로 다시 내려주세요.
변환·서명 완료 전에 호출하면 409 document_pdf_not_ready가 반환됩니다.
/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 | DOCX/HWPX 변환 등 준비 중. 이 상태에서는 /pdf 호출이 409 |
waiting | 모든 참여자가 아직 서명하지 않음. 서명 링크 유효 |
in_progress | 일부 참여자가 서명 완료. 순차 서명일 경우 다음 차례 참여자에게 알림 발송 |
completed | 모든 참여자 서명 완료. /pdf, /audit-certificate.pdf 모두 다운로드 가능 |
rejected | 참여자 중 한 명이 서명을 거절. 후속 참여자 알림은 발송하지 않음 |
expired | 만료 기간 초과. 서명 링크 사용 불가 |
cancelled | DELETE 호출로 취소됨 |
서명 링크 · 참여자 경험
문서 생성 응답의 signing_urls[].url을 참여자에게 그대로 전달하면 SignPlug 서명 앱이 KG이니시스 통합본인인증부터 서명 제출, 완료 안내까지 전 과정을 처리합니다. 아래 /public/signing-links/* 엔드포인트는 서명 앱이 내부적으로 사용하므로 일반 통합에서는 직접 호출할 필요가 없습니다.
서명 URL 형식
https://sign.moram.net/signplug/s/<TOKEN>
토큰은 문서 생성 시 참여자별로 발급되며, 만료 시간이 지나면 자동 무효화됩니다. SignPlug 서명 앱은 모바일·태블릿·데스크탑 환경을 자동 감지해 화면을 최적화하고, 서명 전 KG이니시스 통합본인인증 결과를 서버에서 확인한 뒤 서명을 허용합니다. 참여자는 서명 입력 시 직접 서명, 도장/이미지 업로드, 이름 도장 생성 중 하나를 선택할 수 있습니다.
참여자에게 안내 메시지를 직접 보낼 때 예시
안녕하세요, [회사명]입니다.
"[문서 제목]" 전자서명 요청을 보내드립니다.
▶ 서명하러 가기: https://sign.moram.net/signplug/s/abcd1234...
· KG이니시스 통합본인인증 완료 후 서명할 수 있습니다.
· 링크는 발송일로부터 72시간 동안 유효합니다.
서명 앱 내부 엔드포인트 (참고용)
자체 서명 UI를 구축하는 특수한 케이스가 아니라면 호출할 필요가 없습니다. 인증은 token 기반이며 별도의 API Key를 요구하지 않습니다.
| 엔드포인트 | 용도 |
|---|---|
GET /public/signing-links/:token | 문서 정보, 참여자 상태, 서명 필드, 워크플로 상태(is_current_turn 등) 조회 |
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로 리다이렉트하는 패턴을 권장합니다.
서명 진행 이벤트 수신
문서 생성, 열람, 본인확인, 서명, 완료, 거절, 만료, 취소 이벤트를 등록한 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가 반환됩니다.
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) |
GET /billing/usage?year=YYYY&month=M | 해당 월의 문서 수, API/웹 분리 카운트, 청구 금액, 청구 상태 조회 |
GET /billing/outstanding | 아직 청구되지 않은 누적 사용량과 금액 |
GET /billing/web-entitlement | 웹 결제 무료 문서 잔여 수, 결제 수단 등록 필요 여부 |
GET /billing/config | PortOne 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": "KG이니시스",
"card_number_masked": "",
"customer_key": "sp_customer...",
"subscription_id": null,
"is_active": true
}
오류 코드
모든 오류는 동일한 형식으로 반환됩니다.
// 오류 응답 형식
{
"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 | KG이니시스 본인확인 필수 정책상 require_sms_verification을 false로 보낼 수 없음 |
400 | notification_required | 알림톡 발송 필수 정책상 send_notification을 false로 보낼 수 없음 |
400 | signer_phone_required | 서명자의 알림톡 발송 및 KG이니시스 본인확인을 위한 휴대폰 번호 누락 |
400 | signer_email_required | 서명자의 KG이니시스 본인확인 및 인증서 발송을 위한 이메일 누락 |
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 문서 생성에 필요한 결제 수단 없음. 대시보드에서 카드 등록 후 재시도 |
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]이면 두 사람 모두 즉시 링크 수신.
DOCX · HWPX 지원
DOCX, HWPX 파일은 서버에서 자동으로 PDF 변환 후 처리됩니다. 변환 중에는 문서 상태가 draft일 수 있으므로 완료 PDF 조회 전에 상태를 확인하세요.
파일이 크거나 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을 생성하세요.