Developer Docs

전자서명 API,
5분 만에 연동

문서 생성 → 서명 링크 발급 → 완료 이벤트 수신.
단 3번의 API 호출로 전자서명 흐름이 완성됩니다.

빠른 시작 →
1
문서 생성 POST /documents
2
링크 전달 signing_urls[] 반환
3
완료 수신 Webhook 이벤트
Quick Start

5분 만에 첫 서명 문서 만들기 (Hosted 플로우)

고객사 백엔드는 API Key로 호스티드 세션만 발급하고, 사용자가 SignPlug의 호스티드 페이지에서 PDF 업로드 · 서명 좌표 지정 · 발송까지 진행합니다. 좌표 계산이나 base64 인코딩이 필요 없습니다.

좌표·base64를 직접 다루며 서버에서 단일 호출로 문서를 만들고 싶다면 아래 문서 생성 섹션의 POST /documents를 참고하세요. Hosted 플로우와 동일하게 모든 환경에서 사용할 수 있습니다.
API 고객사는 서명자 개인정보의 적법한 수집 주체이며, SignPlug는 전자서명 요청, 알림 발송, 문서 보관, 로그 생성을 위해 위탁받은 목적 범위 안에서만 처리합니다. 자세한 내용은 SignPlug 개인정보 처리 안내서비스 약관을 확인하세요.
1

API Key 발급

이 페이지 상단의 API Key 발급 버튼을 클릭하면 test · live 키 쌍이 즉시 생성됩니다. Moram 계정으로 로그인되어 있어야 합니다. 키는 고객사 백엔드 서버 사이드에서만 사용하고 브라우저에 노출하지 마세요.

2

호스티드 세션 발급

고객사 백엔드에서 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 }
      ]
    }
  }'

응답 예시

{
  "session_token": "wsh_Us0GyhIY4IrAXfwlO1uUZhnclHTboxGP",
  "hosted_url": "https://signplug.moram.net/start?session_token=wsh_Us0GyhIY4IrAXfwlO1uUZhnclHTboxGP",
  "environment": "test",
  "expires_at": "2026-05-15T13:00:00Z"
}
3

사용자를 hosted_url로 리다이렉트

발급받은 hosted_url로 사용자를 보냅니다. 사용자는 SignPlug 호스티드 페이지에서 PDF를 업로드하고 서명 필드를 마우스로 배치한 뒤 발송합니다. 별도 Moram 로그인은 필요 없습니다.

session_token은 URL의 쿼리 파라미터로 전달되므로, 만료 시간이 지나면 자동 무효화됩니다. 동일 토큰으로 여러 문서를 만들 수도 있으니, 사용자별/거래별로 새 세션을 발급하는 것을 권장합니다.
4

return_url 콜백 처리

문서가 생성되면 SignPlug가 사용자 브라우저를 return_url로 돌려보내며, document_idstatus 쿼리 파라미터를 덧붙입니다.

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 · 환경 구분

모든 환경이 동일한 Base URL을 사용하고, 환경은 요청 시 사용하는 API Key 프리픽스로만 구분됩니다.

https://api.moram.net/signplug/v1
키 프리픽스환경알림톡 OTP과금
sp_test_* 개발 · QA 실제 발송 없음 (테스트 키 OTP 발송량은 합리적인 한도 내에서 무료 제공)
sp_live_* 운영 실제 발송 발생. 활성 결제 수단 등록 필요
테스트 키 알림톡: 테스트 키로도 실제 알림톡이 발송되므로 발송 비용이 발생합니다. 대량 부하 테스트나 한도 상향이 필요하면 contact@moram.net으로 문의하세요.

관련 서비스 URL

서비스URL용도
APIhttps://api.moram.net/signplug/v1모든 REST API 호출
서명 앱https://sign.moram.net/signplug참여자가 서명을 진행하는 페이지 (서명 URL의 호스트)
대시보드https://signplug.moram.net/dashboard키 발급, 결제 수단 등록, 사용량 확인
랜딩https://signplug.moram.net제품 소개, 가격, 문의
Authentication

API Key 인증

