Bulk contract and amendment signing

Sign multiple contract amendments programmatically using the API

Overview

This guide explains how to sign multiple contracts and contract amendments programmatically using the API. The Deel UI requires signing contracts or amendments one by one, but the API enables automation for bulk signing workflows.

When to use this workflow

Use this workflow when you need to:

  • Sign multiple contracts or contract amendments at once
  • Automate contract signing for large teams
  • Process contracts and amendments that have already been reviewed and approved
  • Integrate contract signing into your existing workflow automation
  • Handle seasonal or periodic bulk contract updates

Prerequisites

Before you begin, ensure you have:

  • A valid API token with contracts:write scope
  • Contract IDs for the contracts or amendments you need to sign
  • Authorization to sign on behalf of the client organization
  • Confirmation that contracts and amendments have been reviewed and approved

Contract signatures are legally binding. Only use this workflow for contracts and amendments that have been properly reviewed and approved through your organization’s internal processes.

Step-by-step workflow

This example demonstrates signing multiple contract amendments after a company-wide compensation review.

1

Identify contracts requiring signatures

First, retrieve the list of contracts to identify which ones need signatures.

$curl --request GET 'https://api.letsdeel.com/rest/v2/contracts' \
>--header 'Authorization: Bearer {{token}}'

Response:

1{
2 "data": [
3 {
4 "id": "123e4567-e89b-12d3-a456-426614174000",
5 "title": "Senior Backend Engineer",
6 "type": "ongoing_time_based",
7 "status": "active",
8 "worker": {
9 "id": "28da9a07-102c-4a69-9781-3182a514f669",
10 "full_name": "Sarah Johnson",
11 "email": "sarah@example.com"
12 },
13 "signatures": {
14 "client_signature": "Jane Doe",
15 "client_signed_at": "2026-01-15T10:00:00Z",
16 "worker_signature": "Sarah Johnson",
17 "worker_signed_at": "2026-01-15T11:00:00Z"
18 },
19 "created_at": "2026-01-10T09:00:00Z"
20 },
21 {
22 "id": "456e7890-e89b-12d3-a456-426614174111",
23 "title": "Frontend Engineer",
24 "type": "ongoing_time_based",
25 "status": "active",
26 "worker": {
27 "id": "38da9a07-102c-4a69-9781-3182a514f770",
28 "full_name": "Michael Chen",
29 "email": "michael@example.com"
30 },
31 "signatures": {
32 "client_signature": "Jane Doe",
33 "client_signed_at": "2026-01-16T10:00:00Z",
34 "worker_signature": "Michael Chen",
35 "worker_signed_at": "2026-01-16T11:00:00Z"
36 },
37 "created_at": "2026-01-12T09:00:00Z"
38 }
39 ],
40 "page": {
41 "size": 2,
42 "number": 1
43 }
44}

To identify contracts with pending amendments, you will need to check each contract for amendments in the next step.

2

Check for pending amendments

For each contract, retrieve the list of amendments to identify which ones require client signature.

$curl --request GET 'https://api.letsdeel.com/rest/v2/contracts/123e4567-e89b-12d3-a456-426614174000/amendments?sign_statuses=PENDING&sign_statuses=WAITING_FOR_APPROVAL' \
>--header 'Authorization: Bearer {{token}}'

Response:

1{
2 "data": [
3 {
4 "id": "550e8400-e29b-41d4-a716-446655440000",
5 "status": "PENDING",
6 "sign_status": "PENDING",
7 "rate": 8800.00,
8 "scale": "MONTHLY",
9 "currency_code": "USD",
10 "effective_date": "2026-03-01",
11 "created_at": "2026-02-10T10:00:00Z",
12 "updated_at": "2026-02-10T10:00:00Z",
13 "contract_name": "Senior Backend Engineer",
14 "contract_type": "ongoing_time_based"
15 }
16 ],
17 "has_more": false,
18 "total_count": 1,
19 "cursor": null
20}

Filter amendments where sign_status is PENDING or WAITING_FOR_APPROVAL. These amendments require client signature.

Always verify amendment details before signing. Ensure the changes match your internal approval records and that the effective date is correct.

3

Sign a contract or amendment

Sign the contract or amendment by providing the client signature name. This endpoint handles both initial contract signing and amendment signing.

