E-Signature API Integration: Best Practices & Code Examples
Back to BlogDeveloper

E-Signature API Integration: Best Practices & Code Examples

Learn how to integrate e-signature workflows into your application. Complete guide with REST API examples, webhooks, error handling, and security best practices.

David Park

Senior Developer

Dec 28, 202518 min read

E-Signature API Integration: Best Practices & Code Examples

Integrating e-signatures into your application shouldn't be complicated. Whether you're building a contract management system, HR portal, or custom business application, this guide shows you how to implement bulletproof e-signature workflows.

API Architecture Overview

RESTful Design Principles

Space Sign's API follows REST best practices:

Base URL: `https://api.spacesign.com/v1`

Authentication: Bearer tokens

```http

Authorization: Bearer YOUR_API_KEY

```

Response Format: JSON

```json

{

"status": "success",

"data": { ... },

"meta": { ... }

}

```

Core Integration Workflows

1. Send a Document for Signature

Basic Flow:

1. Upload document

2. Define signers

3. Place signature fields

4. Send for signing

Code Example (Node.js):

```javascript

const SpaceSign = require('@spacesign/sdk');

const client = new SpaceSign({ apiKey: process.env.SPACESIGN_API_KEY });

async function sendContract() {

try {

// Step 1: Upload document

const document = await client.documents.upload({

file: './contract.pdf',

title: 'Employment Agreement - John Doe'

});

// Step 2: Add signers

const envelope = await client.envelopes.create({

documentId: document.id,

signers: [

{

email: 'john.doe@example.com',

name: 'John Doe',

role: 'Employee',

order: 1

},

{

email: 'hr@company.com',

name: 'Jane Smith',

role: 'HR Manager',

order: 2

}

]

});

// Step 3: Add signature fields (or use AI auto-tag)

await client.envelopes.addFields(envelope.id, {

fields: [

{

type: 'signature',

signer: 'john.doe@example.com',

page: 1,

x: 100,

y: 500,

width: 200,

height: 50

},

{

type: 'date',

signer: 'john.doe@example.com',

page: 1,

x: 100,

y: 550

}

]

});

// Step 4: Send envelope

const sent = await client.envelopes.send(envelope.id, {

message: 'Please review and sign your employment agreement.',

subject: 'Action Required: Employment Agreement'

});

console.log(`Envelope sent! ID: ${sent.id}`);

return sent;

} catch (error) {

console.error('Error sending contract:', error);

throw error;

}

}

```

2. AI-Powered Auto-Tagging

Skip manual field placement - let AI do it:

```javascript

async function sendWithAutoTag() {

const document = await client.documents.upload({

file: './contract.pdf',

title: 'Sales Agreement'

});

// AI automatically detects signature locations

const analysis = await client.ai.analyzeDocument(document.id);

const envelope = await client.envelopes.create({

documentId: document.id,

signers: [{ email: 'client@example.com', name: 'Client Name' }],

autoTag: true // Magic happens here

});

await client.envelopes.send(envelope.id);

}

```

3. Embedded Signing

Integrate signing directly into your app:

```javascript

async function getSigningURL() {

const envelope = await client.envelopes.create({ / ... / });

// Generate embedded signing URL

const signingSession = await client.envelopes.createSigningSession(

envelope.id,

{

signerEmail: 'john.doe@example.com',

returnUrl: 'https://yourapp.com/signing-complete',

expiresIn: 3600 // 1 hour

}

);

return signingSession.url;

// Redirect user to this URL or embed in iframe

}

```

Frontend Implementation:

```html

<iframe

src="{{signingUrl}}"

width="100%"

height="600"

allow="camera; microphone"

></iframe>

```

Webhook Event Handling

Setting Up Webhooks

Register webhook endpoint:

```javascript

await client.webhooks.create({

url: 'https://yourapp.com/webhooks/spacesign',

events: [

'envelope.sent',

'envelope.delivered',

'envelope.signed',

'envelope.completed',

'envelope.declined',

'envelope.expired'

],

secret: 'your-webhook-secret'

});

```

Webhook Handler (Express.js)

```javascript

const express = require('express');

const crypto = require('crypto');

const app = express();

app.use(express.json());

// Verify webhook signature

function verifyWebhookSignature(payload, signature, secret) {

const hash = crypto

.createHmac('sha256', secret)

.update(JSON.stringify(payload))

.digest('hex');

return crypto.timingSafeEqual(

Buffer.from(signature),

Buffer.from(hash)

);

}

app.post('/webhooks/spacesign', async (req, res) => {

const signature = req.headers['x-spacesign-signature'];

// Verify authenticity

if (!verifyWebhookSignature(req.body, signature, process.env.WEBHOOK_SECRET)) {

return res.status(401).send('Invalid signature');

}

const { event, data } = req.body;

switch (event) {

case 'envelope.completed':

await handleEnvelopeCompleted(data);

break;

case 'envelope.signed':

await handleEnvelopeSigned(data);

break;

case 'envelope.declined':

await handleEnvelopeDeclined(data);

break;

}

res.status(200).send('OK');

});

async function handleEnvelopeCompleted(data) {

console.log(`Envelope ${data.envelopeId} completed!`);

// Download signed document

const pdf = await client.envelopes.downloadDocument(data.envelopeId);

// Update your database

await db.contracts.update({

id: data.metadata.contractId,

status: 'signed',

signedDocumentUrl: pdf.url

});

// Trigger next workflow

await triggerOnboarding(data.metadata.employeeId);

}

```

Error Handling Best Practices

Implement Retry Logic

