RememberStackremember.dev/docs

SQL queries

SQL queries let you ask questions the assured operations do not cover: counts, joins across claims and documents, timelines, anything you can express as one read-only SELECT. They run over the query space, memory_v1: a fixed set of prepared, read-only views and functions. Every statement is parsed and validated against the query space before it runs, and anything outside it — another schema, a function not on the list, a write — is rejected.

The query space itself (every view, column, function and limit) is described in Query space memory_v1. This page covers the seven routes.

All seven need the read scope. Base URL, authentication and error shapes are described in HTTP API conventions.

How results and errors come back

Statement routes (POST /query/sql, POST /query/sql/explain, POST /query/saved/{namespace}/{name}/run) return a QueryResult/v1 in every outcome, including a rejected or failed statement. A statement problem is not an HTTP error:

termination_reasonHTTP statusMeaning
completed200The statement ran. rows holds the result.
rejected200The statement was refused before it ran: it did not parse, used something outside the query space, had the wrong parameters, or hit an admission limit. error_code and error_message say which.
failed200The statement started and then failed: a timeout, a resource cap, an unavailable store. error_code and error_message say which.

So check termination_reason (or error_code) on every 200.

HTTP errors come from outside the statement: a saved query that cannot run, a malformed request body, an argument the server refuses before it builds a statement. Those use the object form of detail:

{"detail": {"code": "saved_query_not_found", "message": "no saved query named examples.nope"}}

The full list of error_code values, with the status each has when it is an HTTP error, is in Errors and status codes.

Every statement runs under the interactive limits:

LimitDefaultHard cap
Rows returned2001,000
Bytes returned1,048,5768,388,608
Statement timeout5,000 ms15,000 ms (5,000 ms when a graph function is used)
SQL text65,536 bytes
Parameters64, at most 262,144 bytes encoded
Concurrent statements2 per caller, 8 per deployment
Statement time30 s per caller per minute, 120 s per deployment per minute

A caller here is the kind of credential, not the person: every signed credential counts as one caller, the self-hosted shared secret as another, and an open self-hosted deployment as a third. The analytical tier listed in Query space memory_v1 is not reachable over HTTP.

POST /query/sql

Run one read-only SQL statement.

Request body

FieldTypeRequiredDefaultConstraints
sqlstringyesOne statement: SELECT, VALUES or WITH … SELECT. At most 65,536 bytes.
parametersarrayno[]Positional values for $1, $2, …. The count must equal the highest placeholder, and placeholders must be contiguous from $1.
max_rowsintegerno200At least 0; values above 1,000 are clamped to 1,000. 0 returns no rows.

Unknown fields are rejected. Cast parameters in the SQL when the type matters ($1::uuid, $2::timestamptz). Graph functions must take $1 as their first argument, and $1 must be this deployment's id; the deployment_id field of any earlier QueryResult or IngestedVersion gives it to you.

Response

200 with a QueryResult/v1. columns names each column with its SQL type; rows is an array of arrays, one value per column, in column order.

{
  "contract": "QueryResult/v1",
  "grade": "exploratory_tabular",
  "request_id": "2b0f…",
  "deployment_id": "5d0c7a52-3b1e-4f55-9a8e-0e6c1f2b7a10",
  "surface_manifest_hash": "d8be43966d90048ce3fc8ffe6dfdfc7943999fbf4f018ac2eb7998f2c995aae2",
  "query_space_schema": "memory_v1",
  "query_hash": "91c4…",
  "query_language": "sql",
  "saved_query": null,
  "referenced_views": ["documents_live"],
  "referenced_functions": ["count"],
  "source_grain_tags": ["document_lineage_live"],
  "columns": [
    {"name": "source_kind", "type": "text", "nullable": true},
    {"name": "documents", "type": "bigint", "nullable": true}
  ],
  "rows": [["notes", 42], ["transcripts", 17]],
  "returned_row_count": 2,
  "returned_byte_count": 34,
  "limits": {"row_cap": 200, "byte_cap": 1048576, "statement_timeout_ms": 5000, "analytical_tier": false},
  "truncated": false,
  "truncation_reason": null,
  "exact_total_known": false,
  "exact_total": null,
  "ordered_result": true,
  "empty_result": false,
  "negative_kind": null,
  "execution_started_at": "2026-09-23T10:00:00.120000+00:00",
  "evaluated_at": null,
  "pg_snapshot_at": "2026-09-23T10:00:00.121000+00:00",
  "elapsed_ms": 18.4,
  "termination_reason": "completed",
  "error_code": null,
  "error_message": null,
  "warnings": [],
  "semantic_invocations": [],
  "graph_invocations": []
}

