Migrating from DocuSign
This guide helps you migrate an existing DocuSign eSignature integration to Propper Sign API with minimal code changes.
What: Move your e-signature workflows from DocuSign to Propper Sign.
Why it's straightforward: Propper Sign offers backwards-compatible API endpoints for common DocuSign operations, so migrations require minimal code changes—primarily updating the base URL and authentication.
Two options: Switch everything at once (hard cutover) or run both systems in parallel during transition.
Propper Sign offers backwards-compatible API endpoints for core DocuSign v2.1 signing workflows, including envelope creation, embedded signing, recipient routing, document retrieval, and status tracking. See Feature Parity for detailed compatibility information.
- Migrating Data from DocuSign - Use DocuSign Connect webhooks to import historical/completed envelopes and keep status synced
- Sign Webhooks - Propper → your backend event notifications
- Embedded Signing - Recipient view setup and best practices
Who This Guide Is For
This guide is for you if:
- You currently call DocuSign eSignature REST API v2.1 from your backend and want to switch to Propper
- You want minimal code changes and a predictable cutover process
Overview
Most migrations involve three changes:
- Base URL — Change where API requests are sent (from DocuSign servers to Propper servers)
- Authentication — Change how your system proves its identity (from DocuSign's JWT tokens to Propper's OAuth client credentials)
- Webhook strategy — Decide how you'll receive notifications about signing events
Do not hardcode na1.docusign.net or any specific DocuSign region. DocuSign recommends retrieving the correct base_uri via the UserInfo endpoint ↗ (/oauth/userinfo). The base URI depends on where your DocuSign account is hosted.
Migration Paths
Choose the approach that fits your situation:
Option A: Hard Cutover
Switch base URL and auth in one deployment. Best for:
- Simple integrations with few endpoints
- No in-flight DocuSign envelopes to track
- Teams comfortable with a clean break
Option B: Parallel Operation + Sync
Keep DocuSign running during transition, import into Propper via Connect. Best for:
- Complex integrations needing gradual migration
- Existing DocuSign envelopes that need to complete
- Teams wanting to test Propper before full cutover
See Migration Strategy with DocuSign Connect for the parallel approach.
Quick Migration Checklist
- Update API base URL to Propper
- Replace DocuSign auth with Propper OAuth (client credentials)
- Swap DocuSign SDK usage for:
- Direct HTTP calls (recommended), or
- A thin compatibility wrapper
- Update webhook strategy:
- If you used DocuSign Connect, decide whether to keep it during transition
- If you need Propper outbound events, configure Propper Webhooks
- Test core flows end-to-end (see Testing Checklist)
- Review Feature Parity (especially tabs and templates)
- Update error handling for Propper error codes
API Endpoint Mapping
Base URL
| Environment | DocuSign | Propper |
|---|---|---|
| Production | https://{base_uri} (from UserInfo ↗) | https://api.propper.ai |
DocuSign base URI is account-region specific. Always fetch it from /oauth/userinfo rather than hardcoding a region like na1, eu, or au.
Endpoint Paths
Propper uses the same path structure as DocuSign for core eSignature v2.1 envelope operations. Simply replace the base URL:
| Operation | Path |
|---|---|
| Create envelope | POST /accounts/{accountId}/envelopes |
| Get envelope | GET /accounts/{accountId}/envelopes/{envelopeId} |
| List envelopes | GET /accounts/{accountId}/envelopes |
| Update envelope | PUT /accounts/{accountId}/envelopes/{envelopeId} |
| Get recipients | GET /accounts/{accountId}/envelopes/{envelopeId}/recipients |
| Recipient view | POST /accounts/{accountId}/envelopes/{envelopeId}/views/recipient |
| Sender view | POST /accounts/{accountId}/envelopes/{envelopeId}/views/sender |
| List documents | GET /accounts/{accountId}/envelopes/{envelopeId}/documents |
| Download document | GET /accounts/{accountId}/envelopes/{envelopeId}/documents/{documentId} |
All paths are prefixed with /restapi/v2.1. See the DocuSign Envelopes API reference ↗ for detailed request/response schemas.
In Propper, the {accountId} path parameter is your Propper Organization ID. Find this in your Organization Settings.
Known Differences
| Field/Feature | DocuSign | Propper | Notes |
|---|---|---|---|
| Account ID | DocuSign account GUID | Propper org ID | Different value, same path position |
Recipient view returnUrl | Required | Required | Same field, same behavior |
Recipient view clientUserId | Required for embedded | Required for embedded | Same field, same behavior |
Authentication Migration
DocuSign JWT → Propper OAuth
DocuSign JWT grant ↗ is an OAuth flow for service integrations that commonly uses the signature and impersonation scopes (requiring user consent).
Propper uses OAuth client credentials, which is simpler—no user consent flow required:
- DocuSign JWT (Before)
- Propper OAuth (After)
// DocuSign JWT authentication
const docusign = require('docusign-esign');
const apiClient = new docusign.ApiClient();
// Note: In production, get base_uri from /oauth/userinfo
apiClient.setBasePath('https://na1.docusign.net/restapi');
const results = await apiClient.requestJWTUserToken(
integrationKey,
userId,
'signature',
privateKey,
3600,
);
const accessToken = results.body.access_token;
// Propper OAuth client credentials
const getAccessToken = async () => {
const response = await fetch('https://auth.propper.ai/oauth2/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
grant_type: 'client_credentials',
client_id: process.env.PROPPER_CLIENT_ID,
client_secret: process.env.PROPPER_CLIENT_SECRET,
scope: 'sign:read sign:write',
}),
});
if (!response.ok) {
throw new Error('Failed to get access token');
}
const { access_token, expires_in } = await response.json();
return { accessToken: access_token, expiresIn: expires_in };
};
Scope Mapping
| DocuSign Scope | Propper Scope | Notes |
|---|---|---|
signature | sign:read sign:write | Full signing access |
extended | sign:read sign:write | Treated as standard signing access |
impersonation | Not supported | Use Propper service credentials instead |
DocuSign scope definitions are documented in their OAuth scopes reference ↗.
SDK Migration Options
Option A: Replace SDK with Direct HTTP Calls (Recommended)
This is the most portable approach and avoids SDK coupling:
- DocuSign SDK (Before)
- Direct API (After)
const docusign = require('docusign-esign');
const envelopesApi = new docusign.EnvelopesApi(apiClient);
const envelope = await envelopesApi.createEnvelope(accountId, {
envelopeDefinition: {
emailSubject: 'Please sign',
documents: [{ documentBase64: '...', documentId: '1', name: 'Contract.pdf' }],
recipients: {
signers: [{ email: 'signer@example.com', name: 'John', recipientId: '1' }],
},
status: 'sent',
},
});
const createEnvelope = async (accountId, accessToken) => {
const response = await fetch(
`https://api.propper.ai/restapi/v2.1/accounts/${accountId}/envelopes`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
emailSubject: 'Please sign',
documents: [{ documentBase64: '...', documentId: '1', name: 'Contract.pdf' }],
recipients: {
signers: [{ email: 'signer@example.com', name: 'John', recipientId: '1' }],
},
status: 'sent',
}),
},
);
return response.json();
};
Option B: Thin Compatibility Wrapper
If you have extensive SDK usage, create a compatibility wrapper to minimize code changes:
// propper-docusign-compat.js
class PropperEnvelopesApi {
constructor(baseUrl, accessToken) {
this.baseUrl = baseUrl;
this.accessToken = accessToken;
}
async createEnvelope(accountId, { envelopeDefinition }) {
const response = await fetch(`${this.baseUrl}/restapi/v2.1/accounts/${accountId}/envelopes`, {
method: 'POST',
headers: {
Authorization: `Bearer ${this.accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(envelopeDefinition),
});
return response.json();
}
async getEnvelope(accountId, envelopeId) {
const response = await fetch(
`${this.baseUrl}/restapi/v2.1/accounts/${accountId}/envelopes/${envelopeId}`,
{
headers: { Authorization: `Bearer ${this.accessToken}` },
},
);
return response.json();
}
async createRecipientView(accountId, envelopeId, viewRequest) {
const response = await fetch(
`${this.baseUrl}/restapi/v2.1/accounts/${accountId}/envelopes/${envelopeId}/views/recipient`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${this.accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(viewRequest),
},
);
return response.json();
}
// Add more methods as needed...
}
// Usage - minimal code changes from DocuSign SDK
const envelopesApi = new PropperEnvelopesApi('https://api.propper.ai', accessToken);
const envelope = await envelopesApi.createEnvelope(accountId, {
envelopeDefinition: {
/* same structure as before */
},
});
Embedded Signing Migration
When migrating recipient views (embedded signing ↗), ensure your request includes the required fields:
// Recipient view request - same structure for both DocuSign and Propper
const viewRequest = {
returnUrl: 'https://yourapp.com/signing-complete', // Required
authenticationMethod: 'none',
clientUserId: 'user-123', // Required for embedded signing
email: 'signer@example.com',
userName: 'John Smith',
recipientId: '1',
};
const response = await fetch(
`${baseUrl}/restapi/v2.1/accounts/${accountId}/envelopes/${envelopeId}/views/recipient`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(viewRequest),
},
);
const { url } = await response.json();
// Redirect user to `url` for signing
Request/Response Compatibility
For core envelope operations, Propper follows DocuSign v2.1 request shapes (envelopeDefinition, recipients, documents, tabs). The DocuSign envelope resource reference ↗ is the baseline for payload structure.
Create Envelope Request
{
"emailSubject": "Please sign this document",
"emailBlurb": "Please review and sign at your convenience.",
"status": "sent",
"documents": [
{
"documentId": "1",
"name": "Contract.pdf",
"documentBase64": "JVBERi0xLjQKJeLjz9M...",
"order": "1"
}
],
"recipients": {
"signers": [
{
"email": "signer@example.com",
"name": "John Smith",
"recipientId": "1",
"routingOrder": "1",
"clientUserId": "user-123",
"tabs": {
"signHereTabs": [
{
"documentId": "1",
"pageNumber": "1",
"xPosition": "100",
"yPosition": "500"
}
]
}
}
]
}
}
This exact payload structure works with both DocuSign and Propper.
Webhooks: Choose the Right Model
DocuSign Connect and Propper Webhooks are separate systems with different purposes:
| System | Direction | Purpose |
|---|---|---|
| DocuSign Connect | DocuSign → Propper | Import envelopes, sync status during migration |
| Propper Webhooks | Propper → Your app | Notify your backend of Propper events |
If You Previously Used DocuSign Connect
DocuSign Connect is DocuSign's webhook system with defined event triggers ↗ (e.g., envelope-completed, recipient-completed).
During migration, you can keep DocuSign Connect running and configure Propper to receive those events. This is recommended for gradual transition and historical import. See Migrating Data from DocuSign.
If You Want Propper → Your Backend Notifications
Use Propper Webhooks and subscribe to Propper event names.
Propper Webhook Event Names:
| Event | Description |
|---|---|
document.sent | Agreement sent for signature |
document.viewed | Recipient opened the signing page |
document.signed | Individual recipient completed signing |
document.completed | All recipients completed, envelope finalized |
document.declined | Recipient declined to sign |
document.voided | Envelope was cancelled |
Do not reuse your DocuSign Connect HMAC key for Propper webhook verification. These are separate systems with separate secrets. Generate a new webhook secret in the Propper dashboard.
Running Both During Migration
You can run DocuSign Connect (DocuSign → Propper) and Propper Webhooks (Propper → your app) simultaneously:
DocuSign → DocuSign Connect → Propper (imports envelope)
↓
Propper Webhooks → Your Backend (notifies of import)
Feature Parity
Fully Supported (Core Workflows)
| Feature | Compatibility | Notes |
|---|---|---|
| Envelope create/send | ✅ Full | Same API structure |
| Multiple recipients | ✅ Full | Sequential and parallel routing |
| Embedded signing (recipient view) | ✅ Full | Same views/recipient ↗ API |
| Document upload (Base64) | ✅ Full | Same format |
| Envelope status tracking | ✅ Full | DocuSign-compatible lifecycle ↗ |
| Document download | ✅ Full | Same documents API |
| Void envelopes | ✅ Full | Same API |
| Audit trail | ✅ Full | JSON and PDF certificate |
Tabs (Signature Fields)
DocuSign supports many tab types ↗. Propper supports the most commonly used:
| Tab Type | Status | Notes |
|---|---|---|
signHereTabs | ✅ Supported | Primary signature placement |
initialHereTabs | ✅ Supported | Initial placement |
dateSignedTabs | ✅ Supported | Auto-filled signing date |
textTabs | ✅ Supported | Free-form text input |
fullNameTabs | ✅ Supported | Auto-filled signer name |
emailTabs | ✅ Supported | Auto-filled signer email |
companyTabs | ✅ Supported | Company name field |
titleTabs | ✅ Supported | Title/role field |
checkboxTabs | ⚠️ Partial | Basic support, complex validation coming |
radioGroupTabs | ⚠️ Partial | Basic support |
listTabs (dropdowns) | ⚠️ Partial | Basic support |
formulaTabs | ❌ Not yet | Calculated fields on roadmap |
notaryTabs | ❌ Not yet | Notarization not supported |
attachmentTabs | ❌ Not yet | On roadmap |
If your integration uses unsupported tab types, contact support to discuss alternatives or timeline.
Templates
| Feature | Status | Notes |
|---|---|---|
| Create templates | ✅ Supported | Same template API structure |
| Use templates for envelopes | ✅ Supported | Reference by template ID |
| Template roles | ✅ Supported | Map recipients to roles |
| Template tabs | ⚠️ Partial | Depends on tab type support |
Not Supported (Use Alternatives)
| Feature | Alternative |
|---|---|
| DocuSign Admin API | Use Propper Admin dashboard |
| Bulk send | Loop with standard envelope API |
| PowerForms | Use embedded signing with your own forms |
| SMS delivery | Email delivery only |
| In-person signing | Standard embedded signing |
| IDV (ID Verification) | On roadmap |
Error Handling
Propper Error Response Format
All Propper API errors return a consistent structure:
{
"error": {
"code": "ENVELOPE_NOT_FOUND",
"message": "The requested envelope does not exist",
"details": {
"envelopeId": "abc123-def456"
},
"requestId": "req_7f8g9h0i1j2k"
}
}
| Field | Description |
|---|---|
code | Machine-readable error code for programmatic handling |
message | Human-readable error description |
details | Additional context (optional, varies by error) |
requestId | Unique identifier for support/debugging |
Error Code Mapping
| DocuSign Error | Propper Error | HTTP Status |
|---|---|---|
ENVELOPE_NOT_IN_CORRECT_STATE | ENVELOPE_CANNOT_BE_MODIFIED | 400 |
ENVELOPE_DOES_NOT_EXIST | ENVELOPE_NOT_FOUND | 404 |
RECIPIENT_NOT_IN_SEQUENCE | RECIPIENT_NOT_FOUND | 404 |
INVALID_REQUEST_BODY | INVALID_REQUEST_BODY | 400 |
USER_AUTHENTICATION_FAILED | UNAUTHORIZED | 401 |
USER_NOT_AUTHORIZED_FOR_ACCOUNT | FORBIDDEN | 403 |
HOURLY_API_LIMIT_EXCEEDED | RATE_LIMIT_EXCEEDED | 429 |
Step-by-Step Migration
1. Update Environment Variables
# Before (DocuSign)
# Note: Don't hardcode na1 - use UserInfo to get base_uri
DOCUSIGN_BASE_URL=https://na1.docusign.net
DOCUSIGN_ACCOUNT_ID=abc123
DOCUSIGN_INTEGRATION_KEY=xyz789
DOCUSIGN_USER_ID=user123
DOCUSIGN_PRIVATE_KEY_PATH=./private.key
# After (Propper)
PROPPER_BASE_URL=https://api.propper.ai
PROPPER_AUTH_URL=https://auth.propper.ai
PROPPER_ORG_ID=your-org-id
PROPPER_CLIENT_ID=your-client-id
PROPPER_CLIENT_SECRET=your-client-secret
2. Update Authentication
Replace DocuSign JWT with Propper OAuth client credentials:
// auth.js
let tokenCache = { token: null, expiresAt: 0 };
const getAccessToken = async () => {
// Return cached token if still valid
if (tokenCache.token && Date.now() < tokenCache.expiresAt) {
return tokenCache.token;
}
const response = await fetch(`${process.env.PROPPER_AUTH_URL}/oauth2/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
grant_type: 'client_credentials',
client_id: process.env.PROPPER_CLIENT_ID,
client_secret: process.env.PROPPER_CLIENT_SECRET,
scope: 'sign:read sign:write',
}),
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Auth failed: ${error.error?.message || response.statusText}`);
}
const { access_token, expires_in } = await response.json();
// Cache token with 1 minute buffer
tokenCache = {
token: access_token,
expiresAt: Date.now() + expires_in * 1000 - 60000,
};
return access_token;
};
3. Update Base URL
// Before (DocuSign)
// const BASE_URL = 'https://na1.docusign.net/restapi/v2.1';
// After (Propper)
const BASE_URL = 'https://api.propper.ai/restapi/v2.1';
4. Verify Embedded Signing
Test that recipient views work correctly:
// Generate signing URL
const response = await fetch(
`${BASE_URL}/accounts/${orgId}/envelopes/${envelopeId}/views/recipient`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
returnUrl: 'https://yourapp.com/signing-complete',
authenticationMethod: 'none',
clientUserId: 'user-123',
email: 'signer@example.com',
userName: 'John Smith',
recipientId: '1',
}),
},
);
const { url } = await response.json();
// Redirect user to `url` - signer should complete successfully
5. Decide Your Sync Strategy
Hard cutover:
- Stop creating DocuSign envelopes on cutover date
- Only import in-flight DocuSign envelopes until they complete
Parallel operation:
- Keep DocuSign Connect importing into Propper
- Gradually shift new envelope creation to Propper API
- See Migration Strategy with DocuSign Connect
Golden Path Example (End-to-End)
Here's a complete flow showing the full signing lifecycle:
// 1. Get access token
const accessToken = await getAccessToken();
// 2. Create and send envelope
const createResponse = await fetch(`${BASE_URL}/accounts/${orgId}/envelopes`, {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
emailSubject: 'Please sign: Sales Agreement',
status: 'sent',
documents: [
{
documentId: '1',
name: 'Agreement.pdf',
documentBase64: pdfBase64Content,
},
],
recipients: {
signers: [
{
email: 'customer@example.com',
name: 'Jane Customer',
recipientId: '1',
clientUserId: 'customer-456', // For embedded signing
tabs: {
signHereTabs: [
{ documentId: '1', pageNumber: '1', xPosition: '200', yPosition: '600' },
],
},
},
],
},
}),
});
const { envelopeId } = await createResponse.json();
console.log('Created envelope:', envelopeId);
// 3. Generate embedded signing URL
const viewResponse = await fetch(
`${BASE_URL}/accounts/${orgId}/envelopes/${envelopeId}/views/recipient`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
returnUrl: 'https://yourapp.com/signing-complete?envelopeId=' + envelopeId,
authenticationMethod: 'none',
clientUserId: 'customer-456',
email: 'customer@example.com',
userName: 'Jane Customer',
recipientId: '1',
}),
},
);
const { url: signingUrl } = await viewResponse.json();
console.log('Redirect user to:', signingUrl);
// 4. After signing completes, check status
const statusResponse = await fetch(`${BASE_URL}/accounts/${orgId}/envelopes/${envelopeId}`, {
headers: { Authorization: `Bearer ${accessToken}` },
});
const envelope = await statusResponse.json();
console.log('Envelope status:', envelope.status); // "completed"
// 5. Download signed document
const docResponse = await fetch(
`${BASE_URL}/accounts/${orgId}/envelopes/${envelopeId}/documents/1`,
{
headers: { Authorization: `Bearer ${accessToken}` },
},
);
const signedPdf = await docResponse.arrayBuffer();
console.log('Downloaded signed PDF:', signedPdf.byteLength, 'bytes');
Testing Checklist
Use this checklist to verify your migration. Each checkpoint has specific pass/fail criteria.
Checkpoint 1: Authentication
| Test | Command | Expected Result |
|---|---|---|
| Get token | POST /oauth2/token with credentials | Returns access_token and expires_in |
| Token format | Inspect response | access_token is non-empty string |
| Token refresh | Request new token near expiry | New token returned successfully |
# Test command
curl -X POST https://auth.propper.ai/oauth2/token \
-H "Content-Type: application/json" \
-d '{"grant_type":"client_credentials","client_id":"...","client_secret":"...","scope":"sign:read sign:write"}'
Checkpoint 2: Envelope Operations
| Test | Expected Result |
|---|---|
| Create draft envelope (status: "created") | Returns envelopeId, status is "created" |
| Get envelope by ID | Returns envelope with correct fields |
| List envelopes | Returns array including created envelope |
| Send envelope (status: "sent") | Status changes to "sent" |
Checkpoint 3: Embedded Signing Flow
| Test | Expected Result |
|---|---|
| Create recipient view URL | Returns valid url field |
| Navigate to signing URL | Signing page loads, no errors |
| Complete signature | Redirected to returnUrl |
| Check envelope status | Status is "completed" |
Checkpoint 4: Document Retrieval
| Test | Expected Result |
|---|---|
| List documents | Returns document list with IDs |
| Download signed PDF | Returns valid PDF binary |
| Verify PDF | PDF opens, shows signatures |
Checkpoint 5: Webhooks (If Applicable)
| Test | Expected Result |
|---|---|
| Receive webhook on envelope send | Event received, signature verified |
| Receive webhook on completion | Event received with envelope data |
| Invalid signature rejected | 401 returned, event not processed |
Common Migration Issues
"Account not found" errors
Cause: Using your DocuSign account ID instead of your Propper organization ID.
Solution: Get your organization ID from Organization Settings in the Propper dashboard.
Authentication failures
Cause: Using DocuSign JWT assumptions (impersonation, user-specific tokens).
Solution: Switch to OAuth client credentials. See DocuSign JWT flow documentation ↗ for what you're migrating from.
Webhook signature verification fails
Cause: Using DocuSign Connect HMAC key for Propper webhooks.
Solution: These are separate systems with separate secrets. Get your Propper webhook secret from the dashboard.
Tabs missing or not rendering
Cause: Using a tab type not yet supported in Propper.
Solution: Check the Tabs compatibility table. Use supported tab types or contact support for timeline on specific tabs.
"Base URL not reachable" in production
Cause: Firewall or network rules allowing only DocuSign domains.
Solution: Allow outbound traffic to api.propper.ai and auth.propper.ai.
Migration Strategy with DocuSign Connect
For gradual migrations, use DocuSign Connect to keep both systems in sync.
Phase 1: Set Up Sync
- Configure DocuSign Connect in Propper (setup guide)
- Enable auto-import for completed envelopes
- Test with a single envelope
- ✅ Checkpoint: New DocuSign completions appear in Propper
Phase 2: Parallel Operation
- Continue creating envelopes in DocuSign
- Completed envelopes sync to Propper automatically
- Start testing Propper API for new workflows
- ✅ Checkpoint: Can create and complete envelopes in both systems
Phase 3: Gradual Cutover
- New workflows use Propper API
- Existing DocuSign envelopes continue syncing
- Train users on Propper interface
- ✅ Checkpoint: Majority of new envelopes created in Propper
Phase 4: Full Migration
- All new envelopes created via Propper API
- DocuSign Connect continues for any in-flight envelopes
- Once all DocuSign envelopes complete, optionally disable Connect
- ✅ Checkpoint: No new DocuSign envelopes, all operations in Propper
AI-Assisted Migration
Use this prompt with Claude, ChatGPT, or your preferred AI assistant to analyze your codebase and generate a structured migration plan:
📋 Copy Migration Assistant Prompt
You are an expert at migrating DocuSign eSignature REST API v2.1 integrations to Propper Sign API.
## Your Task
Scan the repository and identify all DocuSign usages, then produce a structured migration plan.
## What to Search For
1. **SDK imports:**
- `docusign-esign` (Node.js)
- `DocuSign.eSign` (.NET)
- Similar patterns in other languages
2. **Direct REST calls:**
- URLs containing `docusign.net` or `docusign.com`
- Paths matching `/restapi/v2.1/accounts/`
3. **Auth flows:**
- JWT token requests (`requestJWTUserToken`)
- Auth code flow implementations
- Integration key / user ID references
4. **Recipient views (embedded signing):**
- `/views/recipient` endpoint calls
- `clientUserId` usage patterns
5. **Webhook handlers:**
- DocuSign Connect event processing
- HMAC signature verification
- Event types handled (envelope-completed, recipient-completed, etc.)
6. **Environment variables:**
- `DOCUSIGN_*` prefixed variables
- Base URL configurations
## Required Output
Produce the following sections:
### 1. Inventory Table
| File | Line(s) | Usage Type | DocuSign Feature |
|------|---------|------------|------------------|
| path/to/file.js | 45-60 | SDK import | Authentication |
| ... | ... | ... | ... |
### 2. Tab Types Used
List all tab types found in envelope creation code:
- signHereTabs: ✅ Supported
- dateSignedTabs: ✅ Supported
- formulaTabs: ❌ Not supported (needs alternative)
### 3. Auth Flow Summary
Current: [JWT / Auth Code / other]
Migration: OAuth client credentials
### 4. Webhook Flows
Events currently handled:
- envelope-completed → document.completed
- recipient-completed → document.signed
### 5. PR-Sized Migration Steps
Break the migration into reviewable chunks:
**PR 1: Authentication Migration**
Files: auth.js, config.js
Changes: Replace JWT with client credentials
**PR 2: Base URL Update**
Files: api-client.js
Changes: Update base URL, account ID
**PR 3: Webhook Handler Update**
Files: webhooks/docusign.js
Changes: Update event names, add Propper signature verification
### 6. Compatibility Risks
| Risk | Impact | Mitigation |
|------|--------|------------|
| formulaTabs not supported | Medium | Replace with textTabs, calculate server-side |
| ... | ... | ... |
### 7. End-to-End Test Plan
After migration, verify:
1. [ ] Create envelope with all tab types used
2. [ ] Generate embedded signing URL
3. [ ] Complete signing flow
4. [ ] Verify webhook received with correct event
5. [ ] Download signed document
6. [ ] Confirm audit trail available
Start by searching the codebase for DocuSign integration points.
For best results, use this prompt with an AI coding assistant that has access to your codebase (like Claude Code, Cursor, or GitHub Copilot). The AI can search files directly and provide specific paths and line numbers.
Need Help?
- Migrating Data from DocuSign - Complete DocuSign Connect setup and data import
- Sign Webhooks - Configure Propper outbound webhooks
- API Reference - Full endpoint documentation
- Support - Contact our migration team