DocuSign APIは、電子署名サービスDocuSign(現在の公式表記はDocusign)の署名依頼の作成・送信・状態確認・締結済み書類の取得を、自社システムから行うためのAPI群です。中心になるのはeSignature REST APIで、送信単位の「エンベロープ」を組み立てて送ります。この記事では、JWT Grantでの認証と同意、テンプレートから署名依頼を送るPythonコード、Connect Webhookで締結完了を受け取りHMAC署名を検証する受信処理、本番で詰まりやすいAPIの上限とGo-Liveまでを順に説明します。数値と仕様の出典は、2026年11月時点のDocusign公式ドキュメントです。
まとめ:DocuSign APIで署名依頼を自動化する前に決める認証・通知・本番化の結論
基幹システムやワークフローから無人で署名依頼を送る連携なら、認証はJWT Grantを選びます。送信者となる社員や共通アカウントに一度だけ同意を取り、以後はRSA秘密鍵で1時間ごとにトークンを取り直す形です。送る書類はDocuSign側にテンプレートとして登録し、APIからは署名者の氏名とメールアドレスだけを差し込むと、コードが短く保てます。
締結完了の検知は、状態を取りに行くポーリングではなくConnect Webhookで受けてください。同じエンベロープの状態取得は15分に1回までという規則があり、違反は本番移行の審査で不合格の理由になります。受信側はHMAC署名を検証し、5秒以内に200を返してから非同期に処理します。
本番へ移すGo-Liveには、有料の本番アカウントが必要です。開発は無料のデモ環境で始められますが、本番の契約形態と送信数の見積もりは設計の初期に確かめておくと、手戻りがありません。
DocuSign APIでできることとeSignature API・SDK・環境の全体像
エンベロープ・受信者・タブで組み立てるeSignature REST APIの基本単位
eSignature REST APIでは、署名依頼の1件を「エンベロープ」と呼びます。エンベロープの中身は、書類(documents)、受信者(recipients)、受信者ごとの入力欄であるタブ(tabs)です。署名者(signer)のほかに、写しを受け取るだけのCC、署名順の指定なども受信者の設定で表します。
DocusignにはCLMやRooms、Click、Admin、Navigatorなど別のAPIもありますが、「自社システムから署名依頼を送って結果を受け取る」用途で使うのはeSignature REST APIです。この記事もeSignature REST APIに絞って説明します。電子署名そのものの法的な位置付けは電子署名の仕組みと法的効力の解説にまとめています。
デモ環境account-dと本番で分かれる認証サーバーとbase_uriの取り方
開発は無料の開発者アカウント(デモ環境)で行います。公式のPythonコード例の設定ファイルでは、デモ環境の認証サーバーがhttps://account-d.docusign.comになっており、本番ではaccount.docusign.comを使います。
APIの送り先はアカウントごとに異なります。トークン取得後に/oauth/userinfoを呼び、返ってきたアカウントのbase_uriに/restapiを付けたものがAPIの基点です。URLを固定値で書くと本番移行時に動かなくなるので、base_uriは必ずuserinfoから取ります。公式のリソース制限では、userinfoの呼び出しも利用者あたり毎時25,000回、統合キーあたり毎時50,000回までと決まっており、超えると429が返ります。base_uriはトークンと一緒にキャッシュしてください。
Python SDKの安定版6.1.0系と公式Quickstartで最初の1通を送る手順
公式SDKはPython、Node.js、Java、C#、PHP、Rubyで提供されています。Python版はdocusign-esign-python-clientで、PyPIのdocusign-esignの安定版は2026年11月時点で6.1.0系(2026年3月公開)です。7.0.0rc1以降の版はプレリリースなので、本番の連携では安定版に固定します。
最初の1通を送るまでの近道は公式のQuickstartです。開発者アカウントにログインして言語を選ぶと、アカウントの設定値と認証コードが入ったサンプル一式をダウンロードできます。対応言語はBash、C#、Java、Node.js、PHP、PowerShell、Python、Rubyの8つです。
JWT Grantでサービス連携の認証を通す手順と同意・1時間のトークン期限
認可コード方式とJWT Grantを無人連携かどうかで選ぶ判断基準
分かれ目は「操作する人がその場にいるか」です。利用者が自社の画面からDocuSignにログインして送る形ならAuthorization Code Grantを使います。夜間バッチや基幹システムの処理から無人で送るならJWT Grantです。公式のJWT Grantの解説は、JWTを「特定の利用者になりすまして長期的に動くサービス連携」向けと位置付けています。
JWT Grantではリフレッシュトークンが発行されず、アクセストークンは1時間で失効します。そのたびに新しいJWTを作って交換し直す仕組みで、ログイン画面を挟まない代わりに、送信者として使うアカウントへの同意が事前に1回必要です。なりすまし権限は強いので、送信専用の共通アカウントを用意し、そのアカウントにだけ同意させる運用を勧めます。
RSA鍵ペア登録から同意URL・アクセストークン取得までをPythonで試す
事前準備は3つです。開発者アカウントのApps and Keysで統合キー(Integration Key)を作る、同じ画面でRSA鍵ペアを生成して秘密鍵を保存する、送信者アカウントのUser ID(GUID)を控える。鍵ペアは1つの統合キーに最大5組までです。次のコードはJWTでのトークン取得手順と公式コード例の書き方に沿っています。
# pip install "docusign-esign==6.1.0"
import os
from docusign_esign import ApiClient
AUTH_SERVER = "account-d.docusign.com" # 本番は account.docusign.com
CLIENT_ID = os.environ["DS_INTEGRATION_KEY"]
USER_ID = os.environ["DS_USER_ID"] # なりすます送信者の User ID(GUID)
SCOPES = ["signature", "impersonation"]
# 初回だけ、送信者本人がブラウザで開いて同意する
consent_url = (
f"https://{AUTH_SERVER}/oauth/auth?response_type=code"
f"&scope={'+'.join(SCOPES)}&client_id={CLIENT_ID}"
f"&redirect_uri=https://developers.docusign.com/platform/auth/consent"
)
print(consent_url)
api = ApiClient()
api.set_oauth_host_name(AUTH_SERVER)
with open("private.key", "rb") as f:
private_key = f.read()
token = api.request_jwt_user_token(
client_id=CLIENT_ID,
user_id=USER_ID,
oauth_host_name=AUTH_SERVER,
private_key_bytes=private_key,
expires_in=3600,
scopes=SCOPES,
)
# API の基点はアカウントごとに違うため userinfo から取る
info = api.get_user_info(token.access_token)
account = info.get_accounts()[0]
base_path = account.base_uri + "/restapi"
print(account.account_id, base_path)
SDKを使わない場合は、トークン発行エンドポイントのPOST /oauth/tokenに、grant_type=urn:ietf:params:oauth:grant-type:jwt-bearerと署名済みJWTをassertionとして送ります。公式は失効の15分ほど前に取り直すよう勧めているので、トークンは取得時刻と一緒に保存し、残り15分を切ったら更新する処理にしておきます。
consent_requiredとinvalid_grantで止まるときの確認箇所
JWTの導入で止まる原因は、ほぼ次の2つに集約されます。
| エラー | 主な原因 | 確かめる箇所 |
|---|---|---|
| consent_required | 送信者アカウントの同意が済んでいない | 同意URLのscope |
| invalid_grant | JWTのiatとexpが不正、鍵の取り違え | サーバーの時刻と鍵の組 |
consent_requiredのときは、同意URLのscopeがsignature impersonationになっているかを見ます。invalid_grantはno_valid_keys_or_signaturesの説明付きで返るのが典型で、公式のトラブルシュートでは切り分けとしてiatを現在のUnix時刻、expをその4000秒後にして再実行する方法が紹介されています。コンテナやバッチサーバーの時計のずれも、コードが正しいのに失敗する原因の1つです。NTPで時刻を合わせることを先に確かめてください。
テンプレートから署名依頼を送るエンベロープ作成のリクエストと状態遷移
template_idとtemplateRolesで署名者を差し込む作成コード
契約書の様式が決まっているなら、書類と署名欄の配置をDocuSignの画面でテンプレートとして作り、APIからは署名者だけを差し込みます。テンプレートから送る手順では、テンプレート側で決めたロール名(例:signer)に、氏名とメールアドレスを割り当てる形です。先ほどのトークンとbase_pathを使うと次のようになります。
from docusign_esign import EnvelopeDefinition, EnvelopesApi, TemplateRole
api.host = base_path
api.set_default_header("Authorization", f"Bearer {token.access_token}")
envelope = EnvelopeDefinition(
status="sent", # "created" にすると下書きで止まる
template_id=os.environ["DS_TEMPLATE_ID"],
template_roles=[
TemplateRole(email="[email protected]", name="田中 一郎", role_name="signer"),
TemplateRole(email="[email protected]", name="法務部", role_name="cc"),
],
)
result, status, headers = EnvelopesApi(api).create_envelope_with_http_info(
account_id=account.account_id, envelope_definition=envelope
)
print(result.envelope_id, headers.get("X-RateLimit-Remaining"))
返ってくるenvelope_idは、自社の契約レコードに必ず保存します。後でWebhookを受けたときに、どの契約の締結かを突き合わせる唯一のキーになるためです。create_envelope_with_http_infoを使うと応答ヘッダーも受け取れるので、残りの呼び出し回数をログに出しておくと上限の監視に使えます。
sent・delivered・completedなどエンベロープ状態コードの読み方
エンベロープの状態コードのうち、連携で扱うのは次の6つです。
| 状態 | 意味 | 連携での扱い |
|---|---|---|
| created | 下書き。未送信 | 送信前の確認待ち |
| sent | 少なくとも1人に通知メールを送った | 契約を「署名待ち」にする |
| delivered | 全受信者が署名画面で書類を開いた | メールの到達ではない点に注意 |
| completed | 全受信者が完了 | 締結済みPDFを取得し契約を確定 |
| declined | 受信者のいずれかが署名を拒否 | 担当者へ通知 |
| voided | 送信者が無効化した、または期限切れ | 理由を確認して再送を判断 |
signedという状態もありますが、全員が署名した直後の一時的な状態で、自動的にcompletedへ移ります。締結の確定はcompletedで判定してください。deletedとtimedoutは旧来の状態で、現在は使われていません。
Connect Webhookで締結完了を受け取るリスナーとHMAC署名検証の実装
eventNotificationでエンベロープ単位に締結完了を購読する設定
DocuSign Connectには、アカウント全体のイベントを送るアカウント単位の設定と、エンベロープ作成時に通知先を指定するエンベロープ単位の設定があります。リスナー作成の解説によると、アカウント単位は一部のプランに限られ、エンベロープ単位は全プランで使えます。APIで送るエンベロープなら、エンベロープ単位から始めるのが確実です。
{
"status": "sent",
"templateId": "<テンプレートID>",
"templateRoles": [ { "roleName": "signer", "name": "田中 一郎", "email": "[email protected]" } ],
"eventNotification": {
"url": "https://api.example.co.jp/docusign/connect",
"requireAcknowledgment": "true",
"loggingEnabled": "true",
"deliveryMode": "SIM",
"events": ["envelope-sent", "envelope-completed", "envelope-declined", "envelope-voided"],
"eventData": { "version": "restv2.1" }
}
}
形式はJSON SIMが推奨です。1イベントごとに1通届き、本文のeventにenvelope-completedなどのイベント名、data.envelopeIdに対象のエンベロープID、retryCountに再送回数が入ります。ConnectにはBox、OneDrive、Salesforceへ書類を送る既製の設定もあり、締結済みPDFの保管先がBoxならBox APIの実装手順と組み合わせる方法もあります。
署名ヘッダーをHMAC-SHA256とBase64で照合するPythonの受信コード
ConnectのHMACの解説によると、管理画面でHMACの秘密鍵を作ると、鍵の数だけX-Docusign-Signature-1、-2…のヘッダーが付きます(最大100)。値は、改行を含む本文をHMAC-SHA256でハッシュしたもので、公式コード例ではBase64で符号化しています。どれか1つが一致すれば、DocuSignから届いた改ざんの無い通知と判断できます。
import base64
import hashlib
import hmac
import json
import os
from flask import Flask, abort, request
app = Flask(__name__)
HMAC_KEYS = [os.environ["DS_CONNECT_HMAC_KEY_1"]] # 鍵の入れ替え中は新旧2本を並べる
def is_valid(body: bytes) -> bool:
for i in range(1, 101):
received = request.headers.get(f"X-Docusign-Signature-{i}")
if received is None:
break
for key in HMAC_KEYS:
digest = hmac.new(key.encode("utf-8"), body, hashlib.sha256).digest()
if hmac.compare_digest(base64.b64encode(digest).decode("ascii"), received):
return True
return False
@app.post("/docusign/connect")
def connect_listener():
body = request.get_data() # 加工前の生のバイト列で計算する
if not is_valid(body):
abort(401)
event = json.loads(body)
enqueue(event["event"], event["data"]["envelopeId"], body) # 自社のキューへ積むだけ
return "", 200
JSONを読み込んでから文字列に戻すと空白や改行が変わり、ハッシュが一致しません。検証は必ず受け取ったままのバイト列で行います。比較に==ではなくhmac.compare_digestを使うのは、比較時間の差から値を推測される攻撃を避けるためです。署名検証と冪等性の考え方は、製品に依存しない形でWebhookの受信側実装の解説にも書いています。
5秒以内の200応答とキュー退避、届かない通知をlistFailuresで拾う運用
公式がリスナーに求める条件ははっきりしています。受け取った通知には、使わないものも含めて必ず200を返す。5秒以内に応答する。可用性は99%以上を保つ。重い処理は不揮発のキューに積んでから別のワーカーで行う。上のコードがenqueueだけで返しているのはこのためです。
ワーカー側では、envelope-completedを受けたら締結済みPDFを取得して保存し、契約レコードを確定させます。同じ通知が再送される場合に備え、書き込む前にエンベロープIDとイベント名の組で処理済みかを判定する作りが必要です。それでも届かなかった通知は、Connectの配信失敗の一覧APIで拾えます。締結PDFの冪等な格納と基幹への書き戻しは、契約書管理システムのAPI連携の実装手順で同じ流れを詳しく扱っています。
本番で詰まるAPI上限:毎時3,000件・30秒バーストと15分のポーリング規則
アカウント単位の毎時上限とデモ200件・本番500件の30秒バーストの数え方
公式のリソース制限の値は次のとおりです。
| 上限 | 値 | 応答ヘッダー |
|---|---|---|
| 1時間あたりの呼び出し | 既定3,000回(毎時0分にリセット) | X-RateLimit-* |
| 30秒バースト(デモ環境) | 200回 | X-BurstLimit-* |
| 30秒バースト(本番環境) | 500回 | X-BurstLimit-* |
残り回数はX-RateLimit-Remaining、リセット時刻はX-RateLimit-Reset、バーストの残りはX-BurstLimit-Remainingで分かります。どちらの上限もアカウント単位で、そのアカウントにつながる全ての連携の合計が対象です。販売管理からの送信と人事システムからの送信が同じアカウントを使っていれば、枠を分け合うことになります。月末に数百通をまとめて送る処理は、30秒あたりの回数を抑える間隔を入れて流してください。
同一エンベロープの状態取得は15分に1回、Go-Live審査で見られるポーリング違反
eSignature APIのルールでは、1つのエンベロープの状態取得は15分に1回までです。超えてもリクエスト自体は失敗しませんが、違反として記録され、Go-Liveの審査に落ちる原因になります。公式が勧めるのは20分間隔です。エンベロープの作成から送信までのAPI呼び出しを5回以内に収めることも、あわせて求められています。
結論として、状態はConnectで受け、ポーリングは保険にとどめます。リスナー設計のベストプラクティスでは、Webhookを置けない環境でも状態取得は4〜6時間に1回程度とし、複数のエンベロープはlistStatusChangesで1時間に1回ほどまとめて取る方法を示しています。
Go-Liveで本番へ移す条件:有料アカウント・キーの複製・設定の再登録
統合キーは複製され秘密鍵とリダイレクトURIは本番で登録し直す手順
Go-Liveの解説によると、本番へ移すには、有料の本番アカウントと、その管理者権限が必要です。2026年11月時点では、Developer Consoleでアプリを選び、Go-Liveの手続きから申請します。手動の審査になった場合は24〜48時間ほどかかります。
Go-Liveで統合キーは本番環境へ「複製」され、デモ環境でも同じキーを使い続けられます。ただし、シークレットやリダイレクトURIは複製の対象外なので、本番環境で設定し直す作業が必要です。JWTを使う場合は、本番側でもRSA鍵と送信者アカウントの同意がそろっているかを確かめ、同意は本番の認証サーバー(account.docusign.com)で取り直します。設定ファイルの認証サーバーとbase_uriの取得元を環境ごとに切り替える作りにしておけば、コードの修正なしで移行できます。
DocuSign API連携を自社開発すべき条件と見送るべき場面の判断
自社開発に進むべき条件は基幹データからの差し込みと締結後の自動書き戻し
APIを使う開発に投資する価値があるのは、次の2つが両方とも要件にある場合です。1つは、販売管理や人事システムの取引先・社員データから、氏名や金額を差し込んで署名依頼を送りたい場合。もう1つは、締結完了を受けて契約ステータス、締結日、締結済みPDFを基幹システムへ自動で書き戻したい場合です。片方だけなら、DocuSignの画面操作か既製の連携で足りることが多く、JWTの鍵管理やHMAC検証の保守を抱える理由が弱くなります。
標準連携やiPaaSで足りる場面と受託開発へ相談するときの連携要件による判断目安
Salesforceやクラウドストレージへの保存のように既製のConnect設定がある連携、月に数十通を手作業で送る運用なら、API開発は過剰です。そもそもDocuSignを使うか国内の電子契約サービスにするかが決まっていない段階なら、電子契約システムの比較と選び方やAdobe Acrobat SignのAPI連携の制約で製品を決めてから設計に入るほうが手戻りがありません。
一方、複数の基幹システムからの送信、締結データの双方向同期、海外拠点との契約で英語の書類と国内の書類を同じ仕組みで扱うといった要件は、認証方式の選定、上限を踏まえた送信設計、通知の取りこぼし対策まで含めて作る必要があります。一創のAPI開発・システム連携サービスでは、電子署名サービスと社内システムの連携を、要件整理から運用まで受託しています。
DocuSign APIの料金・開発者アカウント・制限について実装前によくある質問
DocuSign APIを使い始める実装者から出やすい質問に、公式ドキュメントの記載をもとに答えます。
DocuSign APIは無料で使えますか?
開発とテストは無料の開発者アカウント(デモ環境)でできます。デモ環境は検証用で、本番とは認証サーバーもAPIの送り先も別です。本番で使うにはGo-Liveが必要で、その条件として有料の本番アカウントが求められます。API向けの料金プランは地域や契約形態で異なり、公開ページの記載も改定されることがあるため、送信数の見込みを持ってDocusignの営業窓口で見積もりを取ってください。
JWTのアクセストークンが1時間で切れたらどうすればよいですか?
JWT Grantにはリフレッシュトークンがないため、新しいJWTを作り直して/oauth/tokenで交換します。同意は最初の1回で済んでいるので、再取得に人の操作は要りません。公式は失効の15分ほど前に取り直すよう勧めています。取得時刻をトークンと一緒に保存し、残りが15分を切った時点で更新すれば、送信処理の途中で401になる事態を避けられます。
Webhookが届かなかったエンベロープはどう拾えばよいですか?
Connectの配信失敗はlistFailuresで一覧でき、対象のエンベロープについて再送を依頼するAPIもあります。それとは別に、自社側で「送信済みなのに完了通知が来ていない契約」を夜間に抽出し、listStatusChangesでまとめて状態を確かめる照合ジョブを置くと確実です。エンベロープごとの状態取得を繰り返すと15分ルールに触れるので、まとめて取る呼び方を使います。
自社のWeb画面の中で署名してもらうことはできますか?
できます。メールで依頼を送るリモート署名のほかに、自社の画面から署名画面を開かせる埋め込み署名(Embedded Signing)があります。この場合は署名者がその場にいるため、利用者の操作を伴うAuthorization Code Grantも検討の対象です。公式のベストプラクティスでは、埋め込み署名の完了は、署名後に自社画面へ戻すリダイレクトURLのパラメータで判定する方法が効率的とされています。
クラウドサインなど国内サービスのAPIとは何が違いますか?
実装で最も差が出るのは、Webhookの真正性の確かめ方です。DocuSign ConnectはHMAC署名を本文に対して付けられるため、受信側で改ざんの有無を検証できます。署名の付かない通知を受けるサービスでは、受信URLを秘密にし、APIで状態を取り直して確かめる設計が必要です。国内サービス側の実装は契約書管理システムのAPI連携で、署名方式の違いは電子サインと電子署名の違いで解説しています。
関連記事
- 契約書管理システムのAPI連携:Webhook受信から締結PDFの冪等な格納・基幹同期までの実装手順:締結完了を受けた後の格納と基幹同期の実装
- 電子契約システムの比較と選び方とは?認証方式・連携・費用の判断軸を解説:DocuSignを含む電子契約サービスを選ぶ段階の判断材料
- Adobe Acrobat Signとは?API連携の制約と国内サービスとの選び分けを解説:海外の電子署名サービスをAPIでつなぐときの別の選択肢
- Webhookの作り方と実装方法|受信側の署名検証・冪等性と送信側の再送をNode.js・Pythonで解説:Connectの受信処理の土台になる考え方
- Box APIとは:認証4方式の選び方とファイル操作・メタデータ・権限付与の実装手順:締結済みPDFの保管先をBoxにするときの実装