Sources
Connect the systems you already run and keep a training file current from them.
A source connects a system you already run to Nucleus and keeps one training file current from it. Connect the source, map its records onto the chat format, set how often it refreshes, and every refresh writes a new file you can train and evaluate on without exporting anything by hand.
A source starts in connecting while its credentials are checked and its first records are
read, then settles at ready or error. Each refresh runs running and then succeeded or
failed. The file a refresh writes goes through the same line-by-line validation every
upload does, validating and then ready or error, so a bad export is caught before it
reaches a fine-tuning job. The source's file_id always
names the latest ready version. Poll Retrieve a source after connecting
and after a refresh.
type is the catalogue slug of the system you are connecting, for example salesforce,
postgresql, rest or csv. The catalogue of 95 sources is on the Data connectors page of
the Nucleus AI website.
Create a source
/v1/sourcesscope: trainConnects a source. The response comes back in connecting: credentials are checked and the
first refresh is scheduled in the background, so poll
Retrieve a source until the source reaches ready or error.
Body parameters
typestringrequiredThe catalogue slug of the system to connect, for example salesforce, postgresql, rest or
csv.
namestringrequiredNames the source, so you can tell your connections apart later.
credentialsobjectrequiredHow to sign in, which depends on the type. A database takes a connection_string. A REST source
takes a base_url and an api_key. A SaaS system that signs in with OAuth takes a
connection_id, minted in the dashboard when you authorise the connection. Credentials are
stored encrypted and are never returned by the API.
mappingobjectrequiredA template over the record's fields, mapping them onto the chat format. system, user and
assistant each take a string with {{ field }} placeholders that are filled from the record.
An optional filter expression selects which records are used.
refreshstringdefault: manualHow often the source refreshes: hourly, daily, weekly or manual. A manual source
refreshes only when you call Refresh a source.
purposestringWhat the files it writes are for: fine-tune, evaluation or retrieval. This is the
purpose the written file carries.
project_idstringAttach the source to a project. Keys bound to a different project cannot read it.
Connecting is not idempotent: retrying a timed-out request can create a duplicate source. Send
an Idempotency-Key header if your client retries automatically. See
Errors and retries.
curl https://api.nucleus-ai.io/v1/sources \
-H "Authorization: Bearer $NUCLEUS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "salesforce",
"name": "Support cases",
"credentials": { "connection_id": "conn_5Ht9w" },
"mapping": {
"system": "You are a support agent for Acme.",
"user": "{{ subject }}\n\n{{ description }}",
"assistant": "{{ resolution }}",
"filter": "status == '\''closed'\''"
},
"refresh": "daily",
"purpose": "fine-tune",
"project_id": "proj_1a2b"
}'{
"id": "src_7kQ2m",
"type": "salesforce",
"name": "Support cases",
"status": "connecting",
"purpose": "fine-tune",
"project_id": "proj_1a2b",
"refresh": "daily",
"file_id": null,
"rows": null
}List sources
/v1/sourcesscope: readReturns your connected sources, newest first, so you can see what is connected, what is still
connecting and what has failed across the account. See Pagination for how
limit, offset and the cursor object behave across list endpoints.
Query parameters
typestringFilter to one catalogue slug, for example postgresql.
statusstringFilter to one status: connecting, ready or error.
project_idstringOnly sources in this project.
limitintegerMaximum number of sources to return.
offsetintegerNumber of sources to skip from the start of the list.
curl "https://api.nucleus-ai.io/v1/sources?status=ready" \
-H "Authorization: Bearer $NUCLEUS_API_KEY"{
"data": [
{
"id": "src_7kQ2m",
"type": "salesforce",
"name": "Support cases",
"status": "ready",
"file_id": "file_9fQ2x",
"rows": 8412
}
],
"cursor": { "offset": 0, "total_count": 4 }
}Retrieve a source
/v1/sources/{source_id}scope: readReturns one source, including its mapping, its schedule and the file it currently writes to. This is the endpoint to poll after connecting and after a refresh.
file_id names the latest ready file written from this source: pass
it straight to a fine-tuning job as the training_file.
rows is the number of records that file holds. last_refreshed_at and next_refresh_at
report where the source sits in its schedule.
Path parameters
source_idstringrequiredID of the source, from the create response.
curl https://api.nucleus-ai.io/v1/sources/src_7kQ2m \
-H "Authorization: Bearer $NUCLEUS_API_KEY"{
"id": "src_7kQ2m",
"type": "salesforce",
"name": "Support cases",
"status": "ready",
"purpose": "fine-tune",
"project_id": "proj_1a2b",
"mapping": {
"system": "You are a support agent for Acme.",
"user": "{{ subject }}\n\n{{ description }}",
"assistant": "{{ resolution }}",
"filter": "status == 'closed'"
},
"refresh": "daily",
"file_id": "file_9fQ2x",
"rows": 8412,
"last_refreshed_at": "2026-09-07T04:00:12Z",
"next_refresh_at": "2026-09-08T04:00:00Z"
}Update a source
/v1/sources/{source_id}scope: trainChanges the mapping or the schedule of a connected source. Send only the fields you are
changing. A new mapping takes effect on the next refresh, so files already written are left
alone and file_id keeps naming the latest ready one until that refresh completes.
Path parameters
source_idstringrequiredID of the source to update.
Body parameters
mappingobjectA replacement template over the record's fields: system, user, assistant and an optional
filter.
refreshstringA new schedule: hourly, daily, weekly or manual.
curl -X PATCH https://api.nucleus-ai.io/v1/sources/src_7kQ2m \
-H "Authorization: Bearer $NUCLEUS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "refresh": "hourly" }'{
"id": "src_7kQ2m",
"status": "ready",
"refresh": "hourly",
"file_id": "file_9fQ2x",
"next_refresh_at": "2026-09-07T05:00:00Z"
}Refresh a source
/v1/sources/{source_id}/refreshscope: trainRuns a refresh now, without waiting for the schedule. Use it after changing a mapping, or to
pull a system that is set to manual.
The refresh runs asynchronously and returns 202 Accepted with the refresh record. It reports
running while it reads the records and writes the file, then succeeded or failed. The
file it writes starts in validating, so follow it through
List refreshes or Retrieve a dataset.
Path parameters
source_idstringrequiredID of the source to refresh.
curl -X POST https://api.nucleus-ai.io/v1/sources/src_7kQ2m/refresh \
-H "Authorization: Bearer $NUCLEUS_API_KEY"{
"id": "ref_3Wm8t",
"source_id": "src_7kQ2m",
"status": "running",
"file_id": null,
"started_at": "2026-09-07T11:22:41Z"
}List refreshes
/v1/sources/{source_id}/refreshesscope: readReturns the source's refresh history, newest first. Each entry carries the file it wrote, how many rows reached that file and how many lines were rejected, with the line number and reason for each rejection. This is where you see a mapping that has drifted from the records: a rejected count that climbs is a field that stopped arriving.
See Pagination for how limit, offset and the cursor object behave across
list endpoints.
Path parameters
source_idstringrequiredID of the source.
Query parameters
limitintegerMaximum number of refreshes to return.
offsetintegerNumber of refreshes to skip from the start of the list.
curl https://api.nucleus-ai.io/v1/sources/src_7kQ2m/refreshes \
-H "Authorization: Bearer $NUCLEUS_API_KEY"{
"data": [
{
"id": "ref_3Wm8t",
"status": "succeeded",
"file_id": "file_9fQ2x",
"rows": 8412,
"rejected": 2,
"rejections": [{ "line": 812, "reason": "assistant message empty" }]
},
{
"id": "ref_2Vk4s",
"status": "succeeded",
"file_id": "file_8dP1w",
"rows": 8237,
"rejected": 0,
"rejections": []
}
],
"cursor": { "offset": 0, "total_count": 12 }
}Disconnect a source
/v1/sources/{source_id}scope: trainDisconnects a source and discards its stored credentials. No further refresh runs and the schedule stops.
The files already written stay in Files and can still be trained on, until you delete them there. Disconnecting is therefore about stopping the connection, and Delete a dataset is what honours a data-erasure request.
Path parameters
source_idstringrequiredID of the source to disconnect.
curl -X DELETE https://api.nucleus-ai.io/v1/sources/src_7kQ2m \
-H "Authorization: Bearer $NUCLEUS_API_KEY"(empty body)