임베디드 서명 화면 연동

고객사의 서비스 화면 안에서 서명 화면을 직접 구현하는 방법을 안내합니다.
서명자가 이메일/카카오톡 알림 없이 서비스 내에서 바로 서명할 수 있습니다.


일반 연동 vs 임베디드 연동

일반 연동임베디드 연동
서명자 알림이메일 / 카카오톡 자동 발송발송 안 함
서명 URL직접 받을 수 없음sign_url / token으로 응답
서명 화면서명자가 알림 클릭 후 이동서비스 내 새 탭 / 새 창으로 직접 표시
활용 예시계약서 발송, 비대면 서명 요청회원가입 동의, 서비스 내 즉시 서명
📘

임베디드 연동은 언제 사용하나요?

서명자가 지금 바로 서비스 화면에 있는 상황에서 서명을 받아야 할 때 사용합니다.
예를 들어 회원가입 마지막 단계에서 개인정보 동의서를 받거나, 앱 화면에서 계약서에 바로 서명하게 할 때 적합합니다.

⚠️

iframe은 지원하지 않습니다

보안 취약점, 브라우저 호환성 이슈, 페이지 로딩 성능 문제로 인해 iframe 방식은 지원하지 않습니다.
새 탭 또는 새 창 방식으로 서명 화면을 열어 주시기 바랍니다.




이런 순서로 진행합니다


1. 문서 시작하기

일반 문서 시작 API와 동일하지만, export_api_info 파라미터에 is_embed: true를 추가합니다.
이 설정을 하면 이메일 / 카카오톡 알림이 발송되지 않고, 응답에 tokensign_url이 포함됩니다.

POST /api/v3/workflows/start
export_api_info 상세 파라미터
파라미터타입필수설명
urlstringY서명 완료 시 데이터를 수신할 서버 URL
api_typestringY수신 시점: StartAndEnd (시작+완료) / ALL (모든 단계)
is_embedbooleanYtrue 설정 시 임베디드 연동 활성화
link_typestringN완료 문서 URL 타입: viewer (미리보기) / download (다운로드)
authorizationstringNExport API 요청 헤더의 Authorization 값

요청 예시

curl -X POST "https://docs.esignon.net/api/v3/workflows/start" \
  -H "Authorization: esignon {access_token}" \
  -H "Content-Type: application/json" \
  -d '{
    "workflow_name": "2026_개인정보동의서_홍길동",
    "template_id": 753,
    "recipient_list": [
      {
        "order": 1,
        "name": "홍길동",
        "email": "010-1234-5678"
      }
    ],
    "export_api_info": {
      "url": "https://your-server.com/esignon/webhook",
      "api_type": "StartAndEnd",
      "is_embed": true,
      "link_type": "viewer"
    }
  }'
const response = await fetch("https://docs.esignon.net/api/v3/workflows/start", {
  method: "POST",
  headers: {
    "Authorization": "esignon {access_token}",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    workflow_name: "2026_개인정보동의서_홍길동",
    template_id: 753,
    recipient_list: [
      { order: 1, name: "홍길동", email: "010-1234-5678" }
    ],
    export_api_info: {
      url: "https://your-server.com/esignon/webhook",
      api_type: "StartAndEnd",
      is_embed: true,
      link_type: "viewer"
    }
  })
});
const data = await response.json();
const token = data.token;      // 서명 URL 직접 구성 시 사용
const signUrl = data.sign_url; // 바로 사용 가능한 서명 URL
import requests
 
response = requests.post(
    "https://docs.esignon.net/api/v3/workflows/start",
    headers={
        "Authorization": "esignon {access_token}",
        "Content-Type": "application/json"
    },
    json={
        "workflow_name": "2026_개인정보동의서_홍길동",
        "template_id": 753,
        "recipient_list": [
            { "order": 1, "name": "홍길동", "email": "010-1234-5678" }
        ],
        "export_api_info": {
            "url": "https://your-server.com/esignon/webhook",
            "api_type": "StartAndEnd",
            "is_embed": True,
            "link_type": "viewer"
        }
    }
)
data = response.json()
token = data["token"]       # 서명 URL 직접 구성 시 사용
sign_url = data["sign_url"] # 바로 사용 가능한 서명 URL

