Data sources API
Use the data sources API to keep a bot's knowledge in step with your own system, such as a product catalogue, CMS or internal database. You push text to the bot as manual data sources, then sync them so the bot can answer from them.
Before you start
You need the API channel turned on for your bot, its Channel ID and a Secret key. The API channel is on the Standard, Pro and Enterprise plans. See API overview.
How it works
- Create a manual data source and keep its
id. - Add rows to it. Each row is one document of markdown text, such as one product or one help article.
- Sync the data source. The bot only learns new or changed rows after a sync.
- When your content changes, update or delete rows, then sync again.
This API only works with manual data sources that belong to the bot the API channel is on. To add websites, feeds or YouTube from code, use the MCP server. To learn about manual data sources in the dashboard, see Add text manually.
Syncing uses storage tokens, and each data source counts towards your plan's data source limit. See Storage tokens and Plans.
Endpoints
All paths start with https://app.chatthing.ai/api/public/channels/{channelId}/2.0. They also work with 1.0 in place of 2.0.
| Method | Path | What it does |
|---|---|---|
POST | /data-sources | Create a manual data source |
POST | /data-sources/list | List manual data sources |
GET | /data-sources?dataSourceId=... | Get a manual data source |
PUT | /data-sources | Update a manual data source |
DELETE | /data-sources | Delete a manual data source |
POST | /data-sources/sync | Sync a data source |
POST | /data-sources/row | Add rows |
POST | /data-sources/row/list | List rows |
GET | /data-sources/row?dataSourceId=...&dataSourceRowId=... | Get a row |
PUT | /data-sources/row | Update rows |
DELETE | /data-sources/row | Delete rows |
Every request needs these headers:
Content-Type: application/json
X-API-Secret-Key: {secretKey}
The data source object
| Field | Type | Description |
|---|---|---|
id | string | The data source's id |
type | string | Always MANUAL |
state | string | not_synced, syncing, synced or sync_error |
lastSync | string or null | When it last synced successfully (ISO 8601) |
syncInterval | string or null | How often it re-syncs automatically: day, week, month or null for never |
createdAt | string | When it was created (ISO 8601) |
Create a manual data source
Creates an empty manual data source on the bot. There's no request body.
POST /data-sources
curl --request POST \
--url https://app.chatthing.ai/api/public/channels/{channelId}/2.0/data-sources \
--header 'Content-Type: application/json' \
--header 'X-API-Secret-Key: {secretKey}'
{
"success": true,
"dataSource": {
"id": "41559b21-b8ff-44c3-8658-1c9e34273070",
"type": "MANUAL",
"state": "not_synced",
"lastSync": null,
"syncInterval": null,
"createdAt": "2026-01-27T15:02:51.970Z"
}
}
If the bot already has as many data sources as your plan allows, you get 429 and "success": false.
List manual data sources
Returns the bot's manual data sources, oldest first, a page at a time.
POST /data-sources/list
| Field | Type | Description |
|---|---|---|
limit | integer | Optional. Items per page, from 1 to 100. Default 10 |
cursor | string | Optional. To get the next page, send the pagination.last value from the previous response |
curl --request POST \
--url https://app.chatthing.ai/api/public/channels/{channelId}/2.0/data-sources/list \
--header 'Content-Type: application/json' \
--header 'X-API-Secret-Key: {secretKey}' \
--data '{ "limit": 10 }'
{
"success": true,
"dataSources": [
{
"id": "19d95fbc-8285-40bc-8cc0-3e1fe597b765",
"type": "MANUAL",
"state": "synced",
"lastSync": "2026-01-27T14:39:03.038Z",
"syncInterval": null,
"createdAt": "2025-10-31T14:52:19.264Z"
}
],
"pagination": {
"total": 1,
"last": "19d95fbc-8285-40bc-8cc0-3e1fe597b765"
}
}
pagination.total is the total number of manual data sources on the bot. pagination.last is the id of the last item on this page (null if the page is empty).
Get a manual data source
Returns one manual data source. Use it to check whether a sync has finished.
GET /data-sources?dataSourceId={dataSourceId}
curl --request GET \
--url 'https://app.chatthing.ai/api/public/channels/{channelId}/2.0/data-sources?dataSourceId={dataSourceId}' \
--header 'X-API-Secret-Key: {secretKey}'
The response is { "success": true, "dataSource": { ... } } with the data source object. You get 404 if it doesn't exist, isn't manual or belongs to another bot.
Update a manual data source
Changes how often a manual data source re-syncs on its own.
PUT /data-sources
| Field | Type | Description |
|---|---|---|
dataSourceId | string | Required. The data source's id |
syncInterval | string or null | day, week, month, or null to only sync when you ask. Defaults to null if left out |
curl --request PUT \
--url https://app.chatthing.ai/api/public/channels/{channelId}/2.0/data-sources \
--header 'Content-Type: application/json' \
--header 'X-API-Secret-Key: {secretKey}' \
--data '{
"dataSourceId": "41559b21-b8ff-44c3-8658-1c9e34273070",
"syncInterval": "week"
}'
The response is { "success": true, "dataSource": { ... } }. The returned object shows the data source as it was before this change, so call Get a manual data source if you need to confirm the new value.
Delete a manual data source
Deletes a manual data source and everything in it. The bot stops using it. This can't be undone.
DELETE /data-sources
| Field | Type | Description |
|---|---|---|
dataSourceId | string | Required. The data source's id |
curl --request DELETE \
--url https://app.chatthing.ai/api/public/channels/{channelId}/2.0/data-sources \
--header 'Content-Type: application/json' \
--header 'X-API-Secret-Key: {secretKey}' \
--data '{ "dataSourceId": "41559b21-b8ff-44c3-8658-1c9e34273070" }'
Returns { "success": true }.
Sync a data source
Starts a sync, so the bot learns any rows you've added, changed or deleted. Syncing runs in the background: poll Get a manual data source until state is synced (or sync_error).
POST /data-sources/sync
| Field | Type | Description |
|---|---|---|
dataSourceId | string | Required. The data source to sync |
curl --request POST \
--url https://app.chatthing.ai/api/public/channels/{channelId}/2.0/data-sources/sync \
--header 'Content-Type: application/json' \
--header 'X-API-Secret-Key: {secretKey}' \
--data '{ "dataSourceId": "41559b21-b8ff-44c3-8658-1c9e34273070" }'
Returns { "success": true } when the sync has started. You get 429 if the data source is already syncing or your team is out of storage tokens.
You can also trigger a sync from another system without the API key by using a Sync trigger webhook, and get told when a sync finishes with the Sync success and Sync failure webhooks.
Add rows
Adds one or more rows to a manual data source. Each row is one document. Remember to sync afterwards.
POST /data-sources/row
| Field | Type | Description |
|---|---|---|
dataSourceId | string | Required. The data source to add to |
dataSourceRows | array | Required. The rows to add |
dataSourceRows[].name | string | A name for the row that means something to you, such as a file name or product code. The bot isn't trained on it |
dataSourceRows[].content | string | The row's content as markdown (UTF-8). This is what the bot learns |
curl --request POST \
--url https://app.chatthing.ai/api/public/channels/{channelId}/2.0/data-sources/row \
--header 'Content-Type: application/json' \
--header 'X-API-Secret-Key: {secretKey}' \
--data '{
"dataSourceId": "41559b21-b8ff-44c3-8658-1c9e34273070",
"dataSourceRows": [
{ "name": "returns.md", "content": "# Returns\nYou can return any item within 30 days of delivery." },
{ "name": "delivery.md", "content": "# Delivery\nWe deliver to the UK and Ireland in 3 to 5 working days." }
]
}'
Returns { "success": true }. The new row ids aren't returned: use List rows to find them.
List rows
Returns the rows in a manual data source, oldest first, a page at a time.
POST /data-sources/row/list
| Field | Type | Description |
|---|---|---|
dataSourceId | string | Required. The data source to list |
limit | integer | Optional. Rows per page, from 1 to 100. Default 10 |
cursor | string | Optional. To get the next page, send the pagination.last value from the previous response |
curl --request POST \
--url https://app.chatthing.ai/api/public/channels/{channelId}/2.0/data-sources/row/list \
--header 'Content-Type: application/json' \
--header 'X-API-Secret-Key: {secretKey}' \
--data '{
"dataSourceId": "41559b21-b8ff-44c3-8658-1c9e34273070",
"limit": 10
}'
{
"success": true,
"dataSourceRows": [
{ "id": "c4f85105-6d07-44c0-bdb7-eb0baf7c6f62", "name": "returns.md" },
{ "id": "4f401205-5f76-46e1-a2b0-9adf66617d81", "name": "delivery.md" }
],
"pagination": {
"total": 2,
"last": "4f401205-5f76-46e1-a2b0-9adf66617d81"
}
}
pagination.total is the total number of rows in the data source.
Get a row
Returns one row, including its content.
GET /data-sources/row?dataSourceId={dataSourceId}&dataSourceRowId={dataSourceRowId}
curl --request GET \
--url 'https://app.chatthing.ai/api/public/channels/{channelId}/2.0/data-sources/row?dataSourceId={dataSourceId}&dataSourceRowId={dataSourceRowId}' \
--header 'X-API-Secret-Key: {secretKey}'
| Field | Type | Description |
|---|---|---|
id | string | The row's id |
type | string | Always MANUAL |
state | string | The row's sync state: not_synced, syncing, synced or sync_error |
lastSync | string or null | When the row last synced (ISO 8601) |
lastModified | string or null | When the row last changed (ISO 8601) |
createdAt | string | When the row was created (ISO 8601) |
content | string | The row's markdown content |
{
"success": true,
"dataSourceRow": {
"id": "c4f85105-6d07-44c0-bdb7-eb0baf7c6f62",
"type": "MANUAL",
"state": "synced",
"lastSync": "2026-01-27T14:39:03.014Z",
"lastModified": null,
"createdAt": "2026-01-27T14:30:01.216Z",
"content": "# Returns\nYou can return any item within 30 days of delivery."
}
}
Update rows
Replaces the name and content of one or more rows. Remember to sync afterwards.
PUT /data-sources/row
| Field | Type | Description |
|---|---|---|
dataSourceId | string | Required. The data source the rows belong to |
dataSourceRows | array | Required. The rows to update |
dataSourceRows[].id | string | The row's id |
dataSourceRows[].name | string | The row's new name |
dataSourceRows[].content | string | The row's new markdown content. This replaces the old content |
curl --request PUT \
--url https://app.chatthing.ai/api/public/channels/{channelId}/2.0/data-sources/row \
--header 'Content-Type: application/json' \
--header 'X-API-Secret-Key: {secretKey}' \
--data '{
"dataSourceId": "41559b21-b8ff-44c3-8658-1c9e34273070",
"dataSourceRows": [
{
"id": "c4f85105-6d07-44c0-bdb7-eb0baf7c6f62",
"name": "returns.md",
"content": "# Returns\nYou can return any item within 60 days of delivery."
}
]
}'
Returns { "success": true }. If any row id doesn't belong to the data source, the request fails with "success": false, and rows before it in the list may already have been updated.
Delete rows
Deletes one or more rows. Remember to sync afterwards so the bot forgets them.
DELETE /data-sources/row
| Field | Type | Description |
|---|---|---|
dataSourceId | string | Required. The data source the rows belong to |
dataSourceRows | array | Required. The rows to delete, each as { "id": "..." } |
curl --request DELETE \
--url https://app.chatthing.ai/api/public/channels/{channelId}/2.0/data-sources/row \
--header 'Content-Type: application/json' \
--header 'X-API-Secret-Key: {secretKey}' \
--data '{
"dataSourceId": "41559b21-b8ff-44c3-8658-1c9e34273070",
"dataSourceRows": [
{ "id": "c4f85105-6d07-44c0-bdb7-eb0baf7c6f62" },
{ "id": "4f401205-5f76-46e1-a2b0-9adf66617d81" }
]
}'
Returns { "success": true }. Row ids that don't exist are skipped.
Errors
| Code | When |
|---|---|
400 | A required field is missing, or an id isn't a valid UUID |
403 | The secret key doesn't match this channel |
404 | The channel isn't found or is turned off, or the data source doesn't exist, isn't manual or belongs to another bot |
429 | You've reached your plan's data source limit, the data source is already syncing, or your team is out of storage tokens |
500 | Saving a row failed. Try again |
Failed requests on most endpoints also return "success": false in the body. See API overview.
Related
- Add content manually
Write or paste content directly into Chat Thing to fill gaps in your knowledge, such as FAQs, policies or details that aren't published anywhere.
- API overview
Base URL, API keys, authentication headers, versions, error codes and usage limits for the Chat Thing REST API.
Last updated