Creating Data Sources
There are three ways to create a data source:
| Route | Best for | What you can set |
|---|---|---|
| Resources → Data Sources → New data source | Internal queries, public APIs, event streams and webhooks | Name, connector type and config, event transport and accumulate settings, event emission |
| Create Data Source on a function | Wrapping a function you already have | Name, description, integrations, on-demand or interval refresh, static arguments; tests the source as it is created |
| An agent, or the API and platform tools | Everything else: authenticated REST and GraphQL, cron schedules, scope, transforms, templates | Every field |
Before you start
- For an authenticated API, store the credential in an integration first and note the integration's ID and the secret's name.
- When your trial has ended, the platform is read-only and the create buttons are disabled.
From the Data Sources page
- Go to Resources → Data Sources and choose New data source.
- Enter a Name.
- Choose a Connector type. The form below it changes to match.
- Fill in the configuration (see below).
- Optionally tick Emit a datasource.refreshed event on update.
- Choose Create.
The source is personal (only you can see it). REST, GraphQL, function, MCP and internal-query sources created here refresh on demand; event-stream sources refresh as events arrive and webhook sources as deliveries arrive. Choose the source's row to fetch and view its latest data.
Configuring a REST API source
The configuration form labels each field with its description. Object fields such as headers, query and body are entered as JSON.
| Field | Example |
|---|---|
| HTTP method | GET |
| Full URL to the API endpoint | https://api.example.com/v1/orders |
| HTTP headers | {"Accept": "application/json"} |
| Query string parameters | {"status": "open"} |
| JSONPath to the array of result rows | $.data.results |
| Pagination (JSON) | {"type": "cursor", "param": "cursor", "cursor_path": "$.next_cursor"} |
For a page-numbered API use {"type": "page", "param": "page", "start": 1}; for an API that returns a Link: <…>; rel="next" header use {"type": "link_header"}. The platform follows at most 50 pages.
The New Data Source dialog does not attach integrations. A {{integration:<id>:<KEY>}} placeholder is only resolved for an integration attached to the source, so a source created here with a placeholder fails with Unresolved integration placeholder until you attach the integration. Attach it through the API (PATCH /data-sources/{id} with {"integration_ids": [12]}, or the update_data_source tool), or ask an agent to create the source with the integration in the first place.
An authenticated REST config, once its integration is attached, looks like this:
{
"method": "GET",
"url": "https://api.example.com/v1/usage",
"headers": { "Authorization": "Bearer {{integration:12:EXAMPLE_API_KEY}}" },
"result_path": "$.data"
}
Configuring a GraphQL source
Enter the endpoint URL, the query string, and optionally variables, headers and a result path such as $.data.viewer.repositories.nodes. Query parameters supplied when the source is read are merged into the variables.
Configuring an internal query
Enter the query name, for example token_usage, and optional params as JSON, for example {"period": "30d"}. No integration is needed. See Data Sources for the full list of queries.
Configuring a function or MCP tool source
Prefer an internal query for the platform's own state and a REST or GraphQL source for an external API; use a function only when neither fits. Enter the function_id of an existing function and optional args. For an MCP source, also enter the tool name exposed by the MCP server. If the function needs credentials, use Create Data Source on the function instead, so you can attach integrations.
Configuring an event-stream source
- Choose an Event transport, or + New transport… to create one. The pencil icon edits the selected transport. See Event Sources.
- Choose how events Accumulate: Append, Upsert (by key) (then enter the Key field that identifies a record) or Replace.
- Set Max rows, the number of most recent rows to keep (default 200).
Configuring a webhook source
- Set Accumulate, Key field and Max rows as for an event stream, and create the source.
- Link a webhook to the source by setting the webhook's
data_source_idto the source's ID through the API. Each delivery the webhook accepts is then pushed into the source. The webhook form under Settings → Webhooks does not offer this link.
From a function
- Go to Resources → Functions and open the function.
- Choose Create Data Source.
- Check the Name (it defaults to the function's name followed by "Data Source") and add a Description (optional).
- Under Integrations (credentials), use Attach integrations to choose the integrations that supply the function's credentials. The function's own integrations are preselected.
- Choose a Refresh mode: On demand, or Scheduled (interval) with an Interval (seconds) (default 900, minimum 60).
- Optionally enter Static arguments (JSON, optional), for example
{ "lookback_days": 30 }. - Choose Create & Test.
The source is created and tested straight away. The dialog shows Test succeeded with the row count, latency and a sample, or Test failed with the error. Fix the function or its integrations and choose Test again, then Done.
With an agent or the API
Ask an agent that has the create_data_source function, or call the API, when you need a setting the pages do not offer:
POST /data-sources
{
"name": "Open pull requests",
"connector_type": "graphql",
"connector_config": {
"url": "https://api.github.com/graphql",
"headers": { "Authorization": "Bearer {{integration:7:GITHUB_TOKEN}}" },
"query": "query { viewer { pullRequests(states: OPEN, first: 50) { nodes { title url createdAt } } } }",
"result_path": "$.data.viewer.pullRequests.nodes"
},
"integration_ids": [7],
"refresh_mode": "scheduled",
"refresh_config": { "cron": "*/15 * * * *" },
"cache_ttl_seconds": 900,
"transform": { "title": "$.title", "url": "$.url", "opened": "$.createdAt" },
"scope": "project"
}
The platform rejects a source whose required config is missing (for example a REST source with no url) when you create it, so a broken source is caught before anything reads it.
Other operations available through the API and the platform tools:
| Operation | Endpoint | Tool |
|---|---|---|
| Dry-run a fetch: up to five sample rows, an inferred schema and latency. Writes nothing to the cache | POST /data-sources/{id}/test | test_data_source |
| Refresh now, bypassing the cache | POST /data-sources/{id}/refresh | refresh_data_source |
| Read the current result | GET /data-sources/{id}/data (add force_refresh=true to bypass the cache) | get_data_source_data |
Change any field, including enabled | PATCH /data-sources/{id} | update_data_source |
| List the connector types and their config schemas | GET /data-sources/connectors | list_data_source_connectors |
| List and instantiate templates | GET /data-source-templates, POST /data-source-templates/instantiate | list_data_source_templates, instantiate_data_source_template |
Check that it works
- On Resources → Data Sources, choose the source's row, or its View latest data icon. The dialog shows the status, row count and when it was last refreshed, with the rows below.
- Choose Refresh now in the dialog (or the refresh icon on the row) to fetch fresh data.
- If the status is
errorordegraded, the error says what failed. Common causes: an unattached integration, a wrongresult_path(the rows come back empty), or a host on a private network address (always blocked). For a function source, an error that starts withunsupported_platform_accessmeans the function reaches for something the sandbox does not provide (anotherapp.*module, the platform database, a platform environment variable) and says what to use instead; fix the function rather than retrying the source.
Disable or delete a source
- Disable a scheduled source through the API (
"enabled": false) to stop it refreshing. Disabling also deletes its cached data, and bound tiles show Paused. - Delete removes the source from the Data Sources page immediately, without a confirmation, even when dashboard tiles are bound to it. Those tiles then show Source deleted and fall back to their sample content. Through the API, deleting a bound source returns a conflict listing the bound tiles unless you pass
confirm=true.