Skip to main content

Create a Template Contract and Sync Data Back

Goal

Create a SpotDraft contract from your source system, keep your own identifiers attached to it, and sync the final contract status, key data, and signed document back after execution.

When to use this

Use this when your CRM, ERP, procurement system, or internal workflow owns the business record and SpotDraft owns drafting, signature, and the final repository copy.

The normal production flow is:

  1. your system creates the contract in SpotDraft
  2. your system stores the SpotDraft contract id alongside its own external id
  3. webhook events drive downstream sync
  4. your system pulls final metadata and the executed file only when needed

Endpoints used

  • POST /api/v2.1/public/contracts/
  • POST /api/v2.1/public/contracts/{composite_id}/send_to_counterparties
  • GET /api/v2.1/public/contracts/{composite_id}/status
  • GET /api/v2.1/public/contracts/{composite_id}/key_pointers for contract metadata
  • POST /api/v2.1/public/contracts/{composite_id}/download_link

Prerequisites

  • a valid public API key
  • a published template-backed contract flow in SpotDraft
  • the contract_template_id for that template
  • the contract data and counterparty data available in your source system
  • a stable identifier from your system, such as a CRM deal id or ERP request id

Overview

This flow depends on workspace-specific template configuration:

  • contract_template_id must resolve in the target workspace
  • contract_data keys must match that template's configured field names

Treat the values below as an example shape, not a globally valid payload.

Treat organization_type as a workspace-specific value, not a universal enum. If your workspace uses a different label, send that exact value instead of copying "company" literally.

1. Create the contract from structured data

Write your upstream record id into external_metadata.id. That is the lookup key you should later use for webhooks, reconciliation, and support debugging.

import requests

base_url = "https://api.in.spotdraft.com" # Replace with your workspace region.
headers = {
"client-id": SPOTDRAFT_CLIENT_ID,
"client-secret": SPOTDRAFT_CLIENT_SECRET,
"Content-Type": "application/json",
}
payload = {
"contract_template_id": 1204, # Replace with a template your workspace can access.
"contract_name": "Acme Master Services Agreement",
"contract_data": {
"agreement_date": "2026-04-20",
"customer_name": "Acme Inc.",
"billing_currency": {"type": "USD", "value": 12000},
"subscription_term": {"type": "MONTHS", "value": 12, "days": 365},
"auto_renewal": True,
"notice_period_days": 30,
},
"counter_party_details": [
{
"is_individual": False,
"organization_type": "company",
"organization_name": "Acme Inc.",
"poc_details": {
"first_name": "Avery",
"last_name": "Stone",
"email": "legal@acme.example",
},
"organization_details": {
"jurisdiction_iso_code": "US",
"address": {
"line_one": "350 Fifth Avenue",
"city": "New York",
"state": "NY",
"country_iso_code": "US",
"zipcode": "10118",
},
},
}
],
"external_metadata": {
"id": "hubspot-deal-10024",
"integration_name": "HubSpot",
"record_type": "Deal",
"record_data": {
"deal_stage": "contractsent",
"owner_email": "owner@acme.example",
},
},
}

response = requests.post(
f"{base_url}/api/v2.1/public/contracts/",
headers=headers,
json=payload,
timeout=30,
)
response.raise_for_status()
contract = response.json()

2. Send the contract to counterparties

Once the contract is ready for signature, move it forward explicitly.

Replace T-1234 with the id returned from the create call.

requests.post(
f"{base_url}/api/v2.1/public/contracts/T-1234/send_to_counterparties",
headers=headers,
json={},
timeout=30,
).raise_for_status()

3. Track completion from webhooks, then reconcile if needed

The normal production path is:

  1. register lifecycle webhooks for the contract events your integration cares about
  2. use those deliveries to trigger downstream sync
  3. call the status endpoint only when a delivery needs repair, validation, or bounded reconciliation

That keeps the integration event-driven without forcing you to trust webhook delivery as the only operational safety net.

status_response = requests.get(
f"{base_url}/api/v2.1/public/contracts/T-1234/status",
headers=headers,
timeout=30,
)
status_response.raise_for_status()
status_payload = status_response.json()

4. Pull contract metadata back

After the contract is executed, fetch the extracted contract metadata your downstream systems care about. The API endpoint still uses the historical key_pointers path name.

key_pointer_response = requests.get(
f"{base_url}/api/v2.1/public/contracts/T-1234/key_pointers",
headers=headers,
timeout=30,
)
key_pointer_response.raise_for_status()
contract_metadata = key_pointer_response.json()

Use a signed URL instead of scraping the SpotDraft UI or storing an app-only link in your system.

download_response = requests.post(
f"{base_url}/api/v2.1/public/contracts/T-1234/download_link",
headers=headers,
json={"format": "pdf"},
timeout=30,
)
download_response.raise_for_status()
download_link = download_response.json()

Production checklist

  • your own upstream identifier in external_metadata.id
  • the SpotDraft id
  • the latest contract status from webhook-driven sync
  • the download URL expiry or your own copied file location for the executed file
  • any contract metadata that drives downstream workflows, such as effective date or contract value

Production notes

  • POST /api/v2.1/public/contracts/ requires contract_data, even if the selected template has very few fields.
  • For non-individual counterparties, send organization_type and organization_name consistently. organization_type is workspace-specific.
  • Treat GET /status as operational visibility. Prefer webhooks over polling for long-running workflows.

Common failure points

  • inaccessible template id A template id from another workspace or environment will fail even if the shape of the request is valid.

  • template field mismatch contract_data keys must match the selected template's field names exactly.

  • creator entity mismatch Some contract types enforce creator-side entity constraints. If your workspace uses multiple entities, validate that the chosen template supports the creator entity you expect.