Errors

StatusdetailCause
422validation listsql missing, parameters not an array, max_rows negative, or an unknown field.

Everything else about the statement is reported inside the 200 result.

Example

curl -s -X POST "$REMEMBER_API_URL/query/sql" \
  -H "Authorization: Bearer $REMEMBER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"sql": "SELECT source_kind, count(*) AS documents FROM documents_live GROUP BY source_kind ORDER BY documents DESC",
       "max_rows": 50}'
from remember import Client
 
memory = Client()
result = memory.open_query(
    "SELECT claim_text, asserted_at FROM claims_live"
    " WHERE claim_text ILIKE $1 ORDER BY asserted_at DESC",
    parameters=["%cutover%"],
    max_rows=20,
)
if result["termination_reason"] != "completed":
    raise RuntimeError(f"{result['error_code']}: {result['error_message']}")
for row in result.rows:
    print(row)

open_query returns a dictionary with .rows, .columns and .truncated shortcuts; query_sql(sql=..., parameters=..., max_rows=...) returns the plain dictionary. Each row is a list of values in column order.

POST /query/sql/explain

Validate one statement and return PostgreSQL's plan for it without running it.

Request body

FieldTypeRequiredDefaultConstraints
sqlstringyesAs for POST /query/sql.
parametersarrayno[]As for POST /query/sql.

Unknown fields (including max_rows) are rejected.

Response

200 with a QueryResult/v1 whose one row holds the plan as JSON (EXPLAIN (FORMAT JSON)). The same validation, parameter checks and admission limits as POST /query/sql apply. Semantic and lexical search functions are not called: the planner sees an empty relation of the same shape in their place.

Errors

As for POST /query/sql.

Example

curl -s -X POST "$REMEMBER_API_URL/query/sql/explain" \
  -H "Authorization: Bearer $REMEMBER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"sql": "SELECT count(*) FROM facts_current WHERE subject_entity_id = $1::uuid",
       "parameters": ["7c1e5a0e-2f4b-4d8a-9c3e-1b2a3c4d5e6f"]}'
plan = memory.explain_query(
    "SELECT count(*) FROM facts_current WHERE subject_entity_id = $1::uuid",
    parameters=[str(dana_id)],
)

GET /query/space

Describe the query space: its views and columns, functions, limits, grammar allowlists and, optionally, the shipped example queries. The answer comes from the checked-in manifest, never from your data, so it is identical for every deployment on the same release.

Parameters

NameInTypeRequiredDefaultConstraints
patternquerystringnoall viewsA shell-style glob matched against view names (facts_*).
include_examplesquerybooleannofalseAlso list the shipped example query names.

Response

200 with a JSON object:

FieldTypeContents
schemastringmemory_v1
schema_majorinteger1
surface_manifest_hashstringThe manifest hash every QueryResult also reports.
headlinestringA short orientation for agents.
retrieval_choicesarray of stringGuidance on choosing between operations, search and SQL.
honesty_warningsarray of stringWhat SQL results do not guarantee.
worked_examplesarray of objectWorked examples.
viewsarray of objectOne per matching view: name, grain, row_key (array), comment, columns (array of [name, type, nullable]).
functionsarray of stringThe 12 public function names.
limitsobjectinteractive and analytical limit sets.
core_operation_descriptorsobjectThe assured operations as the manifest records them.
function_signaturesobjectEvery function's arguments, result columns and caps.
sql_grammarobjectThe allowlists: functions, operators, cast_types, statement_node_classes, public_functions, srf_categories, srf_invocations_max_per_category, recursion_depth_max.
examplesarray of stringexamples.<name> for each shipped example when include_examples=true; empty otherwise.

Errors

StatusdetailCause
422validation listinclude_examples is not a boolean.

Example

curl -s "$REMEMBER_API_URL/query/space?pattern=facts_*" \
  -H "Authorization: Bearer $REMEMBER_API_KEY"
space = memory.describe_query_space(pattern="facts_*", include_examples=True)

GET /query/space/search

Search the query space's own text — view and column comments, function descriptions, operation descriptions, example purposes — for a phrase. It never searches your data.

Parameters

NameInTypeRequiredDefaultConstraints
queryquerystringyesAt least 1 character, and at least one word.
kqueryintegerno101 to 25.

Response

200 with an array of hits, best first:

FieldTypeContents
kindview | function | core_operation | exampleWhat the hit is.
namestringIts name (facts_current, semantic_claims, examples.claims_about).
scorenumberTerm-overlap score. A word in the name counts more than a word in the text.
purposestringThe comment or description that matched.
tagsarray of stringGrain and key names for views; channel and target for functions.

