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.