Documents API Reference

Upload, manage, and retrieve documents across your tenant library, entities, and questionnaire responses.

Document Types
  • Tenant Documents - Stored in your organisation's private library
  • Entity Documents - Attached to a specific entity
  • Response Documents - Uploaded as file answers on a Questionnaire Response
Download tokens

All /download/ endpoints return a short-lived (15 min), single-use proxy URL. The token is invalidated after first use.

Tenant Documents

Manage documents in your organisation's private library.

Upload Tenant Document

Upload a document to your tenant's private library.

Request Body (multipart/form-data)

FieldTypeRequiredDescription
file file Yes The file to upload (max 10 MB)
document_type string Yes Document type label (e.g. Financial Statement)

Response (201 Created)

Returns a Document object. See response example on the right.

List Tenant Documents

Retrieve all documents in your tenant's private library.

Query Parameters

ParameterTypeDescription
document_type string Filter by document type label
limit v1 integer Max results to return (default 100, max 1000)
after v2 string Cursor token - fetch the next page
before v2 string Cursor token - fetch the previous page
page_size v2 integer Results per page (default 50, max 100)

Get Tenant Document

Retrieve metadata for a single tenant document.

Response (200 OK)

Returns a Document object. Same shape as a list item.

Delete Tenant Document

Permanently delete a document from your library. Returns 400 if the document is referenced by any response.

Response (204 No Content)

Empty body on success.

Get Tenant Document Download URL

Generate a short-lived (15 min), single-use proxy download URL.

Response (200 OK)

FieldTypeDescription
download_urlstringProxy URL - fetch directly, no auth required
expires_atdatetimeISO 8601 expiry timestamp (15 min from now)
filenamestringOriginal filename
content_typestringMIME type
sizeintegerFile size in bytes

Upload Tenant Document

curl -X POST https://api.kycgenie.com/api/v1/documents/upload/ \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "file=@financial_statement.pdf" \
  -F "document_type=Financial Statement"
curl -X POST https://api.kycgenie.com/api/v2/documents/upload/ \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "file=@financial_statement.pdf" \
  -F "document_type=Financial Statement"
import requests

with open('financial_statement.pdf', 'rb') as f:
    resp = requests.post(
        'https://api.kycgenie.com/api/v1/documents/upload/',
        headers={'Authorization': 'Bearer YOUR_API_KEY'},
        files={'file': ('financial_statement.pdf', f, 'application/pdf')},
        data={'document_type': 'Financial Statement'},
    )

print(resp.json())
client = KYCGenie(api_key="YOUR_API_KEY")

with open('financial_statement.pdf', 'rb') as f:
    doc = client.documents.upload(
        file=f,
        document_type='Financial Statement',
    )

print(doc.id, doc.name)
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('document_type', 'Financial Statement');

const resp = await fetch('https://api.kycgenie.com/api/v1/documents/upload/', {
  method: 'POST',
  headers: { 'Authorization': 'Bearer YOUR_API_KEY' },
  body: formData,
});
console.log(await resp.json());
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('document_type', 'Financial Statement');

const resp = await fetch('https://api.kycgenie.com/api/v2/documents/upload/', {
  method: 'POST',
  headers: { 'Authorization': 'Bearer YOUR_API_KEY' },
  body: formData,
});
console.log(await resp.json());

Response (201 Created)

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "financial_statement.pdf",
  "size": 2457600,
  "content_type": "application/pdf",
  "document_type_name": "Financial Statement",
  "classification": "tenant_private",
  "uploaded_by_email": "api.user@example.com",
  "created_at": "2026-01-30T14:23:45Z",
  "expiry_date": null
}

List Tenant Documents

curl https://api.kycgenie.com/api/v1/documents/ \
  -H "Authorization: Bearer YOUR_API_KEY"
curl https://api.kycgenie.com/api/v2/documents/ \
  -H "Authorization: Bearer YOUR_API_KEY"
