Skip to main content
Every hook and component below must be used inside an <OxyAppProvider> — the provider resolves the app’s identity and shares a credentialed fetcher. Hooks fail loudly if called outside it.

Reading data

useQuery

Run inline SQL against the project’s warehouse and get typed rows back. SELECT / WITH only, capped at 10,000 rows.
  • params fill the SQL’s {{ params.X | sqlquote }} placeholders. They are sent alongside the SQL and substituted by the server, which writes a string as a literal the query’s database reads as that value. The escaping differs by engine — a backslash is an ordinary character to DuckDB and Postgres and an escape to ClickHouse, MySQL, Snowflake, Redshift and BigQuery — and only the server knows which engine the database is. A number or boolean is written as itself, and a missing or null param as NULL.
  • {{ params.X }} with no filter is written as it is, unquoted. Use it for a number, or an identifier you have validated yourself.
  • Quoting keeps a value a user typed from changing the query. It is not access control: the endpoint runs any read-only SQL a signed-in member sends it.
  • enabled: false defers the request until required inputs (e.g. a user-picked filter) are available.
  • The query re-runs whenever sql or the enabled params change.

useSemanticQuery

Query the semantic model by name instead of writing SQL. The server compiles it through airlayer and executes it on the same connector path as useQuery, so the result shape matches.
truncated is true when the result hit the server row cap. Pass { debug: true } as the second argument to also get the compiled sql back while developing.

Running agents

useAgentRun

Start an analytics agent run and stream its events over SSE. Use this when you want to build your own chat or results UI.
The agent supports human-in-the-loop suspension: when state is needs_clarification, call ask() again to answer and resume.

useProcedureRun

Start a long-running automation, poll its progress, and cancel it. Runs survive server restarts.

Rendering components

<OxyChat>

A drop-in chat UI over useAgentRun — the fastest way to embed conversational analytics in your app.

<OxyAnswer>

Renders an agent answer: markdown plus SQL artifacts and a link back to the full thread. Link URL schemes are allowlisted (it rejects javascript: and similar), so it’s safe to render model output.

Viewer identity and sign-out

@oxy-hq/sdk/shell exposes who is viewing the app and a way to end their session:
  • useIdentity() returns { id, name, email, picture, kind, reach, loading }. kind is "member" or "frontline"; email is null for a frontline worker. reach ({ everywhere, via, locations }) is for display only — a greeting, a default store — never for authorization; gate on ctx.user.reach inside a function.
  • signOut(fetcher, loginUrl?) ends the platform session and sends the viewer to the login page with the app as the place to return to. It throws if the server refuses, so the app never navigates away from a session that still exists.
Inside functions, @oxy-hq/sdk/ops turns ctx.user.reach into checks: requireReach(ctx, locationId) returns a 403 response (or null to proceed), predicate(reach, column, params) returns a SQL condition limiting rows to the caller’s locations, and isAdmin(ctx) / adminOnly(what) gate app-admin actions.

Errors

OxyApiError is the structured { message, code? } envelope the server returns. Hook error values are normal Error objects you can render directly; check for OxyApiError when you want to branch on code.

Metric-tree analyses

For programmatic access to the metric tree and anomaly inbox, the package root also exports MetricTreeClient and AnomaliesClient (plus their result types — ExplainResult, SensitivityResult, OpportunityResult, Anomaly, and more). These call the same endpoints the World Model graph uses, so you can build custom root-cause and sensitivity views.

Scenario forecasting

Three hooks — useBaseline, usePredict and useProjection — cover the what-if canvas, split by what each one costs: Fetch the two query-backed ones when the window changes; usePredict touches no database and is meant to re-run as the analyst drags a lever. It is also why the split exists: usePredict cannot measure a coefficient itself, so feed it useBaseline’s values and fitted — without them, a driver edge whose .view.yml declares no coefficient: propagates nothing and its downstream measures are simply absent from impacts. useProjection returns the baseline curve only. The scenario’s second curve is arithmetic over that curve and the usePredict result, composed on the client so a lever edit never costs a query. A series carrying a refusal — most often too little history to fit — is a stated absence, never a flat forward line.

Next steps

Oxygen Functions

Run server-side handlers with useFunction

The semantic model

Define the measures useSemanticQuery reads