Sources
Push one source, list them, delete them.
Upsert one
POST
/api/v1/kb/sourcescollectionIduuidrequiredexternalIdstringrequiredYour id. Max 255. Upsert key inside the collection.
titlestringrequiredMax 500.
contentstringrequiredMarkdown or plain text. Max 500k chars.
validFromRFC3339 | nullWith timezone offset, e.g.
2026-09-01T00:00:00+03:00.validUntilRFC3339 | nullMust be after
validFrom.contentRolecurrent | historical | nullmetadataobjectStored and returned. We add
api_created_at / api_updated_at to it.curl -X POST https://v2.heppu.ai/api/v1/kb/sources \
-H "x-api-key: hep_..." -H "Content-Type: application/json" \
-d '{
"collectionId": "col_…",
"externalId": "VIN-123",
"title": "2021 Volvo XC60",
"content": "# 2021 Volvo XC60\n\nPrice 34 900 €. 61 000 km. …",
"validUntil": "2026-12-31T23:59:59+02:00"
}'201 new, 200 updated or unchanged:
{
"data": {
"documentId": "1f0a…",
"externalId": "VIN-123",
"status": "processing",
"message": "Source queued for processing"
},
"meta": { "timestamp": "…" }
}status is processing or unchanged. Note that on unchanged we still update the validity fields and contentRole, but not title or metadata. Change the content if you need those to move.
404 if the collection isn't yours. 400 with all validation issues joined.
List
GET
/api/v1/kb/sourcesOnly sources created through the API. Uploads and crawls don't show here.
collectionIduuidrequiredQuery param.
externalIdstringExact match.
statuspending | processing | completed | failedlimitintMax 100.default 50
offsetintdefault 0
{
"data": {
"sources": [
{
"id": "1f0a…",
"externalId": "VIN-123",
"title": "2021 Volvo XC60",
"status": "completed",
"chunkCount": 7,
"fileSize": 4211,
"error": null,
"validFrom": null,
"validUntil": "2026-12-31T21:59:59.000Z",
"contentRole": "current",
"metadata": { "api_created_at": "…" },
"createdAt": "…",
"updatedAt": "…"
}
],
"pagination": { "total": 128, "limit": 50, "offset": 0, "hasMore": true }
},
"meta": { "timestamp": "…" }
}error is the processing error text when status is failed. Ordered by updatedAt desc.
Delete
DELETE
/api/v1/kb/sourcesYes, a DELETE with a body.
{ "collectionId": "col_…", "externalIds": ["VIN-123", "VIN-124"] }Max 100 ids. Removes them from this collection. If a source is also in another collection it survives there, otherwise the document, its chunks and its file go too.
{
"data": {
"deleted": 2,
"documentsDeleted": 1,
"deletedExternalIds": ["VIN-123", "VIN-124"],
"notFound": ["VIN-999"],
"message": "Removed 2 source(s) from collection col_…; 1 deleted entirely, 1 kept for other collections"
},
"meta": { "timestamp": "…" }
}notFound is omitted when empty. Always 200, even if nothing matched.
