Documents API Reference
Upload, manage, and retrieve documents across your tenant library, entities, and questionnaire responses.
- 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
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)
| Field | Type | Required | Description |
|---|---|---|---|
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
| Parameter | Type | Description |
|---|---|---|
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)
| Field | Type | Description |
|---|---|---|
download_url | string | Proxy URL - fetch directly, no auth required |
expires_at | datetime | ISO 8601 expiry timestamp (15 min from now) |
filename | string | Original filename |
content_type | string | MIME type |
size | integer | File 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
| Field | Type | Required | Description |
|---|---|---|---|
entity_id |
UUID | Yes | The entity's unique identifier |
Request Body (multipart/form-data)
| Field | Type | Required | Description |
|---|---|---|---|
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".
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
| Parameter | Type | Description |
|---|---|---|
document_type |
string | Filter by document type label |
limit v1 |
integer | Max results (default 100) |
Response shape differences
| Version | Envelope key | Extra 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
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.
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/
|
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
| Status | Cause | Example 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
- Idempotency - Include an
Idempotency-Keyheader 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