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"
}
FieldTypeRequiredDescription
connectionstringyesName of the database connection as configured in Integrations
sqlstringyesSQL query to execute. Must be a SELECT query.
orgIdstringyesOrganization ID
spaceIdstringnoSpace ID. If provided, verifies the connection is enabled in this Space.
cachestringnoCache 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
}
FieldTypeDescription
columnsstring[]Column names from the query result
rowsany[][]Array of row arrays. Each row contains values in the same order as columns.
rowCountnumberNumber of rows returned
truncatedbooleantrue if the result exceeded the 1,000 row limit
cachedbooleantrue 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 ValueDurationUse Case
none0Real-time dashboards, debugging
30s30 secondsFrequently changing data
5m5 minutesDefault — good balance of freshness and performance
1h1 hourSlow-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:

  • organizationId matches the authenticated user’s org
  • name matches the connection parameter
  • isActive is true
  • category is 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"
  }'