모든 요청에 Authorization 헤더로 Bearer 토큰을 전달합니다.

# 개발/QA (알림톡 OTP 발송, 과금 없음)
Authorization: Bearer sp_test_xxxxxxxxxxxxxxxxxxxxx

# 운영 (알림톡 OTP 발송, 과금)
Authorization: Bearer sp_live_xxxxxxxxxxxxxxxxxxxxx
보안 주의: API Key를 프론트엔드(브라우저) 코드에 직접 포함하지 마세요. 서버 사이드에서만 사용하거나, 내부 프록시 엔드포인트로 감싸세요.
Hosted Sessions

호스티드 세션 발급

고객사 백엔드가 API Key로 짧은 수명의 세션 토큰을 발급받고, 사용자를 SignPlug 호스티드 페이지로 리다이렉트합니다. 사용자는 그 페이지에서 PDF 업로드 · 좌표 지정 · 발송을 진행하며, 완료되면 return_url로 돌아옵니다.

POST /hosted-sessions

Request Body (모두 선택)

필드타입설명
return_urlstring 문서 생성 완료 후 사용자를 돌려보낼 URL. https://만 허용 (개발 시 localhost 예외). 비우면 호스티드 페이지에 머무름
metadataobject 호스티드 세션 식별용 메타데이터. 생성된 문서의 metadata에 병합되어 webhook 등에서 활용 가능
source_object_keystring POST /internal/source-upload로 먼저 업로드한 PDF 키. 전달하면 사용자가 hosted_url에 접속할 때 PDF가 자동 로드됩니다.
source_file_typestring 현재 개선형 Hosted 자동 로드는 pdf만 지원합니다.
expires_in_minutesinteger 세션 만료 시간(분). 5~240, 기본 60
defaults.titlestring 호스티드 페이지의 문서 제목 입력란 기본값
defaults.participantsarray 참여자 카드를 미리 채울 값. { name, phone, email, role, signing_order } 형식
defaults.optionsobject expiration_hours, require_sms_verification, send_notification 등의 기본값

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)을 따릅니다.

세션 정보 조회 (호스티드 페이지 내부 호출)

GET /hosted-sessions/current

호스티드 페이지가 세션 토큰을 직접 검증하고 defaults · return_url을 읽어오는 데 사용됩니다. Authorization: Bearer <session_token> 헤더로 호출합니다. 일반적으로 통합 코드에서 직접 호출할 필요는 없습니다.

연결된 PDF 조회 (호스티드 페이지 내부 호출)

GET /hosted-sessions/current/source.pdf

source_object_key가 연결된 호스티드 세션에서 PDF 미리보기를 렌더링하기 위해 사용합니다. Authorization: Bearer <session_token> 헤더가 필요하며, 연결된 PDF가 없으면 404 source_not_found를 반환합니다.

주요 오류

HTTP코드원인
400invalid_requestreturn_url이 https가 아니거나 URL 형식 오류
400unsupported_source_file_type호스티드 자동 로드가 지원하지 않는 파일 형식. 현재 pdf만 지원
401invalid_api_key세션 발급은 정식 API Key(sp_test_* 또는 sp_live_*)로만 가능
401invalid_hosted_session호스티드 세션 토큰이 만료/폐기된 경우 (호스티드 페이지 측 오류)
403source_owner_mismatchsource_object_key가 다른 계정 또는 다른 환경에서 생성됨
409source_already_attached이미 다른 호스티드 세션에 연결된 업로드 키
409source_already_used이미 문서 생성에 사용된 업로드 키
410source_expired선업로드된 PDF가 만료됨. 다시 업로드 필요
Create Document

문서 생성 (직접 API 호출)

서버 단일 호출로 PDF · 참여자 · 서명 좌표까지 한 번에 만들고 싶을 때 사용합니다. 좌표 계산을 직접 제어해야 하는 자동화 흐름에 적합합니다. UI 없이 즉시 문서를 만들고 싶지 않다면 위의 호스티드 세션이 더 간단합니다.

POST /documents

문서 파일, 참여자, 서명 필드를 한 번에 전달해 전자서명 문서를 생성합니다. 성공 시 참여자별 서명 URL을 즉시 반환합니다.

