Skip to main content

Find Contracts Using Your Own System's IDs

Goal​

Use your CRM, ERP, procurement, or ticketing ids as the stable lookup key for SpotDraft contracts instead of relying on contract titles or user-entered text.

When to use this​

Use this when your CRM, ERP, procurement, or ticketing system already has a canonical record id and you want that id to be the stable lookup key for SpotDraft contracts.

Endpoints used​

  • POST /api/v2.1/public/contracts/
  • GET /api/v2.1/public/contracts/by_external_metadata/{external_metadata_id}
  • POST /api/v2.1/public/contracts/{contract_id}/external_metadata

Overview​

The stable pattern is to write the upstream id during contract creation, then read it back through by_external_metadata later. The sample ids below are placeholders for your own source-system identifiers.

Prerequisites​

  • a valid public API key
  • a template id your workspace can access
  • a stable upstream identifier, such as a CRM deal id or ERP request id
  • write the upstream identifier into external_metadata.id when the contract is created
  • keep that identifier stable across environments
  • query by that id when you need to re-open or reconcile a contract
  • patch external metadata later only when your source-of-truth record changed after creation

1. Create the contract with external metadata​

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,
"contract_name": "Acme MSA",
"contract_data": {
"agreement_date": "2026-04-20",
"customer_name": "Acme Inc.",
},
"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",
},
}
],
"external_metadata": {
"id": "deal-122",
"integration_name": "HubSpot",
"record_type": "Deal",
"record_data": {"owner_email": "owner@acme.example"},
},
}

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

2. Retrieve contracts later by the same external id​

contracts = requests.get(
f"{base_url}/api/v2.1/public/contracts/by_external_metadata/deal-122",
headers=headers,
timeout=30,
).json()

3. Update external metadata when needed​

If your upstream record changes after the contract is created, upsert the metadata explicitly.

metadata_response = requests.post(
f"{base_url}/api/v2.1/public/contracts/T-1234/external_metadata",
headers=headers,
json={
"external_metadata": {
"id": "deal-122",
"integration_name": "HubSpot",
"record_type": "Deal",
"record_data": {
"deal_stage": "closedwon",
"owner_email": "owner@acme.example",
},
}
},
timeout=30,
)
metadata_response.raise_for_status()

Good external ids​

  • CRM opportunity ids
  • procurement request ids
  • ERP purchase order ids
  • internal workflow or request ids

Avoid​

  • parsing identifiers from contract_name
  • changing the meaning of the same external id field across environments
  • storing only human-readable labels without a stable machine identifier

Production checklist​

  • write external_metadata.id on create, not later if you can avoid it
  • keep the value stable across the life of the source record
  • use lookup by external metadata as the default re-entry path for downstream sync
  • do not rely on contract titles for reconciliation

Production notes​

  • Keep external_metadata.id stable across retries and reconciliation jobs.
  • Query by external metadata for support tooling and background sync, not by contract title.

Common failure points​

  • non-stable upstream ids If the source record id changes across environments or retries, the lookup pattern becomes unreliable.

  • missing metadata at create time Backfilling later works, but it makes webhook-driven reconciliation harder.