$curl --request POST \
> 'https://api.letsdeel.com/rest/v2/contracts/123e4567-e89b-12d3-a456-426614174000/signatures' \
> --header 'Authorization: Bearer {{token}}' \
> --header 'Content-Type: application/json' \
> --data-raw '{
> "data": {
> "client_signature": "Johnathan A. Doe"
> }
> }'

The signature name should be the full legal name of the authorized signatory on behalf of the client organization. Use a consistent signature format across all contracts.

Response:

1{
2 "data": {
3 "created": true
4 }
5}

The contract or amendment is now signed and will proceed to the next step in the contract workflow.

4

Automate bulk signing

To sign multiple contracts, iterate through your list and sign each one programmatically.

1const axios = require('axios');
2
3// Configuration
4const API_BASE_URL = 'https://api.letsdeel.com/rest/v2';
5const API_TOKEN = process.env.DEEL_API_TOKEN || process.env.API_TOKEN;
6const CLIENT_SIGNATURE = 'Johnathan A. Doe';
7
8// Headers for API requests
9const headers = {
10 'Authorization': `Bearer ${API_TOKEN}`,
11 'Content-Type': 'application/json'
12};
13
14// List of contract IDs requiring signatures
15const contractIds = [
16 '123e4567-e89b-12d3-a456-426614174000',
17 '456e7890-e89b-12d3-a456-426614174111',
18 '789e0123-e89b-12d3-a456-426614174222'
19];
20
21// Track results
22const signedContracts = [];
23const failedContracts = [];
24
25async function signContracts() {
26 for (const contractId of contractIds) {
27 const url = `${API_BASE_URL}/contracts/${contractId}/signatures`;
28 const payload = {
29 data: {
30 client_signature: CLIENT_SIGNATURE
31 }
32 };
33
34 try {
35 const response = await axios.post(url, payload, { headers });
36 const data = response.data.data || {};
37 signedContracts.push({
38 contract_id: contractId,
39 signed: data.created === true,
40 });
41
42 console.log(`✓ Successfully signed contract ${contractId}`);
43 } catch (error) {
44 failedContracts.push({
45 contract_id: contractId,
46 error: error.response ? `${error.response.status} ${error.response.statusText}` : error.message
47 });
48 console.log(`✗ Failed to sign contract ${contractId}: ${
49 error.response ? `${error.response.status} ${error.response.statusText}` : error.message
50 }`);
51 }
52 }
53
54 // Summary
55 console.log('\nSummary:');
56 console.log(`Successfully signed: ${signedContracts.length} contracts`);
57 console.log(`Failed: ${failedContracts.length} contracts`);
58
59 if (failedContracts.length > 0) {
60 console.log('\nFailed contracts:');
61 failedContracts.forEach(contract => {
62 console.log(` - ${contract.contract_id}: ${contract.error}`);
63 });
64 }
65}
66
67signContracts();

This script processes multiple contracts and provides a summary of successful and failed signatures.

5

Verify signatures

After bulk signing, verify that contracts and amendments were signed successfully by checking the amendment status.

$curl --request GET 'https://api.letsdeel.com/rest/v2/contracts/123e4567-e89b-12d3-a456-426614174000/amendments' \
>--header 'Authorization: Bearer {{token}}'

Response:

1{
2 "data": [
3 {
4 "id": "550e8400-e29b-41d4-a716-446655440000",
5 "status": "APPROVED",
6 "sign_status": "APPROVED",
7 "rate": 8800.00,
8 "scale": "MONTHLY",
9 "currency_code": "USD",
10 "effective_date": "2026-03-01",
11 "created_at": "2026-02-10T10:00:00Z",
12 "updated_at": "2026-02-12T14:30:00Z",
13 "contract_name": "Senior Backend Engineer",
14 "contract_type": "ongoing_time_based"
15 }
16 ],
17 "has_more": false,
18 "total_count": 1,
19 "cursor": null
20}

Confirm that sign_status is APPROVED for successfully signed amendments.

6

Monitor signing progress

For large batches, monitor progress in real-time by tracking signature status.