```javascript

async function sendWithRetry(envelope, maxRetries = 3) {

for (let i = 0; i < maxRetries; i++) {

try {

return await client.envelopes.send(envelope.id);

} catch (error) {

if (error.code === 'RATE_LIMIT_EXCEEDED') {

// Exponential backoff

await sleep(Math.pow(2, i) 1000);

continue;

}

if (error.code === 'VALIDATION_ERROR') {

// Don't retry validation errors

throw error;

}

if (i === maxRetries - 1) throw error;

}

}

}

```

Handle Common Errors

```javascript

try {

await client.envelopes.send(envelopeId);

} catch (error) {

switch (error.code) {

case 'INVALID_EMAIL':

return { error: 'Please check signer email address' };

case 'DOCUMENT_NOT_FOUND':

return { error: 'Document has been deleted' };

case 'INSUFFICIENT_CREDITS':

return { error: 'Please upgrade your plan' };

case 'RATE_LIMIT_EXCEEDED':

return { error: 'Too many requests. Try again in a minute' };

default:

// Log unexpected errors

logger.error('Unexpected API error:', error);

return { error: 'Something went wrong. Please try again' };

}

}

```

Security Best Practices

1. Protect API Keys

NEVER commit API keys to version control:

```javascript

// ❌ BAD

const client = new SpaceSign({

apiKey: 'sk_live_abc123...'

});

// βœ… GOOD

const client = new SpaceSign({

apiKey: process.env.SPACESIGN_API_KEY

});

```

Use different keys for environments:

  • Development: `sk_test_...`
  • Production: `sk_live_...`
  • 2. Implement Rate Limiting

    ```javascript

    const rateLimit = require('express-rate-limit');

    const apiLimiter = rateLimit({

    windowMs: 60 1000, // 1 minute

    max: 60, // 60 requests per minute

    message: 'Too many requests from this IP'

    });

    app.use('/api/signatures', apiLimiter);

    ```

    3. Validate Webhook Signatures

    Always verify webhooks come from Space Sign:

    ```javascript

    // See webhook handler example above

    if (!verifyWebhookSignature(payload, signature, secret)) {

    return res.status(401).send('Unauthorized');

    }

    ```

    4. Use HTTPS Only

    ```javascript

    // Enforce HTTPS in production

    if (process.env.NODE_ENV === 'production') {

    app.use((req, res, next) => {

    if (!req.secure) {

    return res.redirect('https://' + req.headers.host + req.url);

    }

    next();

    });

    }

    ```

    Performance Optimization

    1. Batch Operations

    Instead of sending documents one-by-one:

    ```javascript

    // ❌ Slow (sequential)

    for (const employee of employees) {

    await sendContract(employee);

    }

    // βœ… Fast (parallel)

    await Promise.all(

    employees.map(employee => sendContract(employee))

    );

    ```

    2. Cache Frequently Accessed Data

    ```javascript

    const NodeCache = require('node-cache');

    const cache = new NodeCache({ stdTTL: 600 }); // 10 minutes

    async function getTemplate(id) {

    const cached = cache.get(`template:${id}`);

    if (cached) return cached;

    const template = await client.templates.get(id);

    cache.set(`template:${id}`, template);

    return template;

    }

    ```

    3. Use Webhooks Instead of Polling

    ```javascript

    // ❌ BAD: Poll for status

    setInterval(async () => {

    const status = await client.envelopes.getStatus(envelopeId);

    if (status === 'completed') {

    // do something

    }

    }, 5000);

    // βœ… GOOD: Use webhooks

    // Webhook automatically notifies when completed

    ```

    Testing Your Integration

    Unit Tests

    ```javascript

    const nock = require('nock');

    describe('SpaceSign Integration', () => {

    it('should send envelope successfully', async () => {

    // Mock API response

    nock('https://api.spacesign.com')

    .post('/v1/envelopes')

    .reply(200, {

    status: 'success',

    data: { id: 'env_123', status: 'sent' }

    });

    const result = await sendContract();

    expect(result.status).toBe('sent');

    });

    it('should handle rate limit errors', async () => {

    nock('https://api.spacesign.com')

    .post('/v1/envelopes')

    .reply(429, { error: 'Rate limit exceeded' });

    await expect(sendContract()).rejects.toThrow('Rate limit');

    });

    });

    ```

    Integration Tests

    Use test mode API keys for safe testing:

    ```javascript

    const client = new SpaceSign({

    apiKey: process.env.SPACESIGN_TEST_KEY,

    testMode: true

    });

    ```

    Monitoring & Logging

    Track API Usage

    ```javascript

    const client = new SpaceSign({

    apiKey: process.env.SPACESIGN_API_KEY,

    onRequest: (config) => {

    logger.info('API Request:', {

    method: config.method,

    url: config.url,

    timestamp: new Date()

    });

    },

    onResponse: (response) => {

    logger.info('API Response:', {

    status: response.status,

    duration: response.duration

    });

    }

    });

    ```

    Error Monitoring

    Integrate with Sentry or similar:

    ```javascript

    const Sentry = require('@sentry/node');

    client.on('error', (error) => {

    Sentry.captureException(error, {

    tags: {

    integration: 'spacesign',

    endpoint: error.endpoint

    }

    });

    });

    ```

    Conclusion

    Building robust e-signature integrations requires:

    βœ… Proper error handling

    βœ… Webhook implementation

    βœ… Security best practices

    βœ… Performance optimization

    βœ… Comprehensive testing

    Follow these patterns, and you'll have a production-ready integration that scales.


    Need help with your integration? [Join our developer community](https://github.com/pmspaceai7-wq/space-sign/discussions) or [request technical support](/request-a-demo).

    Ready to Try Space Sign?

    Experience the power of open-source, AI-powered e-signatures.

    Space Sign Assistant

    Hello there πŸ‘‹ I’m the Space Sign Assistant. How can I help you today?