응답 예시

{
  "workflow_id": 98765,
  "workflow_name": "2026_개인정보동의서_홍길동",
  "language": "ko-KR",
  "token": "FAEx6i%2BuVzYe...",
  "sign_url": "https://docs.esignon.net/mail/sign?token=...",
  "next_name": "홍길동",
  "next_email": "010-1234-5678"
}
응답 필드설명
token서명 URL 직접 구성 시 사용하는 토큰
sign_url바로 사용 가능한 서명 화면 URL
next_name다음 서명 차례인 서명자 이름
next_email다음 서명 차례인 서명자 이메일

2. 서명 URL 구성하기

응답으로 받은 token을 사용해 서명 URL을 직접 구성할 수 있습니다.
URL에 파라미터를 추가하여 서명 완료 후 동작을 제어합니다.

GET https://docs.esignon.net/api/{companyId}/sign?token={token}

선택 파라미터

파라미터설명
callback_fntrue서명 완료 후 팝업의 닫기 버튼 클릭 시 부모 페이지로 콜백 이벤트를 전달합니다
next_signfalse서명 완료 팝업에서 "다음 문서 작성하기" 버튼을 숨깁니다
langko / en / ja서명 화면 언어를 설정합니다 (기본값: ko)
📷 next_sign=false 적용 시 화면 비교
기존 완료 팝업next_sign=false 적용 팝업

URL 구성 예시

콜백 이벤트만 사용

https://docs.esignon.net/api/{companyId}/sign?token={token}&callback_fn=true

다음 문서 작성 버튼만 숨기기

https://docs.esignon.net/api/{companyId}/sign?token={token}&next_sign=false

콜백 + 다음 문서 작성 버튼 숨기기 (권장)

https://docs.esignon.net/api/{companyId}/sign?token={token}&callback_fn=true&next_sign=false

새 탭 / 새 창으로 열기

// 서명 URL 구성
const signPageUrl = `https://docs.esignon.net/api/${companyId}/sign?token=${token}&callback_fn=true&next_sign=false&lang=ko`;
 
// 새 탭으로 열기
window.open(signPageUrl, '_blank');
 
// 새 창으로 열기 (크기 지정)
window.open(signPageUrl, '_blank', 'width=1200, height=800, top=100, left=100');
⚠️

팝업 차단 주의

브라우저의 팝업 차단 기능이 활성화된 경우 서명 창이 열리지 않을 수 있습니다.
팝업 차단이 감지되면 사용자에게 팝업 허용을 안내하는 메시지를 표시하시기 바랍니다.

try {
  window.open(signPageUrl, '_blank');
} catch (e) {
  alert("팝업 차단이 설정되어 있습니다. 팝업을 허용한 후 다시 시도해 주세요.");
}

3. 서명 완료 처리하기

콜백 이벤트 수신

callback_fn=true로 설정한 경우, 서명 완료 후 팝업에서 닫기 버튼을 클릭하면 부모 페이지로 이벤트가 전달됩니다.
부모 페이지에서 아래와 같이 이벤트를 수신합니다.

window.addEventListener("message", function (e) {
  console.log(e.data); // 서명 완료 이벤트 데이터
  // 서명 완료 후 처리 로직 작성
});

Export API로 데이터 수신

서명자가 서명을 완료하면, 1단계에서 설정한 export_api_info.url로 서명 결과 데이터가 자동으로 전송됩니다.

📘

api_type 설정에 따라 수신 시점이 달라집니다

  • StartAndEnd — 문서 시작 시와 모든 서명 완료 시, 총 2번 수신합니다.
  • ALL — 각 서명자가 서명할 때마다 수신합니다. 다단계 서명에서 중간 진행 상황을 추적할 때 유용합니다.

Export API 이력 조회 및 재전송은 Export API 연동 가이드를 참고하시기 바랍니다.


주의사항

⚠️

API 호출은 반드시 서버 사이드에서 하시기 바랍니다