import requests

resp = requests.get(
    'https://api.kycgenie.com/api/v1/documents/',
    headers={'Authorization': 'Bearer YOUR_API_KEY'},
)
print(resp.json())
from kycgenie import KYCGenie

client = KYCGenie(api_key="YOUR_API_KEY")

result = client.documents.list(
    document_type='Financial Statement',  # optional
    page_size=50,
)

for doc in result.results:
    print(doc.id, doc.name)
const resp = await fetch('https://api.kycgenie.com/api/v1/documents/', {
  headers: { 'Authorization': 'Bearer YOUR_API_KEY' },
});
console.log(await resp.json());
const resp = await fetch('https://api.kycgenie.com/api/v2/documents/', {
  headers: { 'Authorization': 'Bearer YOUR_API_KEY' },
});
console.log(await resp.json());

Response (200 OK)

{
  "count": 1,
  "documents": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "financial_statement.pdf",
      "size": 2457600,
      "content_type": "application/pdf",
      "document_type_name": "Financial Statement",
      "classification": "tenant_private",
      "uploaded_by_email": "api.user@example.com",
      "created_at": "2026-01-30T14:23:45Z",
      "expiry_date": null
    }
  ]
}

Response (200 OK) - cursor-paginated

{
  "has_more": false,
  "next_cursor": null,
  "prev_cursor": null,
  "results": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "financial_statement.pdf",
      "size": 2457600,
      "content_type": "application/pdf",
      "document_type_name": "Financial Statement",
      "classification": "tenant_private",
      "uploaded_by_email": "api.user@example.com",
      "created_at": "2026-01-30T14:23:45Z",
      "expiry_date": null
    }
  ]
}

Get Tenant Document

curl https://api.kycgenie.com/api/v1/documents/550e8400-e29b-41d4-a716-446655440000/ \
  -H "Authorization: Bearer YOUR_API_KEY"
curl https://api.kycgenie.com/api/v2/documents/550e8400-e29b-41d4-a716-446655440000/ \
  -H "Authorization: Bearer YOUR_API_KEY"
