Contents
1. Python SDK のインストールとクライアント初期化
DocuSign が公式に提供している docusign‑esign パッケージは、OAuth 認証・API 呼び出し用のラッパーをすべて備えた成熟した SDK です。まずは環境にインストールし、ベース URL とアクセストークンだけで利用できる ApiClient を作成します。
|
1 2 |
pip install docusign_esign |
1‑1. 基本モジュールと役割
| モジュール | 主な役割 |
|---|---|
ApiClient |
エンドポイント・認証情報を保持し、全リクエストの基礎となる |
AuthenticationApi (※OAuth 用) |
JWT および Authorization Code フローでアクセストークン取得 |
EnvelopesApi |
エンベロープ作成・送信・状態取得など |
AccountsApi |
アカウント情報やユーザー管理 |
注:SDK のバージョン 3.x 系以降では、トークン取得は
AuthenticationApiのlogin_optionsを使うのではなく、ApiClientのrequest_jwt_user_token/request_access_tokenメソッドを利用します。
1‑2. ApiClient の初期化例
|
1 2 3 4 5 6 7 8 |
from docusign_esign import ApiClient # デモ環境 (Sandbox) 用のベース URL BASE_PATH = "https://demo.docusign.net/restapi" api_client = ApiClient() api_client.host = BASE_PATH # base_path に相当 |
2. 認証方式の選択と実装
DocuSign では主に Authorization Code と JWT(JSON Web Token) の 2 種類が提供されています。以下でそれぞれのフローを最新 SDK に合わせて実装します。
2‑1. Authorization Code フロー(ユーザー操作が必要)
この方式は、Web アプリやデスクトップアプリでエンドユーザーに同意画面を表示させるケース向けです。
手順概要
1. 開発者ポータルで Integration Key とリダイレクト URI を作成
2. ユーザーを認可 URL にリダイレクトし、code(認可コード)を取得
3. 取得した code をアクセストークンに交換
実装サンプル
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
import urllib.parse from docusign_esign import AuthenticationApi, ApiException # ① 認可 URL の生成(ユーザーへ提示) def build_auth_url(integration_key: str, redirect_uri: str) -> str: base = "https://account-d.docusign.com/oauth/auth" params = { "response_type": "code", "scope": "signature impersonation", "client_id": integration_key, "redirect_uri": redirect_uri, } return f"{base}?{urllib.parse.urlencode(params)}" # ② 認可コードをアクセストークンに交換 def get_access_token_via_code( client_id: str, client_secret: str, code: str, redirect_uri: str, ) -> str: auth_api = AuthenticationApi(api_client) try: token_response = auth_api.generate_access_token( grant_type="authorization_code", code=code, client_id=client_id, client_secret=client_secret, redirect_uri=redirect_uri, ) return token_response.access_token except ApiException as e: raise RuntimeError(f"Token取得失敗: {e.body}") |
ポイント
-AuthenticationApi.generate_access_tokenの第一引数はgrant_type="authorization_code"です。
- 取得したトークンはapi_client.set_default_header("Authorization", f"Bearer {token}")で以降のリクエストに付与します。
2‑2. JWT フロー(バックエンド専用)
JWT はサーバー側だけで完結し、ユーザー操作が不要です。事前に Integration Key に Impersonation 権限を付与しておく必要があります。
必要なライブラリ
|
1 2 |
pip install PyJWT # jwt ライブラリ(公式名は pyjwt) |
実装サンプル(SDK 3.x 系)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
import time import jwt # PyJWT from docusign_esign import ApiException, AuthenticationApi def get_jwt_access_token( client_id: str, user_id: str, private_key_path: str, oauth_host: str = "account-d.docusign.com", expires_in: int = 3600, ) -> str: # ① JWT ペイロード作成 now = int(time.time()) payload = { "iss": client_id, # Integration Key (client_id) "sub": user_id, # ユーザー GUID(APIユーザー) "aud": oauth_host, "iat": now, "exp": now + expires_in, "scope": "signature impersonation", } # ② 秘密鍵で署名 with open(private_key_path, "rb") as key_file: private_key = key_file.read() assertion = jwt.encode(payload, private_key, algorithm="RS256") # ③ SDK のヘルパーメソッドでトークン取得 auth_api = AuthenticationApi(api_client) try: token_response = auth_api.request_jwt_user_token( client_id=client_id, user_id=user_id, oauth_host_name=oauth_host, private_key_bytes=private_key, expires_in=expires_in, scopes=["signature", "impersonation"], ) return token_response.access_token except ApiException as e: raise RuntimeError(f"JWT トークン取得失敗: {e.body}") |
重要
-AuthenticationApi.request_jwt_user_tokenが現在の公式メソッド名です(過去のgenerate_access_token_using_jwtは廃止)。
- 取得したアクセストークンは同様にapi_client.set_default_header("Authorization", f"Bearer {token}")で設定します。
3. サンドボックス環境と本番環境の切替え手順
DocuSign は Demo(Sandbox) と Production の 2 つのエンドポイントを提供しています。コードベースは同一でも、ベース URL と Integration Key が別々になる点に注意が必要です。
| 環境 | ベース URL | 主な利用シーン |
|---|---|---|
| Sandbox (Demo) | https://demo.docusign.net/restapi |
開発・テスト、無料アカウントでの検証 |
| Production | https://www.docusign.net/restapi |
本番リリース時に使用 |
切替え実装例
|
1 2 3 4 5 6 7 8 9 10 11 12 |
import os from docusign_esign import ApiClient ENV = os.getenv("DOCUSIGN_ENV", "demo") # 環境変数で切替える想定 BASE_PATH_MAP = { "demo": "https://demo.docusign.net/restapi", "prod": "https://www.docusign.net/restapi", } api_client = ApiClient() api_client.host = BASE_PATH_MAP[ENV] |
- 環境変数
DOCUSIGN_ENVをprodに変更するだけで本番エンドポイントに切り替わります。 - 本番移行時は必ず Integration Key(クライアント ID)とシークレット、RSA 鍵 も Production 用のものに差し替えてください。
4. エンベロープ作成・送信とメールカスタマイズ
エンベロープは「文書 + 受信者 + メール設定」の集合体です。以下では PDF の Base64 エンコードから、件名・本文の個別カスタマイズまでを網羅的に示します。
4‑1. 文書と受信者オブジェクトの作成
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
import base64 from docusign_esign import ( EnvelopesApi, EnvelopeDefinition, Document, Signer, Recipients, RecipientEmailNotification, ) # PDF を Base64 エンコード with open("contract.pdf", "rb") as f: pdf_b64 = base64.b64encode(f.read()).decode() document = Document( document_base64=pdf_b64, name="契約書", file_extension="pdf", document_id="1", ) signer = Signer( email="alice@example.com", name="Alice 山田", recipient_id="1", routing_order="1", ) |
4‑2. メール件名・本文の個別カスタマイズ
RecipientEmailNotification を Signer に直接設定すると、受信者ごとに異なるメール内容を送れます。
公式ブログ
「メール件名と本文のカスタマイズ」 (2020‑02‑18)
|
1 2 3 4 5 6 7 8 9 |
signer.email_notification = RecipientEmailNotification( email_subject="【ABC社】ご契約書への署名依頼", email_body=( "お世話になっております。\n" "添付の契約書をご確認の上、署名をお願いいたします。" ), ) recipients = Recipients(signers=[signer]) |
4‑3. EnvelopeDefinition にまとめて送信
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
envelope_definition = EnvelopeDefinition( email_subject="【重要】ご署名のお願い", # 全体の件名(個別が優先されます) email_blurb="このメールは自動送信です。以下の書類にご署名ください。", documents=[document], recipients=recipients, status="sent", # "created" にすると下書き保存になる ) envelopes_api = EnvelopesApi(api_client) result = envelopes_api.create_envelope( account_id=os.getenv("DOCUSIGN_ACCOUNT_ID"), envelope_definition=envelope_definition, ) print(f"Envelope ID: {result.envelope_id}") |
5. タブ取得と Connect Webhook の設定
署名完了後に入力されたフィールド(タブ)の値を取得したり、リアルタイムでステータス変化を受け取るには EnvelopesApi と EventNotification が鍵になります。
5‑1. タブ情報の取得
公式ブログ
「エンベロープのタブ情報取得」 (2020‑05‑21)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
from docusign_esign import EnvelopesApi, ApiException envelopes_api = EnvelopesApi(api_client) def list_recipient_tabs(account_id: str, envelope_id: str, recipient_id: str): try: tabs_resp = envelopes_api.list_tabs( account_id=account_id, envelope_id=envelope_id, recipient_id=recipient_id, ) for tab in tabs_resp.tab_labels: print(f"Tab label: {tab.tab_label}, Value: {tab.value}") except ApiException as e: raise RuntimeError(f"タブ取得失敗: {e.body}") # 呼び出し例 list_recipient_tabs( account_id=os.getenv("DOCUSIGN_ACCOUNT_ID"), envelope_id=result.envelope_id, recipient_id="1", ) |
5‑2. Connect(Webhook)設定の組み込み
Connect はエンベロープ単位でイベント通知を外部システムへプッシュします。以下は「送信」および「完了」イベントを受け取る最小構成です。
公式ブログ
「エンベロープに Connect Webhook を追加する」 (2020‑05‑27)
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
from docusign_esign import EventNotification, EnvelopeDefinition event_notification = EventNotification( url="https://yourapp.example.com/docusign/webhook", # HTTPS 必須 logging_enabled=True, require_acknowledgment=True, use_ssl=False, # True にすべきだが、ローカルテスト時は False も可 include_documents=True, envelope_events=[ {"envelope_event_status_code": "sent"}, {"envelope_event_status_code": "completed"}, ], recipient_events=[ {"recipient_event_status_code": "Completed"}, {"recipient_event_status_code": "Declined"}, ], ) # 既存の envelope_definition に組み込む envelope_definition.event_notification = event_notification # 再送信(Webhook が有効なエンベロープが作成される) result_with_webhook = envelopes_api.create_envelope( account_id=os.getenv("DOCUSIGN_ACCOUNT_ID"), envelope_definition=envelope_definition, ) print(f"Envelope (with webhook) ID: {result_with_webhook.envelope_id}") |
受信側実装のヒント
- DocuSign はX-DocuSign-Signatureヘッダーで署名検証を行います。必ずこのヘッダーをチェックしてください。
- POST ボディは JSON 形式で、eventEnvelopeオブジェクトにエンベロープ ID やステータスが含まれます。
6. エラーハンドリング・レートリミット対策・リトライ戦略
実運用では HTTP ステータスコードごとに適切な処理を行うことが成功率向上の鍵です。以下は 429(レートリミット) に対する指数バックオフ実装例です。
6‑1. 主なステータスと推奨アクション
| ステータス | 原因例 | 推奨対応 |
|---|---|---|
| 400 | パラメータ不正、必須ヘッダー欠損 | 入力検証・リクエスト構築ロジックの見直し |
| 401 | トークン期限切れ、無効トークン | 再度認証フローを実行(JWT の場合は再取得) |
| 403 | 権限不足、ユーザーが対象アカウントに未所属 | Integration Key の権限・ユーザー設定を確認 |
| 429 | レートリミット超過 | Exponential Backoff でリトライ |
| 500 系 | DocuSign 側の一時障害 | 数回リトライし、継続失敗は監視アラートへ |
6‑2. リトライユーティリティ
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import time from docusign_esign import ApiException def retry_on_rate_limit(func, max_attempts: int = 5): """429 を受けたら指数バックオフで再試行するデコレータ相当関数""" wait_seconds = 1 for attempt in range(1, max_attempts + 1): try: return func() except ApiException as e: if e.status == 429: # Rate limit print(f"[{attempt}] Rate limit hit – waiting {wait_seconds}s") time.sleep(wait_seconds) wait_seconds *= 2 # 1, 2, 4, 8 ... else: raise # その他は即座に例外送出 # 使用例(エンベロープ作成) envelopes_api = EnvelopesApi(api_client) result = retry_on_rate_limit( lambda: envelopes_api.create_envelope( account_id=os.getenv("DOCUSIGN_ACCOUNT_ID"), envelope_definition=envelope_definition, ) ) print(f"Created envelope ID: {result.envelope_id}") |
7. 本番環境移行チェックリスト
サンドボックスから本番へ切り替える際に見落としがちなのは「設定項目の差分」です。以下の項目を 必ず 確認してください。
| チェック項目 | 内容・確認ポイント |
|---|---|
| ベース URL | api_client.host が https://www.docusign.net/restapi になっているか |
| アカウント ID | サンドボックスのものから本番アカウント ID に置き換えたか |
| Integration Key / シークレット | 本番用に新規作成したものを使用しているか |
| リダイレクト URI | 本番環境で公開する URL が OAuth 設定に登録済みか |
| Connect Webhook の URL | HTTPS で外部から到達可能、証明書が有効か |
| レートリミット上限 | デフォルトは 1,000 req/HR。必要なら DocuSign に増枠依頼 |
| 監査ログ保存期間 | 法令・社内ポリシーに沿った保管設定を実装したか |
| テストケースの再実行 | 本番環境で全フロー(認証 → エンベロープ送信 → Webhook)を最低 1 回は走らせたか |
8. サンプルプロジェクトと活用方法
公式 GitHub リポジトリには、上記すべてのシナリオが動作するサンプルコードが格納されています。以下の手順でローカル環境にクローンし、実際に動かしてみましょう。
8‑1. クローンと依存パッケージインストール
|
1 2 3 4 5 6 7 8 9 |
git clone https://github.com/docusign/code-examples-python.git cd code-examples-python # Python 仮想環境作成(任意) python -m venv .venv source .venv/bin/activate # Windows は .venv\Scripts\activate pip install -r requirements.txt |
8‑2. 環境変数の設定例 (.env)
|
1 2 3 4 5 6 |
DOCUSIGN_INTEGRATION_KEY=YOUR_INTEGRATION_KEY DOCUSIGN_USER_ID=USER_GUID_WITH_IMPERSONATION DOCUSIGN_PRIVATE_KEY_PATH=private.key DOCUSIGN_ACCOUNT_ID=YOUR_ACCOUNT_ID DOCUSIGN_ENV=demo # 本番は prod に変更 |
.env ファイルは python-dotenv で自動ロードされます(requirements.txt に含まれています)。
8‑3. サンプル実行
|
1 2 3 4 |
python eg001CreateEnvelope.py # エンベロープ作成サンプル python eg002ListEnvelopes.py # エンベロープ一覧取得サンプル python eg003ConnectWebhook.py # Connect 設定サンプル(ローカルで受信テストは ngrok 推奨) |
ヒント
-ngrokなどのトンネルツールを使うと、ローカル開発環境でも HTTPS の Webhook エンドポイントを公開できます。
- サンプルコードは最新 SDK に合わせて随時更新されているため、リポジトリの README と release notes を定期的に確認してください。
9. まとめ
| 項目 | 要点 |
|---|---|
| SDK インストール | pip install docusign_esign → ApiClient にベース URL とトークン設定 |
| 認証方式 | UI が必要なら Authorization Code、サーバー側だけなら JWT(request_jwt_user_token) |
| 環境切替 | api_client.host と Integration Key を環境変数で管理し、Demo ↔ Production を明示的に切り替える |
| エンベロープ作成 | PDF の Base64 エンコード → EnvelopeDefinition に文書・受信者・メールカスタマイズを設定 |
| タブ取得 & Webhook | EnvelopesApi.list_tabs と EventNotification でリアルタイム通知を実装 |
| エラーハンドリング | 429 は指数バックオフ、401/403 は認証情報再確認、500 系はリトライ+監視 |
| 本番移行 | ベース URL・アカウント ID・鍵・Webhook の HTTPS 必須チェックを徹底 |
| サンプル活用 | 公式 GitHub リポジトリからコード取得 → .env で設定 → 実行テスト |
この手順通りに実装すれば、Python アプリケーションから DocuSign eSignature の全機能を安全かつスムーズに利用できます。問題が発生した場合は公式ドキュメント(https://developers.docusign.com/docs/esign-rest-api/)と GitHub の Issue トラッカーを参照してください。