Code Examples

Complete workflows showing how to use the Draftory API from start to finish.

Complete Workflow

This example walks through the full lifecycle: verify your key, create a multi-party contract, and check its signing status.

Step 1: Verify your API key

Send a GET request to /status to confirm your API key is valid. The response includes your key info and subscription tier.

curl https://api.draftory.ca/api/external/v2/status \
  -H "x-api-key: dft_live_your_key"
const response = await fetch('https://api.draftory.ca/api/external/v2/status', {
  headers: { 'x-api-key': 'dft_live_your_key' }
});
const data = await response.json();
console.log(data.status);            // "operational"
console.log(data.subscription.tier);  // "startup"
import requests

response = requests.get(
    'https://api.draftory.ca/api/external/v2/status',
    headers={'x-api-key': 'dft_live_your_key'}
)
data = response.json()
print(data['status'])  # "operational"

Step 2: Create a multi-party contract

Send a POST request to /contracts to create a new contract with multiple parties.

curl -X POST https://api.draftory.ca/api/external/v2/contracts \
  -H "x-api-key: dft_live_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "parties": [
      { "role": "seller", "legalName": "Acme Corp", "email": "acme@example.com" },
      { "role": "buyer", "legalName": "Jane Smith", "email": "jane@example.com" }
    ],
    "contractType": "nda",
    "content": "MUTUAL NON-DISCLOSURE AGREEMENT\n\n1. PARTIES\nSeller: Acme Corp\nBuyer: Jane Smith\n\n2. TERMS\nBoth parties agree not to disclose confidential information.\nThis agreement remains in effect for 2 years.\n\n3. GOVERNING LAW\nProvince of Ontario, Canada.",
    "senderName": "Acme Corp"
  }'
const response = await fetch('https://api.draftory.ca/api/external/v2/contracts', {
  method: 'POST',
  headers: {
    'x-api-key': 'dft_live_your_key',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    parties: [
      { role: 'seller', legalName: 'Acme Corp', email: 'acme@example.com' },
      { role: 'buyer', legalName: 'Jane Smith', email: 'jane@example.com' }
    ],
    contractType: 'nda',
    content: 'MUTUAL NON-DISCLOSURE AGREEMENT\n\n1. PARTIES\nSeller: Acme Corp\nBuyer: Jane Smith\n\n2. TERMS\nBoth parties agree not to disclose confidential information.',
    senderName: 'Acme Corp'
  })
});

const contract = await response.json();
console.log(contract);
import requests

response = requests.post(
    'https://api.draftory.ca/api/external/v2/contracts',
    headers={
        'x-api-key': 'dft_live_your_key',
        'Content-Type': 'application/json'
    },
    json={
        'parties': [
            {'role': 'seller', 'legalName': 'Acme Corp', 'email': 'acme@example.com'},
            {'role': 'buyer', 'legalName': 'Jane Smith', 'email': 'jane@example.com'}
        ],
        'contractType': 'nda',
        'content': 'MUTUAL NON-DISCLOSURE AGREEMENT\n\n1. PARTIES\nSeller: Acme Corp\nBuyer: Jane Smith\n\n2. TERMS\nBoth parties agree not to disclose confidential information.',
        'senderName': 'Acme Corp'
    }
)

contract = response.json()
print(contract)

The response includes a shareUrl and signingProgress. Draftory automatically emails all parties:

{
  "success": true,
  "contract": {
    "id": "contract-1711234567890-AbCdEfGh",
    "contractNumber": "EXT-20260321-ABCDEFGH",
    "shareToken": "abc123token",
    "shareUrl": "https://draftory.ca/contract/abc123token",
    "status": "pending_signature",
    "contractType": "nda",
    "parties": [
      { "role": "seller", "legalName": "Acme Corp", "email": "acme@example.com" },
      { "role": "buyer", "legalName": "Jane Smith", "email": "jane@example.com" }
    ],
    "signingProgress": { "signed": 0, "total": 2, "pending": 2, "percentage": 0 },
    "createdAt": "2026-03-21T12:00:00.000Z",
    "expiresAt": "2026-04-04T12:00:00.000Z"
  }
}

