Product · Contracts

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.

01What's in a contract

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.

Sources

The tables and models the contract reads from

Dimensions

What callers can group by, with the right time grains

Measures

What callers can aggregate, defined once

Access rules

Row-level rules that scope what each caller sees

The full schema, including time grains, joins and filter operators, lives in the docs.

Validation

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.

Where it fits

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.

02A small example

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.

contract · orders
{
"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" }
}
}
POST /v1/api/<consumer>/query
{
"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.

03Row-level security

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.

Auth context

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.

Request context

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.

04Without contracts vs With Gaur

Same question. One way it goes right.

Without contracts
  • 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
With Gaur
  • 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
Ready when you are

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.