Questionnaire Templates API
Questionnaire Templates are reusable DDQ (Due Diligence Questionnaire) blueprints containing structured questions. Each template is the source of truth for question order, types, and conditional logic. Use this API to retrieve template structure when creating responses.
Questionnaire Templates are created and managed through the web interface. This API lets you read template metadata, fetch question structures, and build dynamic forms.
Common Use Cases
- Retrieve available templates before creating a questionnaire response
- Fetch question structure to build dynamic UI forms
- Understand required fields and question types for autofill
- Evaluate conditional visibility rules before submitting answers
Questionnaire Template Object
The template object represents a reusable DDQ blueprint with structured questions.
List View Fields
| Field | Type | v1 | v2 | Description |
|---|---|---|---|---|
id | integer | ✓ | - | Integer identifier (v1 only) |
uuid | uuid | - | ✓ | UUID identifier (v2 only) |
name | string | ✓ | ✓ | Template name |
description | string | ✓ | ✓ | Description text |
questionnaire_type | string | ✓ | ✓ | ddq or rfi |
question_count | integer | ✓ | ✓ | Total number of text questions |
created_at | datetime | ✓ | ✓ | Creation timestamp (ISO 8601) |
Detail View Additional Fields
| Field | Type | Description |
|---|---|---|
sections | object | Optional JSON structure defining predefined sections |
questions | array | Array of text-based question objects |
file_questions | array | Array of file upload requirement objects |
response_count | integer | Number of active responses using this template (excludes PREFILL status) |
List Questionnaire Templates
Retrieve a paginated list of all questionnaire templates in your tenant.
/api/v1/questionnaires/
/api/v2/questionnaire-templates/
Query Parameters
| Parameter | Type | Description |
|---|---|---|
type | string | Filter: ddq or rfi |
page | integer | Page number (default: 1) |
page_size | integer | Results per page (default: 50, max: 100) |
Query Parameters
| Parameter | Type | Description |
|---|---|---|
type | string | Filter: ddq or rfi |
after | string | Cursor for next page (from next_cursor) |
before | string | Cursor for previous page (from prev_cursor) |
page_size | integer | Results per page (default: 50, max: 100) |
List Questionnaire Templates
curl https://api.kycgenie.com/api/v1/questionnaires/ \
-H "Authorization: Bearer YOUR_API_KEY"
curl https://api.kycgenie.com/api/v2/questionnaire-templates/ \
-H "Authorization: Bearer YOUR_API_KEY"
import requests
response = requests.get(
"https://api.kycgenie.com/api/v1/questionnaires/",
headers={"Authorization": "Bearer YOUR_API_KEY"},
)
data = response.json()
for t in data["results"]:
print(f"{t['name']} ({t['id']}) - {t['question_count']} questions")
from kycgenie import KYCGenie
import os
client = KYCGenie(api_key=os.getenv("KYCGENIE_API_KEY"))
result = client.questionnaire_templates.list()
for t in result.results:
print(f"{t.name} ({t.uuid}) - {t.question_count} questions")
# Paginate
if result.has_more:
next_page = client.questionnaire_templates.list() # cursor pagination handled automatically
const response = await fetch(
"https://api.kycgenie.com/api/v1/questionnaires/",
{ headers: { "Authorization": "Bearer YOUR_API_KEY" } }
);
const data = await response.json();
data.results.forEach(t => {
console.log(`${t.name} (${t.id}) - ${t.question_count} questions`);
});
const response = await fetch(
"https://api.kycgenie.com/api/v2/questionnaire-templates/",
{ headers: { "Authorization": "Bearer YOUR_API_KEY" } }
);
const data = await response.json();
data.results.forEach(t => {
console.log(`${t.name} (${t.uuid}) - ${t.question_count} questions`);
});
if (data.has_more) console.log("Next cursor:", data.next_cursor);
Response Example
{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"id": 1,
"name": "Standard DDQ",
"description": "Standard due diligence questionnaire",
"questionnaire_type": "ddq",
"question_count": 42,
"created_at": "2026-01-15T10:30:00Z"
},
{
"id": 2,
"name": "Security Assessment",
"description": "Information security assessment",
"questionnaire_type": "ddq",
"question_count": 28,
"created_at": "2026-01-18T14:20:00Z"
}
]
}
{
"has_more": false,
"next_cursor": null,
"prev_cursor": null,
"results": [
{
"uuid": "550e8400-e29b-41d4-a716-446655440010",
"name": "Standard DDQ",
"description": "Standard due diligence questionnaire",
"questionnaire_type": "ddq",
"question_count": 42,
"created_at": "2026-01-15T10:30:00Z"
},
{
"uuid": "660e8400-e29b-41d4-a716-446655440011",
"name": "Security Assessment",
"description": "Information security assessment",
"questionnaire_type": "ddq",
"question_count": 28,
"created_at": "2026-01-18T14:20:00Z"
}
]
}
Get Questionnaire Template
Retrieve a specific template by ID, including all questions and file requirements.
/api/v1/questionnaires/{id}/
/api/v2/questionnaire-templates/{uuid}/
Path Parameters
| Parameter | Type | Description |
|---|---|---|
id | integer | Template integer ID |
Path Parameters
| Parameter | Type | Description |
|---|---|---|
uuid | uuid | Template UUID (use uuid from list response) |
Returns the full template object including questions, file_questions, and section structure. Returns 200 OK.
Get Template Details
curl https://api.kycgenie.com/api/v1/questionnaires/1/ \
-H "Authorization: Bearer YOUR_API_KEY"
curl https://api.kycgenie.com/api/v2/questionnaire-templates/550e8400-e29b-41d4-a716-446655440010/ \
-H "Authorization: Bearer YOUR_API_KEY"
import requests
response = requests.get(
"https://api.kycgenie.com/api/v1/questionnaires/1/",
headers={"Authorization": "Bearer YOUR_API_KEY"},
)
tmpl = response.json()
print(f"Name: {tmpl['name']}")
print(f"Questions: {len(tmpl['questions'])}")
print(f"File requirements: {len(tmpl['file_questions'])}")
from kycgenie import KYCGenie
import os
client = KYCGenie(api_key=os.getenv("KYCGENIE_API_KEY"))
tmpl = client.questionnaire_templates.get_template(
questionnaire_uuid="550e8400-e29b-41d4-a716-446655440010",
)
print(f"Name: {tmpl.name}")
print(f"Questions: {len(tmpl.questions)}")
print(f"File requirements: {len(tmpl.file_questions)}")
const response = await fetch(
"https://api.kycgenie.com/api/v1/questionnaires/1/",
{ headers: { "Authorization": "Bearer YOUR_API_KEY" } }
);
const tmpl = await response.json();
console.log(`Name: ${tmpl.name}`);
console.log(`Questions: ${tmpl.questions.length}`);
const response = await fetch(
"https://api.kycgenie.com/api/v2/questionnaire-templates/550e8400-e29b-41d4-a716-446655440010/",
{ headers: { "Authorization": "Bearer YOUR_API_KEY" } }
);
const tmpl = await response.json();
console.log(`Name: ${tmpl.name}`);
console.log(`UUID: ${tmpl.uuid}`);
Response Example (200 OK)
{
"id": 1,
"name": "Standard DDQ",
"description": "Standard due diligence questionnaire",
"questionnaire_type": "ddq",
"sections": null,
"question_count": 3,
"response_count": 12,
"created_at": "2026-01-15T10:30:00Z",
"questions": [
{
"id": 123,
"number": 1,
"section": "Part 1",
"section_number": 1,
"sub_section": "Company Information",
"sub_section_number": 1,
"text": "What is your company's legal name?",
"question_type": "text",
"required": true,
"exclude_from_autofill": false,
"options": null,
"visibility_conditions": null
}
],
"file_questions": [
{
"id": 45,
"document_type": "certificate_of_incorporation",
"description": "Certificate of Incorporation",
"required": true,
"multiple": false,
"order": 1,
"min_files": 1,
"max_files": 1,
"accepted_extensions": ["pdf", "jpg", "png"]
}
]
}
{
"uuid": "550e8400-e29b-41d4-a716-446655440010",
"name": "Standard DDQ",
"description": "Standard due diligence questionnaire",
"questionnaire_type": "ddq",
"sections": null,
"question_count": 3,
"response_count": 12,
"created_at": "2026-01-15T10:30:00Z",
"questions": [
{
"id": 123,
"number": 1,
"section": "Part 1",
"section_number": 1,
"sub_section": "Company Information",
"sub_section_number": 1,
"text": "What is your company's legal name?",
"question_type": "text",
"required": true,
"exclude_from_autofill": false,
"options": null,
"visibility_conditions": null
}
],
"file_questions": [
{
"id": 45,
"document_type": "certificate_of_incorporation",
"description": "Certificate of Incorporation",
"required": true,
"multiple": false,
"order": 1,
"min_files": 1,
"max_files": 1,
"accepted_extensions": ["pdf", "jpg", "png"]
}
]
}
List Template Questions
Retrieve all questions for a template, including both text-based questions and file upload requirements.
Questions are ordered by section_number, sub_section_number, then number.
/api/v1/questionnaires/{id}/questions/
/api/v2/questionnaire-templates/{uuid}/questions/
Returns {questions[], file_questions[]} with 200 OK. No pagination - returns all questions for the template.
List Template Questions
curl https://api.kycgenie.com/api/v1/questionnaires/1/questions/ \
-H "Authorization: Bearer YOUR_API_KEY"
curl https://api.kycgenie.com/api/v2/questionnaire-templates/550e8400-e29b-41d4-a716-446655440010/questions/ \
-H "Authorization: Bearer YOUR_API_KEY"
import requests
response = requests.get(
"https://api.kycgenie.com/api/v1/questionnaires/1/questions/",
headers={"Authorization": "Bearer YOUR_API_KEY"},
)
data = response.json()
for q in data["questions"]:
print(f"Q{q['number']}: {q['text']} [{q['question_type']}]")
if q["options"]:
print(f" Options: {q['options']}")
if q["visibility_conditions"]:
print(f" Conditional: {q['visibility_conditions']}")
from kycgenie import KYCGenie
import os
client = KYCGenie(api_key=os.getenv("KYCGENIE_API_KEY"))
data = client.questionnaire_templates.list_questions(
questionnaire_uuid="550e8400-e29b-41d4-a716-446655440010",
)
for q in data.questions:
print(f"Q{q.number}: {q.text} [{q.question_type}]")
for fq in data.file_questions:
print(f"File: {fq.description} (required={fq.required})")
const res = await fetch(
"https://api.kycgenie.com/api/v1/questionnaires/1/questions/",
{ headers: { "Authorization": "Bearer YOUR_API_KEY" } }
);
const data = await res.json();
data.questions.forEach(q => {
console.log(`Q${q.number}: ${q.text} [${q.question_type}]`);
if (q.options) console.log(" Options:", q.options);
});
const res = await fetch(
"https://api.kycgenie.com/api/v2/questionnaire-templates/550e8400-e29b-41d4-a716-446655440010/questions/",
{ headers: { "Authorization": "Bearer YOUR_API_KEY" } }
);
const data = await res.json();
data.questions.forEach(q => {
console.log(`Q${q.number}: ${q.text} [${q.question_type}]`);
if (q.options) console.log(" Options:", q.options);
});
Response Example (200 OK)
{
"questions": [
{
"id": 123,
"number": 1,
"section": "Part 1",
"section_number": 1,
"sub_section": "Company Information",
"sub_section_number": 1,
"text": "What is your company's legal name?",
"question_type": "text",
"required": true,
"exclude_from_autofill": false,
"options": null,
"visibility_conditions": null
},
{
"id": 124,
"number": 2,
"section": "Part 1",
"section_number": 1,
"sub_section": "Company Information",
"sub_section_number": 1,
"text": "What is your primary business activity?",
"question_type": "select",
"required": true,
"exclude_from_autofill": false,
"options": ["Financial Services", "Technology", "Healthcare", "Other"],
"visibility_conditions": null
},
{
"id": 125,
"number": 3,
"section": "Part 1",
"section_number": 1,
"sub_section": "Regulatory",
"sub_section_number": 2,
"text": "Please describe your regulatory authorisations.",
"question_type": "text",
"required": true,
"exclude_from_autofill": false,
"options": null,
"visibility_conditions": {
"operator": "AND",
"rules": [
{"question_id": 124, "condition": "eq", "value": "Financial Services"}
]
}
}
],
"file_questions": [
{
"id": 45,
"document_type": "certificate_of_incorporation",
"description": "Certificate of Incorporation",
"required": true,
"multiple": false,
"order": 1,
"min_files": 1,
"max_files": 1,
"accepted_extensions": ["pdf", "jpg", "png"]
}
]
}
Question Object
Text-based questions within a questionnaire template. Both v1 and v2 return the same question shape (integer id).
Attributes
| Field | Type | Description |
|---|---|---|
id | integer | Unique question identifier |
number | integer | Display ordering number |
section | string | Section name (e.g. "Part 1") |
section_number | integer | Numeric section order |
sub_section | string | null | Subsection name |
sub_section_number | integer | Numeric subsection order |
text | string | The question text (max 1000 characters) |
question_type | string | Question type (see table below) |
required | boolean | Whether an answer is required for submission |
exclude_from_autofill | boolean | If true, AI autofill skips this question |
options | array | null | Valid option strings for select and multi_select types; null for all others |
visibility_conditions | object | null | Conditional visibility rules. null = always shown. See Conditional Questions. |
File Question Attributes
| Field | Type | Description |
|---|---|---|
id | integer | Unique file question identifier |
document_type | string | Document type name (e.g. certificate_of_incorporation) |
description | string | Human-readable description |
required | boolean | Whether this file is required for submission |
multiple | boolean | Whether multiple files are allowed (max_files > 1 or unlimited) |
order | integer | Display order |
min_files | integer | null | Minimum required file count |
max_files | integer | null | Maximum allowed file count; null = unlimited |
accepted_extensions | array | Allowed file extensions (e.g. ["pdf","jpg","png"]) |
Question Types
The question_type field determines what value to supply in answer_text when submitting answers via the Questionnaire Responses API.
| Type | Description | answer_text format |
|---|---|---|
text | Free text | Any string |
number | Numeric value | Numeric string, e.g. "42" |
email | Email address | Valid email string |
tel | Phone number | Phone number string |
url | URL / Website | URL string, e.g. "https://example.com" |
percentage | Percentage value | Numeric string, e.g. "75" (no % symbol) |
currency | Currency amount | Numeric string, e.g. "15000" |
boolean | Yes / No | "yes" or "no" |
yes_no_na | Yes / No / Not Applicable | "yes", "no", or "na" |
date | Date only | Use the date_answer field (YYYY-MM-DD), not answer_text |
text_and_date | Text with an associated date | Supply both answer_text (text) and date_answer (YYYY-MM-DD) |
select | Single-choice dropdown | One of the strings from the question's options array |
multi_select | Multiple-choice checkboxes | JSON array string of selected options, e.g. '["Option A","Option B"]' |
country | Country picker | ISO 3166-1 alpha-2 code, e.g. "GB" |
structured | Multi-field structured data (e.g. UBO details) | Use the structured_data field (list of instance dicts), not answer_text |
File uploads are handled separately via the file_questions array.
These use the FileQuestion model with document type specifications and are submitted via the Documents API.