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.