version:0.1 doc:api-reference updated:2026

API Reference

The public API is a set of semantic discovery, builder, validation, and query routes. The stable contract is served under /api/v1/*.

CLI entrypoints
cli
uv run semantic-rails packages
uv run semantic-rails catalog --package jaffle_shop
uv run semantic-rails discover --package jaffle_shop --terms store_name
uv run semantic-rails inspect --package jaffle_shop --object-id measure.jaffle.order_count
uv run semantic-rails build-options --package jaffle_shop --query-json '@query.json' --step group_by
uv run semantic-rails valid-values --package jaffle_shop --dimension dimension.jaffle_store_name
uv run semantic-rails plan --package jaffle_shop --intent "new customer orders over time"
uv run semantic-rails validate --package jaffle_shop --query-json '@query.json'
uv run semantic-rails compile --package jaffle_shop --query-json '@query.json'
uv run semantic-rails query --package jaffle_shop --query-json '@query.json'
uv run semantic-rails segment-preview --package jaffle_shop --segment-id segment.jaffle.high_value_customers
uv run semantic-rails serve --package jaffle_shop --port 8081
uv run semantic-rails mcp stdio --package jaffle_shop
uv run semantic-rails mcp http --package jaffle_shop --host 127.0.0.1 --port 8091
uv run semantic-rails doctor --package jaffle_shop
HTTP routes

Use the /api/v1 prefix for integrations. Root paths and unversioned /api/* paths are not public routes. This table mirrors docs/QUERY_API.md, which is the canonical contract; request/response shapes and error codes live there.

Route Purpose
GET /api/v1/health Return service, package, schema, and warehouse metadata without warehouse I/O
GET /api/v1/ready Return package readiness; protected deep checks can verify warehouse connectivity
GET /api/v1/capabilities Return route index, supported API versions, aliases, warehouse capabilities, and package capabilities
GET /api/v1/catalog or POST /api/v1/catalog Browse catalog metadata, public objects, value domains, and capability flags
POST /api/v1/discover Rank semantic candidates from business terms; surfaces out_of_scope and low_relevance blocks inline
POST /api/v1/inspect Inspect an object card with usage and compatible next moves
POST /api/v1/build-options Get recommended, available, blocked, and patch-based builder choices
POST /api/v1/valid-values Get valid categorical values for a dimension
POST /api/v1/plan Produce validated best-query drafts and alternatives
POST /api/v1/validate Validate a query without executing it
POST /api/v1/compile Compile Query IR to rendered SQL and plan metadata without execution; the explain payload ships inside this response
POST /api/v1/query Execute a validated query against the configured warehouse
POST /api/v1/segment-validate Validate a package-authored segment membership query
POST /api/v1/segment-explain Return planner and SQL details for a segment membership query
POST /api/v1/segment-preview Preview membership rows for a package-authored segment
Response envelope

Most routes return ok, status, api_version, request_id, package_id, warnings, errors, recovery_hints, and timing_ms alongside route-specific payloads. Runtime calls may also include request_context when trusted semantic context headers or policy_context are supplied.

Request context and API key shim

Integrations can pass X-Request-ID, X-Semantic-Actor, X-Semantic-Tenant, X-Semantic-Project, X-Semantic-Roles, X-Semantic-Environment, and X-Semantic-Audience. Header context wins over body context and is merged into policy-aware routes. Set SEMANTIC_RAILS_API_KEYS or SEMANTIC_RAILS_API_KEY_FILE to require bearer/API-key auth. This is a runtime shim, not managed cloud tenant auth.

The local/customer-side runtime supports /api/v1/query. A hosted v0 service should stay discovery-, validate-, and compile-first until warehouse execution is productized separately.

Representative query shape
/api/v1/query request
{
  "query": {
    "version": 1,
    "select": [
      {
        "expression": { "measure": "measure.jaffle.revenue_usd" },
        "as": "revenue_usd"
      }
    ],
    "group_by": ["dimension.jaffle_store_name"],
    "time": {
      "temporal_role": "temporal_role.jaffle_order_time",
      "grain": "month"
    },
    "order_by": [{ "field": "revenue_usd", "direction": "DESC" }],
    "limit": 5
  }
}
Discovery-first request example
/api/v1/build-options request
{
  "query": {
    "version": 1,
    "select": [
      {
        "expression": { "measure": "measure.jaffle.revenue_usd" },
        "as": "revenue_usd"
      }
    ]
  },
  "focus_terms": "revenue by store",
  "focus_object_id": "measure.jaffle.revenue_usd",
  "step": "group_by",
  "limit": 6
}
Error model

For the authoring-side root cause of each error and the field that typically triggers it, see the error code → root cause table in the Authoring Reference.

Code Meaning Typical fix
AMBIGUOUS_ALIAS Human token maps to multiple semantic objects Use the returned candidates and pick a stable ID
AMBIGUOUS_PATH Multiple non-equivalent join paths remain valid Change the query shape or root so the planner has one safe path
FANOUT_UNSAFE Requested expansion would smear the result Choose a different grouping shape or supported rewrite
DUPLICATE_OUTPUT_ALIAS Projected output names collide Rename one of the projected aliases
CONVERSION_NOT_SUPPORTED Conversion shape falls outside the supported event-count model Use a supported conversion metric or split the ask into explicit legal queries
PREDICATE_SCOPE_UNSAFE Predicate context would smear values across the outer query Change the grouping, time grain, or predicate scope so the entity context is safe

Recovery hint contracts

Errors ship structured recovery_hints so callers can repair the query without re-reading docs. The contracts below are stable.

  • USE_OBJECT_SHAPE — emitted on INVALID_EXPRESSION_AST when a rolling/conversion window or prior_period offset is passed as an int instead of a {unit, value} object.
  • drop_time_start then widen_time_window — emitted on WINDOWED_TIME_FILTER_UNSUPPORTED in that order. drop_time_start is the always-safe first hint; widen_time_window ships a computed suggested_start.
  • USE_CANONICAL_KEY — emitted on INVALID_QUERY when an unknown top-level key is supplied. Maps common typos (filterswhere, dimensionsgroup_by, havingmetric_filters).
  • MOVE_BOUNDS_TO_TIME_TOP_LEVEL — fires when absolute bounds are misplaced under time.range: {start, end} instead of the top-level time object.
  • MOVE_DIMENSION_TO_GROUP_BY — fires when a {dimension: "..."} is placed in select[].expression rather than group_by.

Warning codes

Non-fatal signals returned in the response warnings[] array.

  • UNGRAINED_TIME_PROJECTIONtime.temporal_role is set without a grain, no group_by, and no inline window expression. Recovery: set a grain or drop temporal_role.
  • EXPRESSION_NORMALIZED_AWAY — the input kind did not survive normalization, or the as: alias is missing from the compiled output. Recovery: re-check the projection shape against compile's response.
  • SEMANTIC_CAVEAT_APPLIED — package-authored advisory context matched the compiled query. Caveats do not change SQL, rows, access, discovery, or policy behavior; agents should use them only when interpreting affected results.
  • SEMANTIC_CAVEATS_TRUNCATED — more advisory caveats matched than this verbosity returned.
New callers should follow discover -> inspect -> plan/build-options -> valid-values -> validate -> compile -> execute. plan returns a validated draft, but it is still not a substitute for inspecting the governed objects.

Continue to Getting Started if you want the exact serve-and-query steps, or Guardrails for runtime limits.