access_token은 민감한 인증 정보입니다. 브라우저 클라이언트에서 직접 API를 호출하면 토큰이 노출될 수 있습니다.
반드시 서버에서 API를 호출하고, 클라이언트에는 token 또는 sign_url만 전달하시기 바랍니다.

❌ 브라우저(JS) → eSignon API 직접 호출
✅ 브라우저(JS) → 고객사 서버 → eSignon API 호출 → token 반환 → 브라우저에서 서명 창 열기

샘플 코드

전체 흐름을 한 번에 실행할 수 있는 JavaScript 샘플 코드입니다.
startEsignonEmbed() 함수를 호출하면 토큰 발급 → 문서 시작 → 서명 창 열기까지 자동으로 진행됩니다.

// =============================================
// eSignon 임베디드 연동 샘플 코드
// =============================================
 
const ESIGNON_DOMAIN = "https://docs.esignon.net";
 
// -----------------------------------------------
// 1. 인증 토큰 발급
// -----------------------------------------------
async function getAccessToken(companyId, email, password) {
  const response = await fetch(`${ESIGNON_DOMAIN}/api/${companyId}/login`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      header: { request_code: "1001Q" },
      body: {
        memb_email: email,
        memb_pwd: password,
      },
    }),
  });
 
  const data = await response.json();
 
  if (data.header.result_code !== "00") {
    throw new Error(data.header.result_msg);
  }
 
  return data.body.access_token;
}
 
// -----------------------------------------------
// 2. 비대면 문서 시작 (임베디드)
// -----------------------------------------------
async function startWorkflow(accessToken, { workflowName, templateId, recipient, fieldList }) {
  const response = await fetch(`${ESIGNON_DOMAIN}/api/v3/workflows/start`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Authorization": `esignon ${accessToken}`,
    },
    body: JSON.stringify({
      workflow_name: workflowName,
      template_id: templateId,
      recipient_list: [
        { order: 1, name: recipient.name, email: recipient.email }
      ],
      field_list: fieldList || [],
      export_api_info: {
        url: "https://your-server.com/esignon/webhook", // 서명 완료 데이터 수신 URL
        api_type: "StartAndEnd",
        is_embed: true,
        link_type: "viewer"
      }
    }),
  });
 
  const data = await response.json();
  return data; // token, sign_url 포함
}
 
// -----------------------------------------------
// 3. 서명 창 열기
// -----------------------------------------------
function openSignWindow(companyId, token) {
  const signUrl = `${ESIGNON_DOMAIN}/api/${companyId}/sign?token=${token}`
    + `&callback_fn=true`   // 서명 완료 후 부모 페이지로 콜백 이벤트 전달
    + `&next_sign=false`    // 완료 팝업에서 "다음 문서 작성하기" 버튼 숨기기
    + `&lang=ko`;
 
  const popup = window.open(signUrl, "_blank", "width=1200,height=800,top=100,left=100");
 
  if (!popup) {
    alert("팝업이 차단되었습니다. 팝업 허용 후 다시 시도해 주세요.");
  }
}
 
// -----------------------------------------------
// 4. 서명 완료 콜백 수신
// -----------------------------------------------
window.addEventListener("message", function (e) {
  console.log("서명 완료 콜백:", e.data);
  // 서명 완료 후 처리 로직 작성
});
 
// -----------------------------------------------
// 전체 흐름 실행 예시
// -----------------------------------------------
async function startEsignonEmbed() {
  try {
    const companyId = "testapi2";
    const email = "[email protected]";
    const password = "yourPassword";
 
    // 1. 토큰 발급
    const accessToken = await getAccessToken(companyId, email, password);
 
    // 2. 문서 시작
    const result = await startWorkflow(accessToken, {
      workflowName: "2026_개인정보동의서_홍길동",
      templateId: 753,
      recipient: { name: "홍길동", email: "010-1234-5678" },
      fieldList: [
        { name: "이름", value: "홍길동" },
        { name: "개인정보동의", value: "true" }
      ]
    });
 
    // 3. 서명 창 열기
    openSignWindow(companyId, result.token);
 
  } catch (error) {
    console.error("오류 발생:", error);
    alert(error.message || "오류가 발생했습니다.");
  }
}