Request Body

필드타입설명
titlestring 필수 문서 제목 (예: "공급계약서 2026-04")
file_base64string 둘 중 하나 PDF/DOCX/HWPX 파일을 base64 인코딩한 문자열
source_object_keystring 둘 중 하나 POST /internal/source-upload로 먼저 업로드 후 받은 키
file_typestring 필수 pdf · docx · hwpx
participantsarray 필수 서명 참여자 목록 (아래 participants 참고). 서명자는 문서당 최대 10명
signature_fieldsarray 필수 PDF 내 서명 위치 목록 (아래 signature_fields 참고)
optionsobject 필수 만료 시간, 리마인더, OTP 인증 등 옵션. 리마인더가 없으면 [] 전달
metadataobject 필수 내부 참조용 메타데이터. 없으면 {} 전달

participants[ ] 필드

문서 1건에는 서명자 1명이 기본 포함됩니다. rolesigner인 추가 서명자는 1명당 150원(VAT 별도)이 추가되며, 한 문서의 서명자는 최대 10명입니다.
필드타입설명
namestring필수참여자 표시 이름
phonestring|null필수휴대폰 번호. 없으면 null 전달
emailstring|null필수이메일 보조 알림에 사용. 없으면 null 전달
rolestring필수signer · viewer · approver. signer 역할만 추가 서명자 과금과 최대 10명 제한 대상입니다.
signing_orderinteger필수서명 순서 (1부터 시작). 동시 서명은 동일 숫자 지정

signature_fields[ ] 필드

필드타입설명
participant_indexinteger필수participants 배열 기준 인덱스 (0부터 시작)
pageinteger필수페이지 번호 (1부터 시작)
x, ynumber필수PDF 기준 좌표 (포인트 단위, 좌상단 원점)
width, heightnumber필수필드 크기 (포인트 단위)
typestring필수signature · name · date

options 필드

필드타입예시값설명
expiration_hoursinteger72서명 링크 만료 시간 (시간 단위)
reminder_intervals_hoursarray[]리마인더 발송 시점 (시간 단위 배열)
require_sms_verificationbooleantrue알림톡 OTP 서명자 인증 필수 여부
send_notificationbooleantrue참여자에게 서명 링크 알림 발송 여부

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"
    }
  ]
}
좌표 팁: 서명 필드 위치(x, y)는 PDF 포인트 기준입니다. A4 용지는 가로 595pt × 세로 842pt입니다. 웹 서명 도구를 열면 마우스로 직접 배치하고 좌표를 확인할 수 있습니다.
File Upload

파일 업로드 (대용량)

파일이 크거나 base64 변환이 불편할 때는 먼저 파일을 업로드하고 반환된 키를 POST /documents에 사용합니다. multipart/form-datafile 필드로 최대 50MB까지 업로드할 수 있습니다.

POST /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

필드위치설명
Authorizationheader 필수 Bearer sp_test_* 또는 Bearer sp_live_*
filemultipart 필수 업로드할 파일. 최대 50MB
file_typemultipart 선택 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_keyPOST /hosted-sessions 또는 POST /documentssource_object_key 필드에 그대로 전달
expires_at선업로드 키 만료 시각. 만료 후에는 다시 업로드해야 합니다.
sha256업로드된 파일 원본의 SHA-256 해시. 클라이언트에서 무결성을 검증할 때 사용
size바이트 단위 파일 크기
storage_providers3 또는 minio. 디버깅용 정보

주요 오류

HTTP코드원인
400invalid_requestfile 필드 없음 · 빈 파일 · file_type이 pdf/docx/hwpx가 아님
413FST_REQ_FILE_TOO_LARGE업로드 파일이 50MB(52,428,800 bytes)를 초과
401invalid_api_keyAPI Key 누락 또는 잘못된 키
키 사용 시점: 받아온 source_object_key는 동일 계정의 POST /hosted-sessions에 연결하거나 POST /documents 호출에서 사용할 수 있습니다. 한 번 문서 생성에 사용되면 정식 문서 스토리지로 이동합니다. 사용되지 않은 키는 기본 24시간 후 만료되므로 발급 후 가급적 빠르게 사용하세요.
Document List

