Connections Query API
Execute SQL queries against database connections (MCP instances) configured in your organization. This is the API that powers live chart blocks in documents, but you can also call it directly.
Base URL
https://cc.teamday.ai/api/v1
Authentication
All requests require a Bearer token (Personal Access Token or Firebase JWT):
Authorization: Bearer td_your-token-here
Execute Query
Run a read-only SQL query against a named database connection.
POST /api/v1/connections/query
Request Body
{
"connection": "production-db",
"sql": "SELECT date, COUNT(*) as orders FROM orders GROUP BY date ORDER BY date DESC LIMIT 30",
"orgId": "your-org-id",
"spaceId": "optional-space-id",
"cache": "5m"
}
| Field | Type | Required | Description |
|---|---|---|---|
connection | string | yes | Name of the database connection as configured in Integrations |
sql | string | yes | SQL query to execute. Must be a SELECT query. |
orgId | string | yes | Organization ID |
spaceId | string | no | Space ID. If provided, verifies the connection is enabled in this Space. |
cache | string | no | Cache TTL: 30s, 5m, 1h, or none. Default: 5m. |
Response
{
"columns": ["date", "orders"],
"rows": [
["2026-04-07", 156],
["2026-04-06", 142],
["2026-04-05", 98]
],
"rowCount": 30,
"truncated": false,
"cached": false
}
| Field | Type | Description |
|---|---|---|
columns | string[] | Column names from the query result |
rows | any[][] | Array of row arrays. Each row contains values in the same order as columns. |
rowCount | number | Number of rows returned |
truncated | boolean | true if the result exceeded the 1,000 row limit |
cached | boolean | true if the result was served from cache |
Column Mapping for Charts
When using query results with chart blocks, columns map to chart axes:
SELECT category, revenue, cost
-- ^ ^ ^
-- labels series1 series2
-- (x-axis) (y-axis) (y-axis)
The first column becomes labels (x-axis). All subsequent columns become data series.
Error Responses
400 — Bad Request
Returned when required fields are missing or the query contains mutation statements.
{
"statusCode": 400,
"message": "Only SELECT queries are allowed"
}
Blocked statement types: INSERT, UPDATE, DELETE, DROP, ALTER, TRUNCATE, CREATE, GRANT, REVOKE.
404 — Connection Not Found
{
"statusCode": 404,
"message": "Connection \"my-db\" not found or inactive"
}
The connection name doesn’t match any active database MCP instance in the organization.
403 — Not Enabled in Space
{
"statusCode": 403,
"message": "Connection \"my-db\" is not enabled in this space"
}
Returned when spaceId is provided but the connection is not enabled in that Space.
422 — Query Failed
{
"statusCode": 422,
"message": "Query failed: relation \"nonexistent_table\" does not exist"
}
The SQL query was valid but failed at the database level. The error message from the database is included.
Caching
Results are cached in-memory on the server. The cache key is derived from the connection name and SQL query text.
| Cache Value | Duration | Use Case |
|---|---|---|
none | 0 | Real-time dashboards, debugging |
30s | 30 seconds | Frequently changing data |
5m | 5 minutes | Default — good balance of freshness and performance |
1h | 1 hour | Slow-changing data, heavy queries |
Cache is per-server and resets on deployment. There is no manual cache invalidation endpoint.
Security
Read-Only Enforcement
The API performs a prefix check on the SQL query to block mutation statements. This is a defense-in-depth measure. For production use, always configure your database connection with a read-only user.
Query Timeout
All queries have a 5-second timeout. Queries exceeding this limit are cancelled and return a 422 error.
Row Limit
Results are capped at 1,000 rows. If a query returns more, the response includes "truncated": true. Add a LIMIT clause to your SQL to control which rows are returned.
Connection Scope
Connections resolve by name within the organization. The API queries mcpInstances where:
organizationIdmatches the authenticated user’s orgnamematches theconnectionparameterisActiveistruecategoryis a SQL-capable database type
Examples
Basic Query
curl -X POST https://cc.teamday.ai/api/v1/connections/query \
-H "Authorization: Bearer td_your-token" \
-H "Content-Type: application/json" \
-d '{
"connection": "production-db",
"sql": "SELECT product_name, SUM(revenue) as revenue FROM orders GROUP BY 1 ORDER BY 2 DESC LIMIT 5",
"orgId": "your-org-id"
}'
Cached Query
curl -X POST https://cc.teamday.ai/api/v1/connections/query \
-H "Authorization: Bearer td_your-token" \
-H "Content-Type: application/json" \
-d '{
"connection": "analytics-db",
"sql": "SELECT DATE_TRUNC('\''month'\'', created_at) as month, COUNT(*) as signups FROM users GROUP BY 1 ORDER BY 1",
"orgId": "your-org-id",
"cache": "1h"
}'
Space-Scoped Query
curl -X POST https://cc.teamday.ai/api/v1/connections/query \
-H "Authorization: Bearer td_your-token" \
-H "Content-Type: application/json" \
-d '{
"connection": "production-db",
"sql": "SELECT status, COUNT(*) as count FROM orders GROUP BY status",
"orgId": "your-org-id",
"spaceId": "space-id"
}'