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
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
{
"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
{
"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 onINVALID_EXPRESSION_ASTwhen a rolling/conversionwindowor prior_periodoffsetis passed as an int instead of a{unit, value}object. -
drop_time_startthenwiden_time_window— emitted onWINDOWED_TIME_FILTER_UNSUPPORTEDin that order.drop_time_startis the always-safe first hint;widen_time_windowships a computedsuggested_start. -
USE_CANONICAL_KEY— emitted onINVALID_QUERYwhen an unknown top-level key is supplied. Maps common typos (filters→where,dimensions→group_by,having→metric_filters). -
MOVE_BOUNDS_TO_TIME_TOP_LEVEL— fires when absolute bounds are misplaced undertime.range: {start, end}instead of the top-leveltimeobject. -
MOVE_DIMENSION_TO_GROUP_BY— fires when a{dimension: "..."}is placed inselect[].expressionrather thangroup_by.
Warning codes
Non-fatal signals returned in the response warnings[] array.
-
UNGRAINED_TIME_PROJECTION—time.temporal_roleis set without agrain, nogroup_by, and no inline window expression. Recovery: set agrainor droptemporal_role. -
EXPRESSION_NORMALIZED_AWAY— the inputkinddid not survive normalization, or theas:alias is missing from the compiled output. Recovery: re-check the projection shape againstcompile'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.
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.