---
title: "query_database"
description: "Generated reference for the management action query_database: its description, access metadata, payload contract, and registration status."
slug: /reference/actions/query-database
order: 38
visibility: public
redirects_from: []
---

# query\_database

Generated automatically from the [published contract sources](/reference/about#contract-sources).

**Build metadata:** Registered in this build. Registration describes the default dispatcher in this build. It does not guarantee that a caller has the required credential or grant, that a tool is listed for that connection, or that the required service is configured.

A script calls this action over HTTPS at `POST https://turnzero.ai/api/v1/actions/query_database`, with a bearer credential and the action's payload as the JSON body. It also accepts `GET`.

## Contract description

Run one read\-only SQL statement against an application's database and read a bounded page of its rows, for example \`SELECT count\(\*\) FROM comments\`\. The statement starts with SELECT, WITH, VALUES, or TABLE\. It runs under the environment's own database role, in a read\-only transaction that is always rolled back, with a 10\-second timeout\. The answer carries \`columns\` and at most \`limit\` rows \(100 by default, 1,000 at most\) within 64 KiB of row data, each value as PostgreSQL's text or null\. Where \`truncated\` is \`rows\` or \`bytes\`, more remained: narrow with WHERE, LIMIT, or fewer columns\. A write is refused \`query\_not\_read\_only\`; any other failure is refused \`query\_failed\`\. \`environment\` is production where you give none\. The rest: \`read\_documentation\` page /cloud/reference/actions/query\-database/\.

## More about this action

The statement runs as a cursor's query on a new session of its own, under the environment's own database role, the role your application runs as\. The credential is read from the platform's custody and never returned\. The session takes one of the role's connections for at most 15 seconds, from the half of the role's limit kept for a deploy's overlap\. On a plan whose database connection quantity is 1, a query running while a deploy's new version starts can fail that version's health check\.

One statement goes per call, with no bound parameters\. A second statement after a semicolon, or a SELECT INTO, is refused \`query\_failed\` with SQLSTATE 42601\. EXPLAIN, SHOW, and COPY are not admitted; read a setting with \`current\_setting\('&lt;name&gt;'\)\`\. A write anywhere beneath the statement, in a function it calls or through nextval\(\), is refused \`query\_not\_read\_only\` and changes nothing\.

A statement past the 10\-second timeout is refused \`query\_failed\` with SQLSTATE 57014\. Where the role's connections are all in use the refusal carries 53300: call again once your application's pool frees one\. Every SQLSTATE the server returns comes back as \`sqlstate\` with the server's message, cut to 300 characters, at status 422\. A failure with no SQLSTATE, a connect timeout, TLS, or the network, is refused \`query\_failed\` at status 503 with a fixed sentence\.

Values come back as PostgreSQL writes them as text: numbers, dates, JSON, and \`bytea\` \(as \`\\x\` hex\) keep every digit\. A duplicate column name stays as it is\. The answer is cut at \`limit\` rows or at 64 KiB of row data, whichever comes first, and a read that would pass 256 KiB on the wire ends at once with the rows read so far\.

The \`environment\` argument: \`development\` reads the development database, which for an application with one environment is the database the local run uses\. An environment with no database is refused \`not\_found\`\.

Neither the statement nor any value it reads is written to the platform's log\.

## Access and action metadata

```json
{
  "name": "query_database",
  "resource": "environment",
  "tier": "observe",
  "product": "cloud",
  "summary": "Run one read-only SQL statement against an environment's database and read a bounded page of its rows. The statement's first word is SELECT, WITH, VALUES, or TABLE, and it runs as a cursor's query on a session of its own, under the environment's own database role, in a read-only transaction that is always rolled back, with a 10-second statement timeout. The answer carries each column's name, at most `limit` rows (100 where none is given, 1,000 at most) and 64 KiB of row data, each value in PostgreSQL's text form or null, `truncated` (`false`, `rows`, or `bytes`), and the time taken. A write is refused `query_not_read_only`; any other failure of the statement or its session is refused `query_failed`. Only the application's own account reaches the action. `environment` is production where none is given; `development` reads the development database, a one-environment application's local-run database included. Neither the statement nor any value it reads is written to the platform's log.",
  "annotations": {
    "readOnlyHint": true,
    "destructiveHint": false,
    "openWorldHint": false,
    "idempotentHint": true
  }
}
```

## MCP catalog entry

```json
{
  "name": "query_database",
  "title": "Query app database",
  "tier": "observe",
  "scenario": "CHI-L0-09",
  "summary": "Run one read-only SQL statement against an application's database and read a bounded page of its rows, for example `SELECT count(*) FROM comments`. The statement starts with SELECT, WITH, VALUES, or TABLE. It runs under the environment's own database role, in a read-only transaction that is always rolled back, with a 10-second timeout. The answer carries `columns` and at most `limit` rows (100 by default, 1,000 at most) within 64 KiB of row data, each value as PostgreSQL's text or null. Where `truncated` is `rows` or `bytes`, more remained: narrow with WHERE, LIMIT, or fewer columns. A write is refused `query_not_read_only`; any other failure is refused `query_failed`. `environment` is production where you give none. The rest: `read_documentation` page /cloud/reference/actions/query-database/.",
  "owners": [
    "DBS-L0-11",
    "PLD-L0-40",
    "MAPI-04"
  ]
}
```

## request

| JSON pointer | Description and constraints |
|---|---|
| "" \(root\) | Type: object<br>Required fields: \["application","sql"\] |
| /<wbr>properties/<wbr>application | The application id, from \`list\_applications\`\.<br><br>Type: string |
| /<wbr>properties/<wbr>sql | One SQL statement starting with SELECT, WITH, VALUES, or TABLE, at most 10,000 characters, with no bound parameters\. A write is refused \`query\_not\_read\_only\`; a second statement or a SELECT INTO is refused \`query\_failed\`\.<br><br>Type: string<br>Minimum length: 1<br>Maximum length: 10000 |
| /<wbr>properties/<wbr>environment | Production where absent; or \`development\`, the development database, which for an application with one environment is the one the local run uses\. Any other value is refused \`invalid\_request\`\.<br><br>Type: string<br>Pattern: ^\(development\|production\)$ |
| /<wbr>properties/<wbr>limit | How many rows at most, 1 to 1000; 100 where none is given\. The answer also stops at 64 KiB of row data, and \`truncated\` says which bound cut it\.<br><br>Type: integer<br>Minimum: 1<br>Maximum: 1000 |

## response

| JSON pointer | Description and constraints |
|---|---|
| "" \(root\) | One page of the statement's rows, read under the environment's own database role in a read\-only transaction that was rolled back\. Each value is PostgreSQL's text output, or null for SQL NULL\. \`truncated\` is \`rows\` where more rows remained past \`limit\`\. It is \`bytes\` where the 64 KiB of row data, or the read's 256 KiB on the wire, cut the page, and false where the page is whole\.<br><br>Type: object<br>Required fields: \["contract\_version","application","environment","columns","rows","row\_count","truncated","elapsed\_ms","summary"\] |
| /<wbr>properties/<wbr>contract\_version | Required value: 1 |
| /<wbr>properties/<wbr>reference | The short reference the platform recorded your call under: ten lowercase hexadecimal characters, the value in the call’s record row\. Quote it when you report the call\.<br><br>Type: string<br>Pattern: ^\[0\-9a\-f\]\{10\}$ |
| /<wbr>properties/<wbr>application | Type: string |
| /<wbr>properties/<wbr>environment | Type: string<br>Allowed values: \["development","production"\] |
| /<wbr>properties/<wbr>columns | Each column's name, in order, duplicates kept\.<br><br>Type: array |
| /<wbr>properties/<wbr>columns/<wbr>items | Type: string |
| /<wbr>properties/<wbr>rows | One array per row, its values in the order of \`columns\`\.<br><br>Type: array |
| /<wbr>properties/<wbr>rows/<wbr>items | Type: array |
| /<wbr>properties/<wbr>rows/<wbr>items/<wbr>items | Type: \["string","null"\] |
| /<wbr>properties/<wbr>row\_count | Type: integer<br>Minimum: 0 |
| /<wbr>properties/<wbr>truncated | Allowed values: \[false,"rows","bytes"\] |
| /<wbr>properties/<wbr>elapsed\_ms | Type: integer<br>Minimum: 0 |
| /<wbr>properties/<wbr>summary | Type: string |

## Complete payload contract

```json
{
  "request": {
    "type": "object",
    "required": [
      "application",
      "sql"
    ],
    "properties": {
      "application": {
        "type": "string",
        "description": "The application id, from `list_applications`."
      },
      "sql": {
        "type": "string",
        "minLength": 1,
        "maxLength": 10000,
        "description": "One SQL statement starting with SELECT, WITH, VALUES, or TABLE, at most 10,000 characters, with no bound parameters. A write is refused `query_not_read_only`; a second statement or a SELECT INTO is refused `query_failed`."
      },
      "environment": {
        "type": "string",
        "pattern": "^(development|production)$",
        "description": "Production where absent; or `development`, the development database, which for an application with one environment is the one the local run uses. Any other value is refused `invalid_request`."
      },
      "limit": {
        "type": "integer",
        "minimum": 1,
        "maximum": 1000,
        "description": "How many rows at most, 1 to 1000; 100 where none is given. The answer also stops at 64 KiB of row data, and `truncated` says which bound cut it."
      }
    }
  },
  "response": {
    "type": "object",
    "description": "One page of the statement's rows, read under the environment's own database role in a read-only transaction that was rolled back. Each value is PostgreSQL's text output, or null for SQL NULL. `truncated` is `rows` where more rows remained past `limit`. It is `bytes` where the 64 KiB of row data, or the read's 256 KiB on the wire, cut the page, and false where the page is whole.",
    "required": [
      "contract_version",
      "application",
      "environment",
      "columns",
      "rows",
      "row_count",
      "truncated",
      "elapsed_ms",
      "summary"
    ],
    "properties": {
      "contract_version": {
        "const": 1
      },
      "reference": {
        "type": "string",
        "pattern": "^[0-9a-f]{10}$",
        "description": "The short reference the platform recorded your call under: ten lowercase hexadecimal characters, the value in the call’s record row. Quote it when you report the call."
      },
      "application": {
        "type": "string"
      },
      "environment": {
        "type": "string",
        "enum": [
          "development",
          "production"
        ]
      },
      "columns": {
        "type": "array",
        "items": {
          "type": "string"
        },
        "description": "Each column's name, in order, duplicates kept."
      },
      "rows": {
        "type": "array",
        "items": {
          "type": "array",
          "items": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "description": "One array per row, its values in the order of `columns`."
      },
      "row_count": {
        "type": "integer",
        "minimum": 0
      },
      "truncated": {
        "enum": [
          false,
          "rows",
          "bytes"
        ]
      },
      "elapsed_ms": {
        "type": "integer",
        "minimum": 0
      },
      "summary": {
        "type": "string"
      }
    }
  }
}
```

## Shared contracts

- [Refusals](/reference/refusals): every refusal, by surface, with its cause and its remedy
- [schemas/wire\_error\.schema\.json](/reference/sources/schemas/wire-error-schema)
- [schemas/wire\_errors\.json](/reference/sources/schemas/wire-errors)
- [schemas/action\_payloads\.json](/reference/sources/schemas/action-payloads) (includes shared shapes)
- [management\_api\_contract\.md](/reference/sources/management-api-contract)