문서 목록 조회

계정의 문서를 최신 생성순으로 조회합니다. 대시보드와 운영 모니터링에서 사용하는 API입니다.

GET /documents?page=1&limit=20&status=completed

쿼리 파라미터

쿼리타입기본값설명
pageinteger11부터 시작하는 페이지 번호
limitinteger20페이지당 항목 수. 1~100 사이
statusstring-특정 상태만 필터. 가능한 값: 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 문서를 구분할 수 있습니다.
Document Status

문서 상태 조회

GET /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 }
  ]
}

참여자별 statuspending · viewed · verified · signed · rejected · expired 중 하나이며, 문서의 status와는 별도 흐름입니다.

GET /documents/:document_id/pdf

완료된 문서의 최종 PDF를 application/pdf로 내려받습니다. 브라우저 표시용 Content-Disposition: inline 헤더가 포함되어 있어, 그대로 <iframe>이나 새 탭에 표시할 수 있습니다. 파일을 강제로 저장하게 하려면 응답을 그대로 전달하지 말고 자체 프록시에서 attachment로 다시 내려주세요.

변환·서명 완료 전에 호출하면 409 document_pdf_not_ready가 반환됩니다.

GET /documents/:document_id/audit-log

문서 해시와 열람·OTP 인증·서명·거절·취소 이벤트를 시간순으로 반환합니다.

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": "alimtalk_otp"
    },
    {
      "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"
    }
  ]
}
GET /documents/:document_id/audit-certificate.pdf

완료된 문서의 감사추적 인증서를 PDF로 내려받습니다. 인증서에는 문서 해시(원본·변환·최종), 참여자별 인증/서명 일시, IP, OTP 검증 방법이 포함되어 분쟁 대응 자료로 활용할 수 있습니다. 문서가 completed 상태가 아니면 인증서가 생성되지 않습니다.

DELETE /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 값

설명
draftDOCX/HWPX 변환 등 준비 중. 이 상태에서는 /pdf 호출이 409
waiting모든 참여자가 아직 서명하지 않음. 서명 링크 유효
in_progress일부 참여자가 서명 완료. 순차 서명일 경우 다음 차례 참여자에게 알림 발송
completed모든 참여자 서명 완료. /pdf, /audit-certificate.pdf 모두 다운로드 가능
rejected참여자 중 한 명이 서명을 거절. 후속 참여자 알림은 발송하지 않음
expired만료 기간 초과. 서명 링크 사용 불가
cancelledDELETE 호출로 취소됨
Webhook

서명 진행 이벤트 수신

문서 생성, 열람, OTP 인증, 서명, 완료, 거절, 만료, 취소 이벤트를 등록한 URL로 수신합니다.

POST /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_* 값이 자동 발급되어 응답에 한 번 표시됩니다.

수신 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참여자가 OTP 인증 완료
document.signed참여자 한 명이 서명 완료
document.completed모든 참여자 서명 완료
document.rejected참여자가 서명 거절
document.expired서명 링크 만료
document.reminded리마인더 발송
document.cancelled문서 취소
Webhook은 기본 10초 안에 2xx 응답을 받아야 성공 처리됩니다. 실패 시 60초, 5분, 30분, 2시간 간격으로 재시도하고, 마지막 시도까지 실패하면 dead letter 상태가 됩니다.

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
Operations

운영 · 과금 · 키 관리

라이브 키로 문서를 생성하려면 계정에 활성 결제 수단이 있어야 합니다. 미등록 상태에서는 402 payment_method_required가 반환됩니다.

API Key 관리

엔드포인트용도
GET /api-keys계정의 test/live API Key 목록 조회. 키 원문은 재표시되지 않으며 key_prefix(앞 7~12자)로 구분합니다.
POST /api-keys/:api_key_id/rotate기존 키를 비활성화하고 같은 환경의 새 키를 발급. 새 키 원문은 응답에 1회 표시
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/configToss/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_statuspending · billed · failed 중 하나입니다. free_doc_count는 무료 집계분이므로 amount_krw 산정에서 제외됩니다.