resp = requests.get(
    'https://api.kycgenie.com/api/v1/documents/550e8400-e29b-41d4-a716-446655440000/',
    headers={'Authorization': 'Bearer YOUR_API_KEY'},
)
print(resp.json())
doc = client.documents.get(
    document_id='550e8400-e29b-41d4-a716-446655440000',
)
print(doc.name, doc.size)
const resp = await fetch(
  'https://api.kycgenie.com/api/v1/documents/550e8400-e29b-41d4-a716-446655440000/',
  { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
console.log(await resp.json());
const resp = await fetch(
  'https://api.kycgenie.com/api/v2/documents/550e8400-e29b-41d4-a716-446655440000/',
  { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
console.log(await resp.json());

Delete Tenant Document

curl -X DELETE https://api.kycgenie.com/api/v1/documents/550e8400-e29b-41d4-a716-446655440000/ \
  -H "Authorization: Bearer YOUR_API_KEY"
curl -X DELETE https://api.kycgenie.com/api/v2/documents/550e8400-e29b-41d4-a716-446655440000/ \
  -H "Authorization: Bearer YOUR_API_KEY"
resp = requests.delete(
    'https://api.kycgenie.com/api/v1/documents/550e8400-e29b-41d4-a716-446655440000/',
    headers={'Authorization': 'Bearer YOUR_API_KEY'},
)
# 204 No Content on success
client.documents.delete(
    document_id='550e8400-e29b-41d4-a716-446655440000',
)
# Returns None on success (204)
await fetch(
  'https://api.kycgenie.com/api/v1/documents/550e8400-e29b-41d4-a716-446655440000/',
  { method: 'DELETE', headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
// 204 No Content on success
await fetch(
  'https://api.kycgenie.com/api/v2/documents/550e8400-e29b-41d4-a716-446655440000/',
  { method: 'DELETE', headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
// 204 No Content on success

Get Tenant Document Download URL

curl https://api.kycgenie.com/api/v1/documents/550e8400-e29b-41d4-a716-446655440000/download/ \
  -H "Authorization: Bearer YOUR_API_KEY"
curl https://api.kycgenie.com/api/v2/documents/550e8400-e29b-41d4-a716-446655440000/download/ \
  -H "Authorization: Bearer YOUR_API_KEY"
resp = requests.get(
    'https://api.kycgenie.com/api/v1/documents/550e8400-e29b-41d4-a716-446655440000/download/',
    headers={'Authorization': 'Bearer YOUR_API_KEY'},
)
url = resp.json()['download_url']
# Fetch url directly to stream the file bytes
result = client.documents.get_download_url(
    document_id='550e8400-e29b-41d4-a716-446655440000',
)
# result.download_url - fetch directly, no auth needed
print(result.download_url)
const resp = await fetch(
  'https://api.kycgenie.com/api/v1/documents/550e8400-e29b-41d4-a716-446655440000/download/',
  { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
const { download_url } = await resp.json();
// Fetch download_url directly to stream the file
const resp = await fetch(
  'https://api.kycgenie.com/api/v2/documents/550e8400-e29b-41d4-a716-446655440000/download/',
  { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
const { download_url } = await resp.json();
// Fetch download_url directly to stream the file

Response (200 OK)

{
  "download_url": "https://app.kycgenie.com/api/v1/files/dl_C2e4f6a8b0c1d2e3/",
  "expires_at": "2026-01-30T14:38:45+00:00",
  "filename": "financial_statement.pdf",
  "content_type": "application/pdf",
  "size": 2457600
}

Entity Documents

Attach documents to a specific entity (company or individual). Entity documents are stored in entity-scoped folders and are accessible in KYC workflows for that entity.

Upload Entity Document

Upload a document and attach it to a specific entity.

Path Parameters

FieldTypeRequiredDescription
entity_id UUID Yes The entity's unique identifier

Request Body (multipart/form-data)

FieldTypeRequiredDescription
file file Yes The file to upload (max 10 MB)
document_type string Yes Document type label

Response (201 Created)

Returns a Document object with classification: "entity_private".

Allowed File Types

PDF, DOC, DOCX, XLS, XLSX, XLSM, PPT, PPTX, ODT, ODS, ODP, RTF, TXT, CSV, JPG, JPEG, PNG, TIFF, HEIC, HEIF, WEBP, BMP

List Entity Documents

Retrieve all documents attached to an entity.

Query Parameters

ParameterTypeDescription
document_type string Filter by document type label
limit v1 integer Max results (default 100)

Response shape differences

VersionEnvelope keyExtra fields
v1 documents count, entity_id, entity_name
v2 results count, entity_id, entity_name

Delete Entity Document

Permanently delete a document from an entity's folder. Returns 400 if the document is referenced by a response.

Response (204 No Content)

Empty body on success.

Get Entity Document Download URL

Generate a short-lived (15 min), single-use proxy download URL.

Response (200 OK)

Returns download_url, expires_at, filename, content_type, size.

Upload Entity Document

curl -X POST https://api.kycgenie.com/api/v1/entities/a1b2c3d4-e5f6-7890-abcd-ef1234567890/documents/upload/ \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "file=@bank_statement.pdf" \
  -F "document_type=Bank Statement"
# v2: POST directly to /documents/ (no /upload/ suffix)
curl -X POST https://api.kycgenie.com/api/v2/entities/a1b2c3d4-e5f6-7890-abcd-ef1234567890/documents/ \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "file=@bank_statement.pdf" \
  -F "document_type=Bank Statement"
import requests

entity_id = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'

with open('bank_statement.pdf', 'rb') as f:
    resp = requests.post(
        f'https://api.kycgenie.com/api/v1/entities/{entity_id}/documents/upload/',
        headers={'Authorization': 'Bearer YOUR_API_KEY'},
        files={'file': ('bank_statement.pdf', f, 'application/pdf')},
        data={'document_type': 'Bank Statement'},
    )

print(resp.json())
client = KYCGenie(api_key="YOUR_API_KEY")

with open('bank_statement.pdf', 'rb') as f:
    doc = client.entities.upload_document(
        entity_id='a1b2c3d4-e5f6-7890-abcd-ef1234567890',
        file=f,
        document_type='Bank Statement',
    )

print(doc.id, doc.name)
const entityId = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('document_type', 'Bank Statement');

const resp = await fetch(
  `https://api.kycgenie.com/api/v1/entities/${entityId}/documents/upload/`,
  {
    method: 'POST',
    headers: { 'Authorization': 'Bearer YOUR_API_KEY' },
    body: formData,
  }
);
console.log(await resp.json());
const entityId = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('document_type', 'Bank Statement');

// v2: POST to /documents/ directly (no /upload/ suffix)
const resp = await fetch(
  `https://api.kycgenie.com/api/v2/entities/${entityId}/documents/`,
  {
    method: 'POST',
    headers: { 'Authorization': 'Bearer YOUR_API_KEY' },
    body: formData,
  }
);
console.log(await resp.json());

Response (201 Created)

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "bank_statement.pdf",
  "size": 524288,
  "content_type": "application/pdf",
  "document_type_name": "Bank Statement",
  "classification": "entity_private",
  "uploaded_by_email": "api.user@example.com",
  "created_at": "2026-01-30T15:10:22Z",
  "expiry_date": null
}

List Entity Documents

curl https://api.kycgenie.com/api/v1/entities/a1b2c3d4-e5f6-7890-abcd-ef1234567890/documents/ \
  -H "Authorization: Bearer YOUR_API_KEY"
curl https://api.kycgenie.com/api/v2/entities/a1b2c3d4-e5f6-7890-abcd-ef1234567890/documents/ \
  -H "Authorization: Bearer YOUR_API_KEY"
resp = requests.get(
    'https://api.kycgenie.com/api/v1/entities/a1b2c3d4-e5f6-7890-abcd-ef1234567890/documents/',
    headers={'Authorization': 'Bearer YOUR_API_KEY'},
)
print(resp.json())
result = client.entities.list_documents(
    entity_id='a1b2c3d4-e5f6-7890-abcd-ef1234567890',
)
for doc in result.results:
    print(doc.id, doc.name)
const resp = await fetch(
  'https://api.kycgenie.com/api/v1/entities/a1b2c3d4-e5f6-7890-abcd-ef1234567890/documents/',
  { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
console.log(await resp.json());
const resp = await fetch(
  'https://api.kycgenie.com/api/v2/entities/a1b2c3d4-e5f6-7890-abcd-ef1234567890/documents/',
  { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
console.log(await resp.json());

Response (200 OK)

{
  "count": 1,
  "entity_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "entity_name": "Acme Corp Ltd",
  "documents": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "bank_statement.pdf",
      "size": 524288,
      "content_type": "application/pdf",
      "document_type_name": "Bank Statement",
      "classification": "entity_private",
      "uploaded_by_email": "api.user@example.com",
      "created_at": "2026-01-30T15:10:22Z",
      "expiry_date": null
    }
  ]
}

Response (200 OK)

{
  "count": 1,
  "entity_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "entity_name": "Acme Corp Ltd",
  "results": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "bank_statement.pdf",
      "size": 524288,
      "content_type": "application/pdf",
      "document_type_name": "Bank Statement",
      "classification": "entity_private",
      "uploaded_by_email": "api.user@example.com",
      "created_at": "2026-01-30T15:10:22Z",
      "expiry_date": null
    }
  ]
}

Delete Entity Document

curl -X DELETE https://api.kycgenie.com/api/v1/entities/a1b2c3d4-e5f6-7890-abcd-ef1234567890/documents/550e8400-e29b-41d4-a716-446655440000/ \
  -H "Authorization: Bearer YOUR_API_KEY"
curl -X DELETE https://api.kycgenie.com/api/v2/entities/a1b2c3d4-e5f6-7890-abcd-ef1234567890/documents/550e8400-e29b-41d4-a716-446655440000/ \
  -H "Authorization: Bearer YOUR_API_KEY"
resp = requests.delete(
    'https://api.kycgenie.com/api/v1/entities/a1b2c3d4-e5f6-7890-abcd-ef1234567890/documents/550e8400-e29b-41d4-a716-446655440000/',
    headers={'Authorization': 'Bearer YOUR_API_KEY'},
)
# 204 No Content on success
client.entities.delete_document(
    entity_id='a1b2c3d4-e5f6-7890-abcd-ef1234567890',
    document_id='550e8400-e29b-41d4-a716-446655440000',
)
# Returns None on success (204)
await fetch(
  'https://api.kycgenie.com/api/v1/entities/a1b2c3d4-e5f6-7890-abcd-ef1234567890/documents/550e8400-e29b-41d4-a716-446655440000/',
  { method: 'DELETE', headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
await fetch(
  'https://api.kycgenie.com/api/v2/entities/a1b2c3d4-e5f6-7890-abcd-ef1234567890/documents/550e8400-e29b-41d4-a716-446655440000/',
  { method: 'DELETE', headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);

Get Entity Document Download URL

curl https://api.kycgenie.com/api/v1/entities/a1b2c3d4-e5f6-7890-abcd-ef1234567890/documents/550e8400-e29b-41d4-a716-446655440000/download/ \
  -H "Authorization: Bearer YOUR_API_KEY"
curl https://api.kycgenie.com/api/v2/entities/a1b2c3d4-e5f6-7890-abcd-ef1234567890/documents/550e8400-e29b-41d4-a716-446655440000/download/ \
  -H "Authorization: Bearer YOUR_API_KEY"
resp = requests.get(
    'https://api.kycgenie.com/api/v1/entities/a1b2c3d4-e5f6-7890-abcd-ef1234567890/documents/550e8400-e29b-41d4-a716-446655440000/download/',
    headers={'Authorization': 'Bearer YOUR_API_KEY'},
)
url = resp.json()['download_url']
result = client.entities.get_document_download_url(
    entity_id='a1b2c3d4-e5f6-7890-abcd-ef1234567890',
    document_id='550e8400-e29b-41d4-a716-446655440000',
)
print(result.download_url)
const resp = await fetch(
  'https://api.kycgenie.com/api/v1/entities/a1b2c3d4-e5f6-7890-abcd-ef1234567890/documents/550e8400-e29b-41d4-a716-446655440000/download/',
  { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
const { download_url } = await resp.json();
const resp = await fetch(
  'https://api.kycgenie.com/api/v2/entities/a1b2c3d4-e5f6-7890-abcd-ef1234567890/documents/550e8400-e29b-41d4-a716-446655440000/download/',
  { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
const { download_url } = await resp.json();

Response (200 OK)

{
  "download_url": "https://app.kycgenie.com/api/v1/files/dl_A9b8c7d6e5f4a3b2/",
  "expires_at": "2026-01-30T15:25:22+00:00",
  "filename": "bank_statement.pdf",
  "content_type": "application/pdf",
  "size": 524288
}

Response Documents

Use the File Answers API in v2

Response document endpoints do not exist in v2. Use POST /questionnaire-responses/{id}/answers/files/ to upload, and /answers/files/{id}/ to retrieve or delete. Full reference in the Responses API.

Response Documents

Attach documents to questionnaire responses. Each attachment creates a FileAnswer record that links back to the file question in the DDQ (if applicable).

Upload Response Document

Upload a file and attach it to a response. Optionally link it to a specific file question.

POST /api/v1/responses/{response_id}/documents/upload/

Request Body (multipart/form-data)

FieldTypeRequiredDescription
file file Yes The file to upload (max 10 MB)
document_type string Yes Document type label
file_question_id integer No Links the upload to a specific FileQuestion in the DDQ

Response (201 Created)

Returns a FileAnswer object with nested document metadata and file_question_* correlation fields.

List Response Documents

Get all document attachments on a response.

GET /api/v1/responses/{response_id}/documents/

Response (200 OK)

Returns { count, data: [...] } - an array of FileAnswer objects.

Get Response Document

Retrieve metadata for a single response attachment by attachment_id.

GET /api/v1/responses/{response_id}/documents/{attachment_id}/

Response (200 OK)

Returns the same FileAnswer shape as a single list item.

Get Response Document Download URL

Generate a short-lived (15 min), single-use proxy download URL for a response attachment.

GET /api/v1/responses/{response_id}/documents/{attachment_id}/download/

Response (200 OK)

FieldTypeDescription
download_urlstringProxy URL - fetch directly, no auth required
expires_atdatetimeISO 8601 expiry (15 min)
attachment_idUUIDThe FileAnswer attachment identifier
filenamestringOriginal filename
content_typestringMIME type
sizeintegerFile size in bytes

Delete Response Document

Remove a document attachment from a response. If no other attachments reference the underlying file, the file is also deleted from storage.

DELETE /api/v1/responses/{response_id}/documents/{attachment_id}/

Response (204 No Content)

Empty body on success. Returns 404 if the attachment does not exist or has already been deleted.

Upload Response Document

curl -X POST https://api.kycgenie.com/api/v1/responses/f1a2b3c4-d5e6-7890-abcd-ef1234567890/documents/upload/ \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "file=@bank_statement.pdf" \
  -F "document_type=Bank Statement" \
  -F "file_question_id=42"
Use the file answers endpoint for v2: PUT /questionnaire-responses/{id}/answers/{fq_id}/file/
import requests, uuid

response_id = 'f1a2b3c4-d5e6-7890-abcd-ef1234567890'

with open('bank_statement.pdf', 'rb') as f:
    resp = requests.post(
        f'https://api.kycgenie.com/api/v1/responses/{response_id}/documents/upload/',
        headers={
            'Authorization': 'Bearer YOUR_API_KEY',
            'Idempotency-Key': str(uuid.uuid4()),
        },
        files={'file': ('bank_statement.pdf', f, 'application/pdf')},
        data={
            'document_type': 'Bank Statement',
            'file_question_id': 42,   # optional
        },
    )

print(resp.json())
The Python SDK targets v2 endpoints. Use client.questionnaire_responses.upload_file_answer() or the file answers API for SDK-based uploads.
const responseId = 'f1a2b3c4-d5e6-7890-abcd-ef1234567890';
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('document_type', 'Bank Statement');
formData.append('file_question_id', '42');  // optional

const resp = await fetch(
  `https://api.kycgenie.com/api/v1/responses/${responseId}/documents/upload/`,
  {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY',
      'Idempotency-Key': crypto.randomUUID(),
    },
    body: formData,
  }
);
console.log(await resp.json());
Use the file answers endpoint for v2: PUT /questionnaire-responses/{id}/answers/{fq_id}/file/

Response (201 Created)

{
  "attachment_id": "abb6336c-7edc-4814-92eb-4a80dc5b17fd",
  "response_id": "f1a2b3c4-d5e6-7890-abcd-ef1234567890",
  "document": {
    "id": "880f1733-h50e-74g7-d049-779988773333",
    "name": "bank_statement_jan_2026.pdf",
    "size": 524288,
    "content_type": "application/pdf",
    "document_type_name": "Bank Statement",
    "classification": "entity_private",
    "uploaded_by_email": null,
    "created_at": "2026-01-30T15:10:22Z",
    "expiry_date": null
  },
  "file_question_id": 42,
  "file_question_type": "Bank Statement",
  "file_question_description": "Please provide your most recent bank statement",
  "file_question_required": true,
  "file_question_multiple": false,
  "is_flagged": null,
  "is_submitted": false,
  "created_at": "2026-01-30T15:10:22Z",
  "updated_at": "2026-01-30T15:10:22Z"
}

List Response Documents

curl https://api.kycgenie.com/api/v1/responses/f1a2b3c4-d5e6-7890-abcd-ef1234567890/documents/ \
  -H "Authorization: Bearer YOUR_API_KEY"
Use GET /questionnaire-responses/{id}/answers/files/ in v2.
resp = requests.get(
    'https://api.kycgenie.com/api/v1/responses/f1a2b3c4-d5e6-7890-abcd-ef1234567890/documents/',
    headers={'Authorization': 'Bearer YOUR_API_KEY'},
)
print(resp.json())
Use the file answers API for SDK-based listing.
const resp = await fetch(
  'https://api.kycgenie.com/api/v1/responses/f1a2b3c4-d5e6-7890-abcd-ef1234567890/documents/',
  { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
console.log(await resp.json());
Use GET /questionnaire-responses/{id}/answers/files/ in v2.

Response (200 OK)

{
  "count": 1,
  "data": [
    {
      "attachment_id": "abb6336c-7edc-4814-92eb-4a80dc5b17fd",
      "response_id": "f1a2b3c4-d5e6-7890-abcd-ef1234567890",
      "document": {
        "id": "880f1733-h50e-74g7-d049-779988773333",
        "name": "bank_statement_jan_2026.pdf",
        "size": 524288,
        "content_type": "application/pdf",
        "document_type_name": "Bank Statement",
        "classification": "entity_private",
        "uploaded_by_email": null,
        "created_at": "2026-01-30T15:10:22Z",
        "expiry_date": null
      },
      "file_question_id": 42,
      "file_question_type": "Bank Statement",
      "file_question_description": "Please provide your most recent bank statement",
      "file_question_required": true,
      "file_question_multiple": false,
      "is_flagged": null,
      "is_submitted": false,
      "created_at": "2026-01-30T15:10:22Z",
      "updated_at": "2026-01-30T15:10:22Z"
    }
  ]
}

Get Response Document

curl https://api.kycgenie.com/api/v1/responses/f1a2b3c4-d5e6-7890-abcd-ef1234567890/documents/abb6336c-7edc-4814-92eb-4a80dc5b17fd/ \
  -H "Authorization: Bearer YOUR_API_KEY"
Use GET /questionnaire-responses/{id}/answers/files/{attachment_id}/ in v2.
resp = requests.get(
    'https://api.kycgenie.com/api/v1/responses/f1a2b3c4-d5e6-7890-abcd-ef1234567890/documents/abb6336c-7edc-4814-92eb-4a80dc5b17fd/',
    headers={'Authorization': 'Bearer YOUR_API_KEY'},
)
print(resp.json())
Use the file answers API for SDK-based retrieval.
const resp = await fetch(
  'https://api.kycgenie.com/api/v1/responses/f1a2b3c4-d5e6-7890-abcd-ef1234567890/documents/abb6336c-7edc-4814-92eb-4a80dc5b17fd/',
  { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
console.log(await resp.json());
Use GET /questionnaire-responses/{id}/answers/files/{attachment_id}/ in v2.

Get Response Document Download URL

curl https://api.kycgenie.com/api/v1/responses/f1a2b3c4-d5e6-7890-abcd-ef1234567890/documents/abb6336c-7edc-4814-92eb-4a80dc5b17fd/download/ \
  -H "Authorization: Bearer YOUR_API_KEY"
Use GET /questionnaire-responses/{id}/answers/files/{attachment_id}/download/ in v2.
resp = requests.get(
    'https://api.kycgenie.com/api/v1/responses/f1a2b3c4-d5e6-7890-abcd-ef1234567890/documents/abb6336c-7edc-4814-92eb-4a80dc5b17fd/download/',
    headers={'Authorization': 'Bearer YOUR_API_KEY'},
)
url = resp.json()['download_url']
# Fetch url directly - no auth needed
Use the file answers download endpoint for SDK-based downloads.
const resp = await fetch(
  'https://api.kycgenie.com/api/v1/responses/f1a2b3c4-d5e6-7890-abcd-ef1234567890/documents/abb6336c-7edc-4814-92eb-4a80dc5b17fd/download/',
  { headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
const { download_url } = await resp.json();
// Fetch download_url directly
Use GET /questionnaire-responses/{id}/answers/files/{attachment_id}/download/ in v2.

Response (200 OK)

{
  "download_url": "https://app.kycgenie.com/api/v1/files/dl_C2e4f6a8b0c1d2e3/",
  "expires_at": "2026-01-30T15:25:22+00:00",
  "attachment_id": "abb6336c-7edc-4814-92eb-4a80dc5b17fd",
  "filename": "bank_statement_jan_2026.pdf",
  "content_type": "application/pdf",
  "size": 524288
}

Delete Response Document

curl -X DELETE https://api.kycgenie.com/api/v1/responses/f1a2b3c4-d5e6-7890-abcd-ef1234567890/documents/abb6336c-7edc-4814-92eb-4a80dc5b17fd/ \
  -H "Authorization: Bearer YOUR_API_KEY"
# Returns 204 No Content
Use DELETE /questionnaire-responses/{id}/answers/files/{attachment_id}/ in v2.
resp = requests.delete(
    'https://api.kycgenie.com/api/v1/responses/f1a2b3c4-d5e6-7890-abcd-ef1234567890/documents/abb6336c-7edc-4814-92eb-4a80dc5b17fd/',
    headers={'Authorization': 'Bearer YOUR_API_KEY'},
)
# 204 No Content on success
Use the file answers delete endpoint for SDK-based deletion.
await fetch(
  'https://api.kycgenie.com/api/v1/responses/f1a2b3c4-d5e6-7890-abcd-ef1234567890/documents/abb6336c-7edc-4814-92eb-4a80dc5b17fd/',
  { method: 'DELETE', headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
// 204 No Content on success
Use DELETE /questionnaire-responses/{id}/answers/files/{attachment_id}/ in v2.

Document Classification

Classification is assigned automatically by the server based on the upload endpoint - it is not a request parameter.

Upload endpoint Classification Meaning
POST /documents/upload/ tenant_private Stored in your organisation's library. Accessible only to your tenant users.
POST /entities/{id}/documents/upload/ POST /entities/{id}/documents/ entity_private Attached to a specific entity. Accessible in that entity's KYC workflows.
POST /responses/{id}/documents/upload/ entity_private Attached to a DDQ response and its entity. Accessible in response workflows.

The classification field is returned on all document objects for informational purposes.

Error Handling

StatusCauseExample body
400 Invalid file type {"file": ["File type not allowed. Allowed types: .pdf, .doc, ..."]}
400 File too large {"file": ["File size exceeds maximum allowed size of 10MB"]}
400 Document referenced by a response (cannot delete) {"error": "Cannot delete document.", "details": {"reason": "This document is referenced in one or more responses."}}
404 Entity, response, or document not found {"detail": "Not found."}
500 Storage upload failure {"error": "File upload failed.", "details": {"reason": "..."}}

Best Practices

Upload Strategy
  • Idempotency - Include an Idempotency-Key header on uploads to safely retry without creating duplicates
  • Batch Uploads - For multiple documents, upload sequentially with proper error handling
  • File Size - Compress large PDFs before upload to stay under the 10 MB limit
  • Document Types - Use consistent type labels across your organisation for reliable autofill
  • Download tokens - Tokens expire after 15 minutes and are single-use. Generate one immediately before you need it