Errors

StatusdetailCause
422validation listquery missing or empty, or k out of range.
422{"code": "invalid_parameter", "message": "query must be non-empty"}query is only whitespace.

Example

curl -s -G "$REMEMBER_API_URL/query/space/search" \
  -H "Authorization: Bearer $REMEMBER_API_KEY" \
  --data-urlencode "query=current facts" \
  --data-urlencode "k=5"
hits = memory.search_query_space(query="current facts", k=5)

GET /query/saved

List saved queries. A saved query is a named, versioned SQL statement stored in the deployment. Every deployment ships with 18 in the examples namespace; see Query space memory_v1.

Parameters

NameInTypeRequiredDefaultConstraints
namespacequerystringnoall
statusquerystringnoactiveOne of draft, pending_revalidation, active, deprecated, disabled, broken.

Without status, only active versions of queries that are not disabled are listed. With status=draft, only the latest draft of each query is listed.

Response

200 with an array, ordered by namespace, name and version:

FieldType
query_idUUID
namespacestring
namestring
versioninteger
statusstring
descriptionstring or null
originhuman | agent | import | shipped_example
assurancecustomer_authored | customer_reviewed | shipped_example | null
query_hashstring
validated_surface_manifest_hashstring

Errors

StatusdetailCause
422validation liststatus is not one of the six values.

Example

curl -s "$REMEMBER_API_URL/query/saved?namespace=examples" \
  -H "Authorization: Bearer $REMEMBER_API_KEY"
for saved in memory.list_saved_queries(namespace="examples"):
    print(saved["name"], saved["version"], saved["description"])

GET /query/saved/{namespace}/{name}

Describe one version of a saved query, including its SQL.

Parameters

NameInTypeRequiredDefaultConstraints
namespacepathstringyes^[a-z][a-z0-9_]*$ (enforced by the remember client).
namepathstringyes^[a-z][a-z0-9_]*$ (enforced by the remember client).
versionqueryintegernothe active version

Response

200 with an object: every field of the list above plus sql, parameter_schema, declared_result_schema, declared_interpretation, query_space_major (memory_v1), default_limits (any of max_rows, statement_timeout_ms, max_bytes), validation_report, author_principal and approver_principal.

The shipped examples have an empty parameter_schema. Their parameters are the positional placeholders in their SQL, listed in Query space memory_v1.

Errors

Statusdetail codeCause
404saved_query_not_foundNo such query or version.
422validation listversion is not an integer.

Example

curl -s "$REMEMBER_API_URL/query/saved/examples/claims_about" \
  -H "Authorization: Bearer $REMEMBER_API_KEY"
detail = memory.describe_saved_query(namespace="examples", name="claims_about")
print(detail["sql"])

POST /query/saved/{namespace}/{name}/run

Run the active version of a saved query, or a named version, through the same validation and limits as POST /query/sql.

Parameters

NameInTypeRequiredConstraints
namespacepathstringyes^[a-z][a-z0-9_]*$ (enforced by the remember client).
namepathstringyes^[a-z][a-z0-9_]*$ (enforced by the remember client).

Request body

FieldTypeRequiredDefaultConstraints
versionintegernothe active versionAt least 1.
parametersarrayno[]Positional values for the query's placeholders.
max_rowsintegernothe query's stored default, else 200At least 0; clamped to 1,000.

Unknown fields are rejected. The query's stored default_limits apply (clamped to the interactive caps); your max_rows takes precedence over the stored one. Only a version whose status is exactly active runs.

Response

200 with a QueryResult/v1 whose saved_query field is {"query_id", "namespace", "name", "version", "query_hash"}, all strings. Statement problems are reported inside the result, as for POST /query/sql.

Errors

Statusdetail codeCause
404saved_query_not_foundNo such query or version.
409saved_query_disabledThe query is disabled, or the version is not active (a draft, deprecated or broken version).
409saved_query_revalidation_pendingThe query space changed since the version was validated, and it has not been revalidated.
422validation listMalformed body, version below 1, max_rows negative, or an unknown field.
500execution_errorThe deployment could not read its saved-query registry state.

Example

curl -s -X POST "$REMEMBER_API_URL/query/saved/examples/claims_about/run" \
  -H "Authorization: Bearer $REMEMBER_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"parameters\": [\"$DANA_ID\"], \"max_rows\": 20}"
result = memory.run_saved_query(
    namespace="examples", name="claims_about", parameters=[str(dana_id)], max_rows=20
)

Saved queries can only be created, activated or disabled through the deployment's own tooling; there is no HTTP route for it. See Saved queries.