1const axios = require('axios');
2
3const API_BASE_URL = process.env.API_BASE_URL;
4const API_TOKEN = process.env.API_TOKEN;
5
6const headers = {
7 'Authorization': `Bearer ${API_TOKEN}`,
8 'Content-Type': 'application/json',
9};
10
11async function checkAmendmentStatus(contractId) {
12 const url = `${API_BASE_URL}/contracts/${contractId}/amendments?sign_statuses=PENDING&sign_statuses=WAITING_FOR_APPROVAL&sign_statuses=APPROVED`;
13 const response = await axios.get(url, { headers });
14 const data = response.data.data;
15
16 if (data && data.length > 0) {
17 // Get the most recent amendment
18 const amendment = data[0];
19 return {
20 contract_id: contractId,
21 amendment_id: amendment.id,
22 signed: amendment.sign_status === 'APPROVED',
23 status: amendment.sign_status,
24 updated_at: amendment.updated_at,
25 };
26 }
27
28 return { contract_id: contractId, signed: false, status: 'no_amendment' };
29}
30
31// Check status for multiple contracts
32const contractIds = [
33 '123e4567-e89b-12d3-a456-426614174000',
34 '456e7890-e89b-12d3-a456-426614174111',
35 '789e0123-e89b-12d3-a456-426614174222'
36];
37
38console.log('Checking amendment signature status...');
39(async () => {
40 for (const contractId of contractIds) {
41 try {
42 const status = await checkAmendmentStatus(contractId);
43 if (status.signed) {
44 console.log(`✓ ${contractId}: Signed (${status.status}) at ${status.updated_at}`);
45 } else {
46 console.log(`○ ${contractId}: Not signed (${status.status})`);
47 }
48 } catch (err) {
49 console.error(`Error checking ${contractId}:`, err.response?.data || err.message);
50 }
51 }
52})();

Best practices

Security and authorization

  • Store API tokens securely using environment variables or secret management systems
  • Restrict API token scope to only contracts:write permission
  • Implement role-based access control to limit who can execute bulk signing
  • Log all signature operations with timestamps and user identification
  • Use audit trails to track when and by whom contracts were signed
  • Never commit API tokens to version control systems

Validation before signing

  • Verify amendment type and ensure it matches expected changes
  • Check effective dates are reasonable and align with organizational policies
  • Validate compensation changes are within approved ranges
  • Confirm amendments have been reviewed by appropriate stakeholders
  • Check for data integrity issues or unexpected values
  • Review batch sizes before executing to prevent accidental mass signatures

Error handling

  • Implement retry logic with exponential backoff for transient failures
  • Log all errors with contract IDs and error messages
  • Create a separate process to handle failed signatures
  • Send notifications when signature failures exceed threshold
  • Provide clear error messages for debugging
  • Monitor API rate limits and adjust batch sizes accordingly

Testing and staging

  • Test bulk signing scripts in sandbox environment first
  • Start with small batches (5-10 contracts) before scaling up
  • Verify signatures in the UI after API operations
  • Use dry-run mode to simulate operations without actual signing
  • Create rollback procedures for incorrect batch signatures
  • Document all testing results before production deployment

Audit and compliance

  • Maintain detailed logs of all bulk signature operations
  • Store records of who initiated bulk signing and when
  • Keep copies of amendment details at time of signature
  • Generate regular audit reports for compliance review
  • Implement approval workflows before bulk operations
  • Document the business justification for each bulk signing operation

Troubleshooting

Verify your API token is valid and has the contracts:write scope. Check that the token has not expired and that you are using the correct authorization header format.

Confirm the contract ID is correct and that the contract exists. Check that you are using the correct API environment (production vs. sandbox). Verify you have access to this contract in your Deel account.

Verify the amendment has been created using the GET /contracts/{contract_id}/amendments endpoint. Check if the sign_status is PENDING or WAITING_FOR_APPROVAL. If the status is already APPROVED, the amendment has already been signed.

Ensure the signature name matches your authorized signatory format. The name should be the full legal name of the person authorized to sign on behalf of the organization. Check for extra spaces or special characters.

Reduce your batch size or implement rate limiting in your script. Deel’s API has rate limits to prevent abuse. Wait for the rate limit window to reset before retrying. Consider processing contracts in smaller batches over a longer time period.

Review the error messages for failed contracts. Common causes include amendments that require additional approval, contracts in unexpected states, or temporary API issues. Retry failed contracts individually after resolving the specific issues.

Allow time for the system to process the signature (usually a few seconds). Refresh the contract details to see updated status. Check webhooks or event logs to confirm the signature was processed successfully.

Next steps