Skip to main content

Update Intake Form Responses on a Contract

Goal​

Patch intake-form data on an existing contract after the contract has already been created.

When to use this​

Use this when your contract workflow exposes a contract-level intake form response that can be edited after creation.

This is a supported follow-up flow after contract creation. If you already know all intake-form answers up front, you can still send intake_form_data during upload_review_contracts or upload_sign_contracts to avoid a second API call.

Endpoints used​

  • GET /api/v2.1/public/contract_types/{contract_type_id}/intake_form/questionnaire/
  • GET /api/v2.1/public/contracts/{contract_id}/questionnaire/responses/?questionnaire_type=INTAKE_FORM
  • PATCH /api/v2.1/public/contracts/{composite_id}/intake_form/response

Prerequisites​

  • a valid public API key
  • the contract's composite_id, such as T-1234
  • the contract's numeric contract_id
  • the contract_type_id used for the contract workflow

Overview​

This route is workspace- and workflow-dependent. Validate that the target contract type is accessible in the current workspace, then read the current saved intake-form response before patching it.

Two ids are involved:

  • contract_id is the numeric internal id used by the questionnaire-response read endpoint
  • composite_id is the public id shape like T-1234 or H-1234 used by the patch endpoint

Request sequence​

1. Fetch the questionnaire definition​

Use the contract type questionnaire endpoint to understand the field names and expected value shapes.

import os
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",
}

questionnaire = requests.get(
f"{base_url}/api/v2.1/public/contract_types/{os.environ['SPOTDRAFT_CONTRACT_TYPE_ID']}/intake_form/questionnaire/",
headers=headers,
timeout=30,
).json()

Example response excerpt:

[
{
"display_label": "Contract term",
"variable": "term",
"type": "string",
"required": true
},
{
"display_label": "Payment method",
"variable": "payment_method",
"type": "enum",
"options": [
{
"label": "Annual invoice",
"value": "annual_invoice"
}
]
}
]

Use:

  • variable as the JSON key in your patch body
  • type, parent_type, and options to choose the right value shape

2. Read the current saved intake-form response​

The patch route merges your payload into the latest saved contract intake-form response. Read the current response first if you need to preserve existing values intentionally.

current_response = requests.get(
f"{base_url}/api/v2.1/public/contracts/{os.environ['SPOTDRAFT_NUMERIC_CONTRACT_ID']}/questionnaire/responses/",
headers=headers,
params={"questionnaire_type": "INTAKE_FORM"},
timeout=30,
)
current_response.raise_for_status()
current_intake_form = current_response.json()

Use this response as the current-state baseline. The patch route updates the stored response rather than replacing it from an empty object.

3. Patch the intake-form response​

The schema describes this as a partial update. Only send the questionnaire keys you want to change.

response = requests.patch(
f"{base_url}/api/v2.1/public/contracts/{os.environ['SPOTDRAFT_CONTRACT_ID']}/intake_form/response",
headers=headers,
json={
"term": {"days": 365, "type": "DAYS", "value": 365},
"notes": "Updated after procurement review.",
"start_date": "2026-05-01",
"payment_method": "annual_invoice",
},
timeout=30,
)
response.raise_for_status()
intake_form_response = response.json()

Example response:

{
"data": {
"term": {
"days": 365,
"type": "DAYS",
"value": 365
},
"notes": "Updated after procurement review.",
"start_date": "2026-05-01",
"payment_method": "annual_invoice"
},
"created": "2026-04-17T11:45:00Z"
}

Production notes​

  • The backend merges your patch into the latest saved intake-form response for that contract.
  • Treat the questionnaire definition as the source of truth and send only questionnaire field names.
  • This is a supported post-create contract edit flow. Use it when your integration learns or corrects intake-form values after the contract already exists.
  • This route can return 400 when the workspace feature flag for intake-form editing is disabled, when the patch produces no effective change, or when approval-state constraints block the edit.

Common failure points​

  • wrong contract id shape composite_id must look like T-123 or H-123, not a plain integer.

  • wrong field types Match the questionnaire value shape exactly, especially for enums, dates, repeated structures, and typed objects such as duration or currency.

  • accidental full overwrite assumptions This route merges into the latest saved intake-form response, so read current state first if you need deterministic updates.

  • wrong id type on the read step GET /contracts/{contract_id}/questionnaire/responses/ uses the numeric internal contract_id, not the public composite id.