GET /billing/method 응답 예시

{
  "provider": "toss",
  "card_company": "신한",
  "card_number_masked": "************1234",
  "customer_key": "cust_018f6f3a...",
  "subscription_id": null,
  "is_active": true
}
결제 수단 등록: 카드 등록·교체는 보안상 API로 직접 처리할 수 없으며 SignPlug 대시보드에서 Toss/Paddle 결제창을 통해 진행합니다. 등록 즉시 라이브 키 사용이 가능해집니다.
과금 정책: 웹 결제 문서 기본 300원+VAT, API 생성 문서 기본 400원+VAT. 문서 1건에는 서명자 1명이 포함되고, 추가 서명자는 1명당 150원+VAT가 청구됩니다. 웹에서 결제되는 최초 5건은 기본 문서비만 무료로 집계되며 test 환경은 과금하지 않습니다.
Error Codes

오류 코드

모든 오류는 동일한 형식으로 반환됩니다.

// 오류 응답 형식
{
  "error": "invalid_request",
  "message": "file_type must be one of: pdf, docx, hwpx.",
  "details": null
}

details는 필드 단위 검증 오류를 포함할 수 있는 객체 또는 null입니다. error 코드(기계 판독용)로 분기하고, message는 로그/디버깅 용도로 사용하세요.

HTTP코드원인
400invalid_request필수 필드 누락, 잘못된 파일 형식, 서명자 10명 초과, 잘못된 Webhook URL 등
400agreement_required서명 제출 시 약관 동의 누락
401missing_authorizationAuthorization 헤더 없음
401invalid_authorizationBearer 형식이 아님
401invalid_api_keyAPI Key가 잘못되었거나 비활성화됨
402payment_method_requiredlive 문서 생성에 필요한 결제 수단 없음. 대시보드에서 카드 등록 후 재시도
404document_not_found문서 ID가 없거나, 다른 계정의 문서라서 권한이 없음
404signing_link_not_found서명 토큰을 찾을 수 없음. 잘못된 토큰이거나 이미 폐기됨
409document_pdf_not_ready변환 또는 최종 PDF 생성이 아직 완료되지 않음. 잠시 후 재시도
409participant_not_verifiedOTP 인증 전 서명 제출. 먼저 /otp/verify 호출 필요
409invalid_state완료/만료/취소된 문서에 DELETE를 호출하는 등 상태가 맞지 않음
410document_expired서명 가능 기간 만료
413FST_REQ_FILE_TOO_LARGE업로드 파일이 50MB 한도를 초과. multipart 응답 헤더로 반환
429test_quota_exhausted테스트 키의 알림톡 OTP 누적 발송 한도(기본 50건) 초과. 한도 증가는 contact@moram.net으로 문의. details.sent·details.limit으로 현재 사용량 확인 가능
500internal_server_error서버 내부 오류. 동일 요청을 즉시 재시도하지 말고 contact@moram.net으로 시간/요청 ID 함께 문의
재시도 가이드: 409 document_pdf_not_ready처럼 일시적인 상태 오류는 지수 백오프(예: 1s → 2s → 4s)로 재시도하면 됩니다. 4xx 오류 중 invalid_request, invalid_api_key, document_not_found 등은 클라이언트 측 입력 문제이므로 재시도하지 마세요.
Integration Tips

자주 묻는 질문 · 통합 팁

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_keyPOST /hosted-sessions 또는 POST /documents에 전달하면 base64 인코딩 없이 대용량 파일을 처리할 수 있습니다.

Test 환경에서 OTP 수신 확인

Test 키로 발급된 문서도 알림톡 OTP가 실제로 발송됩니다. 참여자의 phone 필드에 정확한 휴대폰 번호를 넣어야 OTP를 받을 수 있습니다.

Webhook을 받지 못할 때

서버가 설정된 제한 시간 안에 2xx를 응답하지 않으면 재시도됩니다. 운영 환경에서는 https:// URL만 등록할 수 있으므로 로컬 개발 시에는 ngrok이나 유사 터널링 도구로 임시 URL을 생성하세요.