One definition. One correct answer.
A contract is a named, governed interface to your data. Every dashboard, app and AI agent goes through it, so the answer is the same everywhere.
Four things, in one place.
A contract pulls together the sources it reads from, the dimensions callers can group by, the measures they can aggregate, and the access rules that scope what each caller sees.
The tables and models the contract reads from
What callers can group by, with the right time grains
What callers can aggregate, defined once
Row-level rules that scope what each caller sees
The full schema, including time grains, joins and filter operators, lives in the docs.
Checked twice.
Every contract is validated when you publish it, and again whenever someone queries it. If a query asks for something the contract can't safely answer, Gaur returns a clear error with a stable code your backend can branch on.
The Define pillar.
Connect your sources, define your contracts, serve them to every consumer. Contracts are the layer in the middle that every caller depends on, whether the caller is a dashboard, a customer-facing app, or an AI agent.
One contract, one query, one answer.
A contract on the left, a query against it on the right. The full schema and operators live in the docs.
{"name": "orders","sources": [{ "source_type": "table", "name": "orders", "alias": "o" }],"dimensions": {"plan": { "sql": "o.plan", "type": "string" }},"measures": {"revenue": { "sql": "SUM(o.total)", "type": "number" }},"rls": {"sql": "o.tenant_id = {{ tenant_id }}","parameters": { "tenant_id": "request.tenant_id" }}}
{"contract_name": "orders","query": {"measures": ["revenue"],"dimensions": [{ "name": "plan" }],"filters": [{ "field": "plan", "operator": "in","value": ["pro", "enterprise"] }]},"context": { "tenant_id": "acme" }}
For the full contract schema (joins, time grains, filter operators) and the response shape, see the docs.
Access rules live inside the contract.
Every contract carries the rules that decide which rows a caller is allowed to see. Gaur resolves those rules from two places before the query runs.
Bound to the API key.
Fixed values attached to the key when it's issued. Useful for things that never change for a given key, like the customer or workspace the key belongs to.
Set by your backend, per request.
Values your trusted backend sends with each call, like the tenant or user the request is being made for. Never trusted from the end user directly, because it decides what they can see.
The same access rules apply whether the caller is a dashboard, a customer-facing app, or an AI agent. No consumer can ask for rows it shouldn't see.
Same question. One way it goes right.
- Every consumer writes its own SQL against raw tables
- Metric definitions drift between dashboards, apps and agents
- Access checks rebuilt for each new caller, sometimes missed
- A bad join returns a number anyway; nobody is sure it's right
- One contract names the metric, the dimensions and the access rules
- Every consumer asks the same way and gets the same answer
- Row-level access enforced before the query runs
- A bad query returns a clear error, never a silently wrong number
Connect your sources, then serve the contracts everywhere.
You run your business.
We run your analytics.
Hop on a 30-minute call. We'll connect a sandbox to your warehouse and show you Gaur on your numbers.