Step 3: Check signing progress

Send a GET request to /contracts/{id} using the ID from Step 2 to check which parties have signed.

curl https://api.draftory.ca/api/external/v2/contracts/contract-1711234567890-AbCdEfGh \
  -H "x-api-key: dft_live_your_key"
const contractId = 'contract-1711234567890-AbCdEfGh';

const response = await fetch(
  `https://api.draftory.ca/api/external/v2/contracts/${contractId}`,
  { headers: { 'x-api-key': 'dft_live_your_key' } }
);

const data = await response.json();
console.log(data.contract.status);                    // "partially_signed"
console.log(data.contract.signingProgress.signed);     // 1
console.log(data.contract.signingProgress.percentage); // 50

// Check each party's signing status
data.contract.parties.forEach(party => {
  console.log(`${party.role}: ${party.hasSigned ? 'signed' : 'pending'}`);
});
import requests

contract_id = 'contract-1711234567890-AbCdEfGh'

response = requests.get(
    f'https://api.draftory.ca/api/external/v2/contracts/{contract_id}',
    headers={'x-api-key': 'dft_live_your_key'}
)

data = response.json()
print(data['contract']['status'])           # "partially_signed"
print(data['contract']['signingProgress'])  # {"signed": 1, "total": 2, ...}

# Check each party
for party in data['contract']['parties']:
    status = 'signed' if party['hasSigned'] else 'pending'
    print(f'{party["role"]}: {status}')

Error Handling

Always handle errors gracefully. Check the HTTP status code and respond accordingly. Here are complete examples in JavaScript and Python.

async function createContract(data) {
  try {
    const response = await fetch(
      'https://api.draftory.ca/api/external/v2/contracts',
      {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'x-api-key': process.env.DRAFTORY_API_KEY
        },
        body: JSON.stringify(data)
      }
    );

    if (!response.ok) {
      const err = await response.json();

      switch (response.status) {
        case 401:
          console.error('Check your API key:', err.error);
          break;
        case 429:
          const retryAfter = response.headers.get('RateLimit-Reset');
          console.error(`Rate limited, retry after ${retryAfter}s`);
          break;
        default:
          console.error(`Error ${response.status}:`, err.error);
      }

      return null;
    }

    return await response.json();
  } catch (error) {
    console.error('Network error:', error.message);
    return null;
  }
}
import requests
import os

def create_contract(data):
    try:
        response = requests.post(
            'https://api.draftory.ca/api/external/v2/contracts',
            headers={
                'Content-Type': 'application/json',
                'x-api-key': os.environ['DRAFTORY_API_KEY']
            },
            json=data
        )
        response.raise_for_status()
        return response.json()

    except requests.exceptions.HTTPError as e:
        status = e.response.status_code
        err = e.response.json()

        if status == 401:
            print(f'Check your API key: {err["error"]}')
        elif status == 429:
            retry_after = e.response.headers.get('RateLimit-Reset')
            print(f'Rate limited, retry after {retry_after}s')
        else:
            print(f'Error {status}: {err["error"]}')

        return None

    except requests.exceptions.ConnectionError as e:
        print(f'Network error: {e}')
        return None

Polling for Signing Completion

Poll the contract status endpoint to wait for all parties to sign. Here is a JavaScript example:

async function waitForSigning(contractId, pollIntervalMs = 10000) {
  while (true) {
    const response = await fetch(
      `https://api.draftory.ca/api/external/v2/contracts/${contractId}`,
      { headers: { 'x-api-key': process.env.DRAFTORY_API_KEY } }
    );
    const data = await response.json();
    const { signingProgress, status } = data.contract;

    console.log(`Status: ${status} — ${signingProgress.signed}/${signingProgress.total} signed`);

    if (status === 'signed') {
      console.log('All parties signed!');
      return data.contract;
    }

    if (status === 'expired' || status === 'cancelled') {
      console.log(`Contract ${status} — stopping poll`);
      return null;
    }

    await new Promise(r => setTimeout(r, pollIntervalMs));
  }
}

// Usage
const result = await waitForSigning('contract-1711234567890-AbCdEfGh');