Sources
Create, update, list, and delete knowledge base sources
Sources
Manage individual sources in your knowledge base. Sources are identified by an externalId that you control, enabling upsert semantics.
Create or Update Source
Create a new source or update an existing one. If a source with the same externalId exists, it will be updated.
Endpoint
POST /api/v1/kb/sourcesAuthentication
# API Key
x-api-key: YOUR_API_KEY
# Bearer Token
Authorization: Bearer YOUR_API_KEYRequest Body
| Field | Type | Required | Description |
|---|---|---|---|
collectionId | string | Yes | ID of the knowledge collection |
externalId | string | Yes | Your unique identifier for this source (max 255 chars) |
title | string | Yes | Display title for the source (max 500 chars) |
content | string | Yes | Markdown content (max 500KB) |
metadata | object | No | Additional metadata to store with the source |
Response
{
"data": {
"documentId": "uuid",
"externalId": "your-external-id",
"status": "processing" | "unchanged",
"message": "string"
},
"meta": {
"timestamp": "ISO 8601"
}
}Status values:
processing- Content queued for processing (new or updated)unchanged- Content hash matches existing source, no processing needed
Examples
Create a New Source
curl -X POST https://v2.heppu.ai/api/v1/kb/sources \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"collectionId": "550e8400-e29b-41d4-a716-446655440000",
"externalId": "car-12345",
"title": "2024 BMW X5 xDrive40i",
"content": "# 2024 BMW X5 xDrive40i\n\n## Overview\nThe BMW X5 is a luxury mid-size SUV...\n\n## Specifications\n- Engine: 3.0L TwinPower Turbo inline-6\n- Horsepower: 335 hp\n- Torque: 331 lb-ft\n\n## Pricing\nMSRP: $65,200"
}'const response = await fetch('https://v2.heppu.ai/api/v1/kb/sources', {
method: 'POST',
headers: {
'x-api-key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
collectionId: '550e8400-e29b-41d4-a716-446655440000',
externalId: 'car-12345',
title: '2024 BMW X5 xDrive40i',
content: `# 2024 BMW X5 xDrive40i
## Overview
The BMW X5 is a luxury mid-size SUV...
## Specifications
- Engine: 3.0L TwinPower Turbo inline-6
- Horsepower: 335 hp
- Torque: 331 lb-ft
## Pricing
MSRP: $65,200`
})
});
const data = await response.json();
console.log('Source created:', data.data);import requests
response = requests.post(
'https://v2.heppu.ai/api/v1/kb/sources',
headers={'x-api-key': 'YOUR_API_KEY'},
json={
'collectionId': '550e8400-e29b-41d4-a716-446655440000',
'externalId': 'car-12345',
'title': '2024 BMW X5 xDrive40i',
'content': '''# 2024 BMW X5 xDrive40i
## Overview
The BMW X5 is a luxury mid-size SUV...
## Specifications
- Engine: 3.0L TwinPower Turbo inline-6
- Horsepower: 335 hp
- Torque: 331 lb-ft
## Pricing
MSRP: $65,200'''
}
)
data = response.json()
print(f"Source created: {data['data']}")Update an Existing Source
When you POST with an existing externalId, the source is updated and reprocessed:
curl -X POST https://v2.heppu.ai/api/v1/kb/sources \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"collectionId": "550e8400-e29b-41d4-a716-446655440000",
"externalId": "car-12345",
"title": "2024 BMW X5 xDrive40i - SOLD",
"content": "# 2024 BMW X5 xDrive40i\n\n**Status: SOLD**\n\nThis vehicle has been sold."
}'// Update existing source (same externalId)
const response = await fetch('https://v2.heppu.ai/api/v1/kb/sources', {
method: 'POST',
headers: {
'x-api-key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
collectionId: '550e8400-e29b-41d4-a716-446655440000',
externalId: 'car-12345', // Same externalId = update
title: '2024 BMW X5 xDrive40i - SOLD',
content: '# 2024 BMW X5 xDrive40i\n\n**Status: SOLD**\n\nThis vehicle has been sold.'
})
});
const data = await response.json();
console.log('Source updated:', data.data.status); // "processing"# Update existing source (same externalId)
response = requests.post(
'https://v2.heppu.ai/api/v1/kb/sources',
headers={'x-api-key': 'YOUR_API_KEY'},
json={
'collectionId': '550e8400-e29b-41d4-a716-446655440000',
'externalId': 'car-12345', # Same externalId = update
'title': '2024 BMW X5 xDrive40i - SOLD',
'content': '# 2024 BMW X5 xDrive40i\n\n**Status: SOLD**\n\nThis vehicle has been sold.'
}
)
data = response.json()
print(f"Source updated: {data['data']['status']}") # "processing"Source with Metadata
curl -X POST https://v2.heppu.ai/api/v1/kb/sources \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"collectionId": "550e8400-e29b-41d4-a716-446655440000",
"externalId": "car-12345",
"title": "2024 BMW X5 xDrive40i",
"content": "# 2024 BMW X5 xDrive40i\n\nLuxury SUV with premium features...",
"metadata": {
"vin": "5UXCR6C05R9S12345",
"stock_number": "A12345",
"price": 65200,
"mileage": 0,
"condition": "new",
"source_url": "https://dealer.com/inventory/12345",
"last_scraped": "2024-12-19T10:00:00Z"
}
}'const response = await fetch('https://v2.heppu.ai/api/v1/kb/sources', {
method: 'POST',
headers: {
'x-api-key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
collectionId: '550e8400-e29b-41d4-a716-446655440000',
externalId: 'car-12345',
title: '2024 BMW X5 xDrive40i',
content: '# 2024 BMW X5 xDrive40i\n\nLuxury SUV with premium features...',
metadata: {
vin: '5UXCR6C05R9S12345',
stock_number: 'A12345',
price: 65200,
mileage: 0,
condition: 'new',
source_url: 'https://dealer.com/inventory/12345',
last_scraped: new Date().toISOString()
}
})
});from datetime import datetime
response = requests.post(
'https://v2.heppu.ai/api/v1/kb/sources',
headers={'x-api-key': 'YOUR_API_KEY'},
json={
'collectionId': '550e8400-e29b-41d4-a716-446655440000',
'externalId': 'car-12345',
'title': '2024 BMW X5 xDrive40i',
'content': '# 2024 BMW X5 xDrive40i\n\nLuxury SUV with premium features...',
'metadata': {
'vin': '5UXCR6C05R9S12345',
'stock_number': 'A12345',
'price': 65200,
'mileage': 0,
'condition': 'new',
'source_url': 'https://dealer.com/inventory/12345',
'last_scraped': datetime.utcnow().isoformat() + 'Z'
}
}
)List Sources
Retrieve sources in a collection with optional filtering.
Endpoint
GET /api/v1/kb/sourcesQuery Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
collectionId | string | Yes | - | ID of the knowledge collection |
externalId | string | No | - | Filter by specific externalId |
status | string | No | - | Filter by processing status |
limit | integer | No | 50 | Results per page (max: 100) |
offset | integer | No | 0 | Pagination offset |
Status values: pending, processing, completed, failed
Response
{
"data": {
"sources": [
{
"id": "uuid",
"externalId": "string",
"title": "string",
"status": "string",
"chunkCount": "integer",
"fileSize": "integer",
"error": "string|null",
"createdAt": "ISO 8601",
"updatedAt": "ISO 8601",
"metadata": "object"
}
],
"pagination": {
"total": "integer",
"limit": "integer",
"offset": "integer",
"hasMore": "boolean"
}
}
}Examples
List All Sources
curl "https://v2.heppu.ai/api/v1/kb/sources?collectionId=550e8400-e29b-41d4-a716-446655440000" \
-H "x-api-key: YOUR_API_KEY"const collectionId = '550e8400-e29b-41d4-a716-446655440000';
const response = await fetch(
`https://v2.heppu.ai/api/v1/kb/sources?collectionId=${collectionId}`,
{
headers: { 'x-api-key': 'YOUR_API_KEY' }
}
);
const data = await response.json();
console.log(`Found ${data.data.pagination.total} sources`);
data.data.sources.forEach(source => {
console.log(`- ${source.title}: ${source.status} (${source.chunkCount} chunks)`);
});response = requests.get(
'https://v2.heppu.ai/api/v1/kb/sources',
headers={'x-api-key': 'YOUR_API_KEY'},
params={'collectionId': '550e8400-e29b-41d4-a716-446655440000'}
)
data = response.json()
print(f"Found {data['data']['pagination']['total']} sources")
for source in data['data']['sources']:
print(f"- {source['title']}: {source['status']} ({source['chunkCount']} chunks)")Check Processing Status
# Check status of a specific source
curl "https://v2.heppu.ai/api/v1/kb/sources?collectionId=550e8400-e29b-41d4-a716-446655440000&externalId=car-12345" \
-H "x-api-key: YOUR_API_KEY"
# List all failed sources
curl "https://v2.heppu.ai/api/v1/kb/sources?collectionId=550e8400-e29b-41d4-a716-446655440000&status=failed" \
-H "x-api-key: YOUR_API_KEY"// Poll for processing completion
async function waitForProcessing(collectionId, externalId) {
while (true) {
const response = await fetch(
`https://v2.heppu.ai/api/v1/kb/sources?collectionId=${collectionId}&externalId=${externalId}`,
{ headers: { 'x-api-key': 'YOUR_API_KEY' } }
);
const data = await response.json();
const source = data.data.sources[0];
if (!source) {
throw new Error('Source not found');
}
if (source.status === 'completed') {
console.log(`Processing complete: ${source.chunkCount} chunks`);
return source;
}
if (source.status === 'failed') {
throw new Error(`Processing failed: ${source.error}`);
}
console.log(`Status: ${source.status}, waiting...`);
await new Promise(resolve => setTimeout(resolve, 2000));
}
}import time
def wait_for_processing(collection_id, external_id):
"""Poll until processing is complete"""
while True:
response = requests.get(
'https://v2.heppu.ai/api/v1/kb/sources',
headers={'x-api-key': 'YOUR_API_KEY'},
params={
'collectionId': collection_id,
'externalId': external_id
}
)
data = response.json()
sources = data['data']['sources']
if not sources:
raise Exception('Source not found')
source = sources[0]
if source['status'] == 'completed':
print(f"Processing complete: {source['chunkCount']} chunks")
return source
if source['status'] == 'failed':
raise Exception(f"Processing failed: {source['error']}")
print(f"Status: {source['status']}, waiting...")
time.sleep(2)Delete Sources
Bulk delete sources by their externalIds.
Endpoint
DELETE /api/v1/kb/sourcesRequest Body
| Field | Type | Required | Description |
|---|---|---|---|
collectionId | string | Yes | ID of the knowledge collection |
externalIds | string[] | Yes | Array of externalIds to delete (max: 100) |
Response
{
"data": {
"deleted": "integer",
"deletedExternalIds": ["string"],
"notFound": ["string"],
"message": "string"
}
}Examples
curl -X DELETE https://v2.heppu.ai/api/v1/kb/sources \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"collectionId": "550e8400-e29b-41d4-a716-446655440000",
"externalIds": ["car-12345", "car-12346", "car-12347"]
}'const response = await fetch('https://v2.heppu.ai/api/v1/kb/sources', {
method: 'DELETE',
headers: {
'x-api-key': 'YOUR_API_KEY',
'Content-Type': 'application/json'
},
body: JSON.stringify({
collectionId: '550e8400-e29b-41d4-a716-446655440000',
externalIds: ['car-12345', 'car-12346', 'car-12347']
})
});
const data = await response.json();
console.log(`Deleted ${data.data.deleted} sources`);
if (data.data.notFound?.length > 0) {
console.log('Not found:', data.data.notFound);
}response = requests.delete(
'https://v2.heppu.ai/api/v1/kb/sources',
headers={'x-api-key': 'YOUR_API_KEY'},
json={
'collectionId': '550e8400-e29b-41d4-a716-446655440000',
'externalIds': ['car-12345', 'car-12346', 'car-12347']
}
)
data = response.json()
print(f"Deleted {data['data']['deleted']} sources")
if data['data'].get('notFound'):
print(f"Not found: {data['data']['notFound']}")Error Responses
Validation Error
{
"error": {
"message": "Validation error: collectionId is required",
"status": 400,
"timestamp": "ISO 8601"
}
}Collection Not Found
{
"error": {
"message": "Collection 550e8400-... not found or does not belong to your organization",
"status": 404,
"timestamp": "ISO 8601"
}
}Content Too Large
{
"error": {
"message": "Validation error: content too long (max 500KB)",
"status": 400,
"timestamp": "ISO 8601"
}
}Best Practices
- Use meaningful externalIds - Use IDs from your source system (e.g., VIN, SKU, document ID)
- Structure content with Markdown - Use headings to organize content for better chunking
- Include relevant metadata - Store source URLs, timestamps, and other tracking info
- Handle unchanged content - Check for
unchangedstatus to avoid unnecessary processing - Poll for completion - Use the GET endpoint to verify processing completed successfully
- Delete sold/removed items - Clean up sources that are no longer relevant
Related
- Batch Sources - Create or update up to 50 sources at once
- Knowledge Base Overview - API overview and quick start