API v2 Documentation (Beta)
Welcome to the Aito API v2 reference. v2 runs the new rep2 engine: the same
from/where you'd use to filter data is also the evidence for predict,
recommend, and relate — across linked tables, and explainable by
construction ($why returns the factor tree behind every probability). Tables are
schema-flexible: import JSON and the schema is inferred and evolves with your data.
New to Aito v2? Start with the Quickstart — an explained prediction in five minutes, no signup — then skim Core Concepts for the mental model and browse the Use Cases for end-to-end patterns. Prefer to poke around first? The Sandbox and Playground are live and read-only. Hitting an error? See Common Errors.
Note: v2 is in public beta — the API surface is stable enough to build on, unsupported operations fail loud with typed errors (never silently wrong results), and remaining gaps are listed openly in the operator reference. The current production default is the v1 API; v2 is its successor.
Guides:
- Quickstart — zero to an explained prediction in five minutes, against the live sandbox.
- Core Concepts — the mental model: evidence vs population, values vs features, links, calibration,
$why. - Which Query Type? — pick between
_query/_predict/_recommend/_estimate/_search/_relate. - CollectionDb — the v2 engine: collection vs table, JSON import, schema inference & evolution.
- Schema Design — types (incl.
Date, arrays), thequantityflag, links, nullable modelling. - Query Operator Reference — the full where / orderBy / select grammar (incl.
$searchFTS,$group, Date ops). - Inference — predict, recommend, relate,
$contextranking,$why. - Use Cases — GL coding, support triage, auto-tagging, market basket, full-text search, behavioral analytics — with runnable, verified queries.
- Environments & Auth — one database-level key, path-selected envs, branch/promote lifecycle.
- Clusters & Scaling — the object-store-backed multi-node model; read/write scaling, latency & staleness; set up with Aito support.
- Schema Migration & Maintenance — add/alter columns, declarative plan & apply, bulk backfill,
_modify, optimize. - v2 vs v1 — by the numbers — memory, disk, speed, accuracy, warm-up.
- Benchmarks — accuracy vs tuned ML baselines (Random Forest / LightGBM / FLAML / LLM+RAG) and honest write/query performance vs specialized engines.
- Sandbox — a live, read-only v2 env you can query right now (no signup).
- Playground — run v2 queries against that env in the browser, no setup.
- Common Errors — auth & env addressing, 501, empty search, flat predict, and frequent query errors.
Base URL:
https://your-instance.aito.ai/api/v2/
What's New in v2
The v2 API introduces several improvements:
- Query2 Engine: A new high-performance query engine with optimized execution
- Simplified Syntax: Cleaner JSON query format with intuitive field-value conditions
- Flexible CollectionDb: a schema-flexible columnar store —
POSTraw JSON and the schema is inferred, then evolves incrementally as new fields (or even new column types) appear - Deeper links: cross-table relationships now traverse multi-hop dotted paths (e.g.
processor.company.name), not just the single hop v1 allowed - Explainable predictions:
$whyreturns the factor tree behind every probability - Schema as code: declarative
_plan/_apply(liketerraform plan/apply), plus in-place column add and type/analyzer alter (re-indexed), beyond v1's add-only - Richer types: Int / Long / Decimal / String / Boolean / Text / Json + arrays + honest nullable columns (a real defined-mask, not sentinels), plus a numeric
quantityflag that bins prices/ages so neighbours share evidence - More powerful text: per-language analyzers, phrase / positional matching, and
$hasmembership into array / multi-value columns - Lean memory footprint: columnar storage with memory-mapped, zero-allocation reads — the linkage format is ~2.6× smaller and state rebuilds use ~6× less memory — keeping RAM low as data grows
- Scales to millions of rows: predict latency stays low and roughly flat into the 10M-row range (see the internal InvoicePerf benchmarks)
- Horizontal scaling: run Aito as a multi-node cluster for read scale-out, higher write throughput, high availability, and object-store-backed durability (see the Clusters & Scaling guide)
- Unified Interface: Consistent API across different table types
Key Differences from v1:
| Feature | v1 | v2 |
|---|---|---|
| Query endpoint | /api/v1/_search | /api/v2/_query |
| Filter syntax | Operator-based | Field-value based |
| Table creation | Schema-focused | Type-aware |
| Batch insert | Separate endpoint | Unified /batch suffix |
v2 Query Example:
{
"from": "products",
"where": { "tags": { "$has": "fresh" } },
"orderBy": "price",
"limit": 10
}
Behavioral differences from v1
Some v1-vs-v2 output differences are by design — improvements the rep2 engine was built for, not regressions. When comparing results across versions, check here before filing a bug:
- Predict on text fields aggregates duplicate values. v1 returns duplicate
text values as separate hits (
"hello","hello", … each with its own probability); v2 returns one"hello"candidate with the aggregated probability. Candidates are values, not rows — the v2 behavior is the correct one. - The prediction target determines exclusiveness.
predict Xscores distinct whole values exclusively;predict X.$featurescores each member value independently (multi-label). The v1-styleexclusivenessflag is deprecated: if given, it must agree with the target or the request is rejected. - Unsupported operations fail loud. Where v1 sometimes degraded silently
(empty results, substituted defaults), v2 returns a typed
4xx/501with the reason. A200with an empty result always means "no match", never "unsupported and swallowed".
Non-exclusive per-member predict (v2):
{
"from": "products",
"where": { "category": "dairy" },
"predict": "tags.$feature",
"limit": 10
}
Query operator reference
The complete where operator vocabulary ($match, $search, $has, ranges,
$and/$or, $nearest, $refs, …), plus orderBy, select, let, the
top-level query types and config, lives in one place — the
Query Operator Reference (v2).
Full-text search + explainable predict:
{
"from": "products",
"where": {
"description": { "$search": "(wireless OR bluetooth) mouse -refurbished" }
},
"predict": "category",
"select": ["$value", "$p", "$why"]
}
Authentication
The v2 API uses the same key-based authentication as v1 — include your API key in
the x-api-key header. Keys are database-level: one read key reads every
environment, one read-write key writes every environment; there is no
env-scoped key. Environments are selected purely by URL path (not a header or
body field), so a 401/403 that reads fine on master is almost always an
addressing mistake. See Environments & Auth for the full
model and Common Errors when a call is rejected.
curl -X POST \
'https://your-instance.aito.ai/api/v2/_query' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"from": "products", "limit": 5}'
Environments
Environments are shared across API versions — the same branch/promote mechanism
serves v1 and v2. Branch the database into named copies for sandboxes, per-user
demos, or atomic side-prepared releases. Switch envs purely via URL —
/env/{name}/api/v2/... resolves to the named env, /api/v2/... resolves to master.
Manage them under /api/v2/_envs: GET to list, POST to branch ({ "name": "sandbox", "basedOn": "master" }), POST /api/v2/_envs/{name}/promote to swap into
master, DELETE /api/v2/_envs/{name} to drop.
See Environments & Auth for the full URL pattern, promote semantics, naming rules, and the authorization model.
v2 env-scoped query:
curl -X POST \
'https://your-instance.aito.ai/env/sandbox/api/v2/_query' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"from": "products", "limit": 5}'
Query (v2)
POST/api/v2/_query
Query rows using the v2 Query2 engine.
The v2 Query API provides a simplified, high-performance query interface powered by the new Query2 engine. It supports filtering, sorting, and column selection.
The same from/where body also drives prediction, recommendation and relation analysis (predict / recommend / relate), and the full where operator vocabulary ($match, $search, $has, ranges, $and/$or, $nearest, $refs, …) plus orderBy, select and let are documented in the Query Operator Reference (v2). The /api/v2/_predict, /_search, /_recommend and /_relate endpoints are aliases that accept the same body.
Query Format
The query is specified as a JSON object with the following fields:
| Field | Type | Description |
|---|---|---|
from | string | Required. The table name to query. |
where | object | Optional. Filter conditions as field-value pairs. Supports dotted paths for linked fields (e.g., {"processor.role": "Manager"}). |
orderBy | string | Optional. Column name to sort by. Supports linked field paths. |
select | array | Optional. List of column names to return. Supports linked field paths. |
limit | number | Optional. Maximum number of rows to return (default: 10). |
offset | number | Optional. Number of rows to skip (default: 0). |
Response Format
The response includes:
| Field | Type | Description |
|---|---|---|
offset | number | The offset used in the query. |
total | number | Total number of matching rows. |
hits | array | Array of matching documents. |
Examples
Basic query
Retrieve rows from a table with a limit.
You can copy-paste the example curl command to your terminal.
Request body
{
"from": "products",
"limit": 3
}Request
curl -X POST \
https://shared.aito.ai/db/aito-demo/api/v2/_query \
-H 'content-type: application/json' \
-H 'x-api-key: yg4rTlXkqDzm4y8gPeY75HCKaNwfbTQ2si64ONTi' \
-d '
{
"from": "products",
"limit": 3
}'Response
{
"offset": 0,
"total": 42,
"hits": [
{
"googleImpressions": 100,
"name": "Pirkka banana",
"tags": [
"fresh",
"fruit",
"pirkka"
],
"price": 0.166,
"id": "2000818700008",
"cost": 0.1,
"category": "100",
"googleClicks": 12
},
{
"googleImpressions": 100,
"name": "Cucumber Finland",
"tags": [
"fresh",
"vegetable"
],
"price": 0.9765,
"id": "2000604700007",
"cost": 0.586,
"category": "100",
"googleClicks": 11
},
{
"googleImpressions": 100,
"name": "Pirkka bio cherry tomatoes 250g international 1lk",
"tags": [
"fresh",
"vegetable",
"pirkka",
"tomato"
],
"price": 1.29,
"id": "6410405060457",
"cost": 0.774,
"category": "100",
"googleClicks": 10
}
]
}Query with filter
Filter rows using the where clause. Here we keep only products
tagged fresh — $has matches membership in an array column.
Request body
{
"from": "products",
"where": {
"tags": {
"$has": "fresh"
}
},
"limit": 5
}Request
curl -X POST \
https://shared.aito.ai/db/aito-demo/api/v2/_query \
-H 'content-type: application/json' \
-H 'x-api-key: yg4rTlXkqDzm4y8gPeY75HCKaNwfbTQ2si64ONTi' \
-d '
{
"from": "products",
"where": {
"tags": { "$has": "fresh" }
},
"limit": 5
}'Response
{
"offset": 0,
"total": 5,
"hits": [
{
"googleImpressions": 100,
"name": "Pirkka banana",
"tags": [
"fresh",
"fruit",
"pirkka"
],
"price": 0.166,
"id": "2000818700008",
"cost": 0.1,
"category": "100",
"googleClicks": 12
},
{
"googleImpressions": 100,
"name": "Cucumber Finland",
"tags": [
"fresh",
"vegetable"
],
"price": 0.9765,
"id": "2000604700007",
"cost": 0.586,
"category": "100",
"googleClicks": 11
},
{
"googleImpressions": 100,
"name": "Pirkka bio cherry tomatoes 250g international 1lk",
"tags": [
"fresh",
"vegetable",
"pirkka",
"tomato"
],
"price": 1.29,
"id": "6410405060457",
"cost": 0.774,
"category": "100",
"googleClicks": 10
},
{
"googleImpressions": 100,
"name": "Chiquita banana",
"tags": [
"fresh",
"fruit"
],
"price": 0.28054,
"id": "2000503600002",
"cost": 0.168,
"category": "100",
"googleClicks": 9
},
{
"googleImpressions": 100,
"name": "Pirkka iceberg salad Finland 100g 1st class",
"tags": [
"fresh",
"vegetable",
"pirkka",
"salad"
],
"price": 1.29,
"id": "6410405093677",
"cost": 0.774,
"category": "100",
"googleClicks": 9
}
]
}Query with sorting
Sort results using the orderBy clause.
Request body
{
"from": "products",
"orderBy": "name",
"limit": 5
}Request
curl -X POST \
https://shared.aito.ai/db/aito-demo/api/v2/_query \
-H 'content-type: application/json' \
-H 'x-api-key: yg4rTlXkqDzm4y8gPeY75HCKaNwfbTQ2si64ONTi' \
-d '
{
"from": "products",
"orderBy": "name",
"limit": 5
}'Response
{
"offset": 0,
"total": 42,
"hits": [
{
"googleImpressions": 100,
"name": "Atria Gotler ham sausage 300g",
"$p": "Atria Gotler ham sausage 300g",
"tags": [
"meat",
"sausage",
"with-bread"
],
"price": 1.75,
"id": "6407870071224",
"cost": 1.225,
"category": "102",
"googleClicks": 8
},
{
"googleImpressions": 100,
"name": "Atria lauantaimakkara bread sausage 225g",
"$p": "Atria lauantaimakkara bread sausage 225g",
"tags": [
"meat",
"sausage",
"with-bread"
],
"price": 0.89,
"id": "6407870070333",
"cost": 0.623,
"category": "102",
"googleClicks": 9
},
{
"googleImpressions": 100,
"name": "Breadded chicke nuggets 200g",
"$p": "Breadded chicke nuggets 200g",
"tags": [
"meat",
"food",
"protein"
],
"price": 1.49,
"id": "6409100024628",
"cost": 1.043,
"category": "103",
"googleClicks": 8
},
{
"googleImpressions": 100,
"name": "Chiquita banana",
"$p": "Chiquita banana",
"tags": [
"fresh",
"fruit"
],
"price": 0.28054,
"id": "2000503600002",
"cost": 0.168,
"category": "100",
"googleClicks": 9
},
{
"googleImpressions": 100,
"name": "Coca-Cola 1,5l soft drink",
"$p": "Coca-Cola 1,5l soft drink",
"tags": [
"drink",
"soda"
],
"price": 2.49,
"id": "6415600501811",
"cost": 1.743,
"category": "108",
"googleClicks": 8
}
]
}Query with select
Select specific columns to return.
Request body
{
"from": "products",
"select": [
"name",
"price"
],
"limit": 5
}Request
curl -X POST \
https://shared.aito.ai/db/aito-demo/api/v2/_query \
-H 'content-type: application/json' \
-H 'x-api-key: yg4rTlXkqDzm4y8gPeY75HCKaNwfbTQ2si64ONTi' \
-d '
{
"from": "products",
"select": ["name", "price"],
"limit": 5
}'Response
{
"offset": 0,
"total": 42,
"hits": [
{
"name": "Pirkka banana",
"price": 0.166
},
{
"name": "Cucumber Finland",
"price": 0.9765
},
{
"name": "Pirkka bio cherry tomatoes 250g international 1lk",
"price": 1.29
},
{
"name": "Chiquita banana",
"price": 0.28054
},
{
"name": "Pirkka iceberg salad Finland 100g 1st class",
"price": 1.29
}
]
}Search (v2)
POST/api/v2/_search
Full-text and structured search over a v2 collection.
_search is an alias of _query — the v2 Query2 engine dispatches by the body fields, so a body with a $match where and no inference field behaves as a search. Use whichever name reads best for your call site.
Body
| Field | Type | Description |
|---|---|---|
from | string | Required. The collection to search. |
where | object | Filter conditions. $match does full-text matching on Text columns; exact values match String/Int/Decimal. |
orderBy | string / object | Optional sort — a column, or an operator such as $similarity. |
select | array | Optional list of columns to return. |
limit / offset | number | Paging (default limit 10). |
See the Query Operator Reference (v2).
Predict (v2)
POST/api/v2/_predict
Predict the value of a field from the evidence in where.
The from/where you would use to filter is also the evidence for the prediction — you don't train a model per question, you ask the collection. Each candidate value comes back with a calibrated probability $p.
Body
| Field | Type | Description |
|---|---|---|
from | string | Required. The collection to predict from. |
where | object | The evidence — field conditions, dotted link paths (processor.role), $match, $numeric, etc. |
predict | string | Required. The field to predict. |
select | array | Columns/metadata to return per candidate — $p, $value, and $why. |
config | object | Inference tuning. config.ai defaults to group (group formation); see Inference (v2). |
limit | number | Max candidates (default 10). |
To make a prediction better — links, supporting fields, $numeric, scoping — see Improving Inference (v2).
Predict a product category
Given a product name, predict its category. Each candidate comes back with a calibrated $p.
You can copy-paste the example curl command to your terminal.
Request body
{
"from": "products",
"where": {
"name": "rye bread"
},
"predict": "category"
}Request
curl -X POST \
https://shared.aito.ai/db/aito-demo/api/v2/_predict \
-H 'content-type: application/json' \
-H 'x-api-key: yg4rTlXkqDzm4y8gPeY75HCKaNwfbTQ2si64ONTi' \
-d '
{
"from": "products",
"where": { "name": "rye bread" },
"predict": "category"
}'Response
{
"offset": 0,
"total": 11,
"hits": [
{
"$p": 0.9729371230219164,
"$value": "101"
},
{
"$p": 0.0033219857319227723,
"$value": "102"
},
{
"$p": 0.0033219857319227723,
"$value": "103"
},
{
"$p": 0.003305953340962574,
"$value": "100"
},
{
"$p": 0.003305953340962574,
"$value": "104"
},
{
"$p": 0.003305953340962574,
"$value": "108"
},
{
"$p": 0.0027701548656267837,
"$value": "109"
},
{
"$p": 0.002227782360484964,
"$value": "111"
},
{
"$p": 0.002227782360484964,
"$value": "115"
},
{
"$p": 0.00163766295237681,
"$value": "106"
}
]
}Explain the prediction ($why)
Ask Aito to return the factor tree behind each score. With group formation on by
default, correlated features appear as a single $group factor.
Request body
{
"from": "products",
"where": {
"tags": {
"$has": "fresh"
}
},
"predict": "category",
"select": [
"$p",
"$value",
"$why"
]
}Request
curl -X POST \
https://shared.aito.ai/db/aito-demo/api/v2/_predict \
-H 'content-type: application/json' \
-H 'x-api-key: yg4rTlXkqDzm4y8gPeY75HCKaNwfbTQ2si64ONTi' \
-d '
{
"from": "products",
"where": {
"tags": { "$has": "fresh" }
},
"predict": "category",
"select": ["$p", "$value", "$why"]
}'Response
{
"offset": 0,
"total": 11,
"hits": [
{
"$p": 0.9761207744130768,
"$value": "100",
"$why": {
"type": "product",
"factors": [
{
"type": "baseP",
"value": 0.11320754716981132,
"proposition": {
"category": {
"$has": "100"
}
}
},
{
"type": "product",
"factors": [
{
"type": "normalizer",
"name": "exclusiveness",
"value": 1.1015558096595366
},
{
"type": "normalizer",
"name": "trueFalseExclusiveness",
"value": 1.059574630877425
},
{
"type": "calibration",
"name": "support-tempering(auto)",
"value": 0.9058785275709119
}
]
},
{
"type": "relatedPropositionLift",
"proposition": {
"tags": {
"$has": "fresh"
}
},
"value": 8.154928406423556
}
]
}
},
{
"$p": 0.0028675737328865704,
"$value": "101",
"$why": {
"type": "product",
"factors": [
{
"type": "baseP",
"value": 0.11320754716981132,
"proposition": {
"category": {
"$has": "101"
}
}
},
{
"type": "product",
"factors": [
{
"type": "normalizer",
"name": "exclusiveness",
"value": 1.1015558096595366
},
{
"type": "normalizer",
"name": "trueFalseExclusiveness",
"value": 0.9923825725045433
},
{
"type": "calibration",
"name": "support-tempering(auto)",
"value": 0.998676639829455
}
]
},
{
"type": "relatedPropositionLift",
"proposition": {
"tags": {
"$has": "fresh"
}
},
"value": 0.02320217441900012
}
]
}
},
{
"$p": 0.0028675737328865704,
"$value": "102",
"$why": {
"type": "product",
"factors": [
{
"type": "baseP",
"value": 0.11320754716981132,
"proposition": {
"category": {
"$has": "102"
}
}
},
{
"type": "product",
"factors": [
{
"type": "normalizer",
"name": "exclusiveness",
"value": 1.1015558096595366
},
{
"type": "normalizer",
"name": "trueFalseExclusiveness",
"value": 0.9923825725045433
},
{
"type": "calibration",
"name": "support-tempering(auto)",
"value": 0.998676639829455
}
]
},
{
"type": "relatedPropositionLift",
"proposition": {
"tags": {
"$has": "fresh"
}
},
"value": 0.02320217441900012
}
]
}
},
{
"$p": 0.0028675737328865704,
"$value": "103",
"$why": {
"type": "product",
"factors": [
{
"type": "baseP",
"value": 0.11320754716981132,
"proposition": {
"category": {
"$has": "103"
}
}
},
{
"type": "product",
"factors": [
{
"type": "normalizer",
"name": "exclusiveness",
"value": 1.1015558096595366
},
{
"type": "normalizer",
"name": "trueFalseExclusiveness",
"value": 0.9923825725045433
},
{
"type": "calibration",
"name": "support-tempering(auto)",
"value": 0.998676639829455
}
]
},
{
"type": "relatedPropositionLift",
"proposition": {
"tags": {
"$has": "fresh"
}
},
"value": 0.02320217441900012
}
]
}
},
{
"$p": 0.0028675737328865704,
"$value": "104",
"$why": {
"type": "product",
"factors": [
{
"type": "baseP",
"value": 0.11320754716981132,
"proposition": {
"category": {
"$has": "104"
}
}
},
{
"type": "product",
"factors": [
{
"type": "normalizer",
"name": "exclusiveness",
"value": 1.1015558096595366
},
{
"type": "normalizer",
"name": "trueFalseExclusiveness",
"value": 0.9923825725045433
},
{
"type": "calibration",
"name": "support-tempering(auto)",
"value": 0.998676639829455
}
]
},
{
"type": "relatedPropositionLift",
"proposition": {
"tags": {
"$has": "fresh"
}
},
"value": 0.02320217441900012
}
]
}
},
{
"$p": 0.0028675737328865704,
"$value": "108",
"$why": {
"type": "product",
"factors": [
{
"type": "baseP",
"value": 0.11320754716981132,
"proposition": {
"category": {
"$has": "108"
}
}
},
{
"type": "product",
"factors": [
{
"type": "normalizer",
"name": "exclusiveness",
"value": 1.1015558096595366
},
{
"type": "normalizer",
"name": "trueFalseExclusiveness",
"value": 0.9923825725045433
},
{
"type": "calibration",
"name": "support-tempering(auto)",
"value": 0.998676639829455
}
]
},
{
"type": "relatedPropositionLift",
"proposition": {
"tags": {
"$has": "fresh"
}
},
"value": 0.02320217441900012
}
]
}
},
{
"$p": 0.0024026894935425002,
"$value": "109",
"$why": {
"type": "product",
"factors": [
{
"type": "baseP",
"value": 0.09433962264150944,
"proposition": {
"category": {
"$has": "109"
}
}
},
{
"type": "product",
"factors": [
{
"type": "normalizer",
"name": "exclusiveness",
"value": 1.1015558096595366
},
{
"type": "normalizer",
"name": "trueFalseExclusiveness",
"value": 0.9977998416652373
},
{
"type": "calibration",
"name": "support-tempering(auto)",
"value": 0.998676639829455
}
]
},
{
"type": "relatedPropositionLift",
"proposition": {
"tags": {
"$has": "fresh"
}
},
"value": 0.02320217441900012
}
]
}
},
{
"$p": 0.0019321588950813253,
"$value": "111",
"$why": {
"type": "product",
"factors": [
{
"type": "baseP",
"value": 0.07547169811320754,
"proposition": {
"category": {
"$has": "111"
}
}
},
{
"type": "product",
"factors": [
{
"type": "normalizer",
"name": "exclusiveness",
"value": 1.1015558096595366
},
{
"type": "normalizer",
"name": "trueFalseExclusiveness",
"value": 1.0029946882263068
},
{
"type": "calibration",
"name": "support-tempering(auto)",
"value": 0.998676639829455
}
]
},
{
"type": "relatedPropositionLift",
"proposition": {
"tags": {
"$has": "fresh"
}
},
"value": 0.023202174419000122
}
]
}
},
{
"$p": 0.0019321588950813253,
"$value": "115",
"$why": {
"type": "product",
"factors": [
{
"type": "baseP",
"value": 0.07547169811320754,
"proposition": {
"category": {
"$has": "115"
}
}
},
{
"type": "product",
"factors": [
{
"type": "normalizer",
"name": "exclusiveness",
"value": 1.1015558096595366
},
{
"type": "normalizer",
"name": "trueFalseExclusiveness",
"value": 1.0029946882263068
},
{
"type": "calibration",
"name": "support-tempering(auto)",
"value": 0.998676639829455
}
]
},
{
"type": "relatedPropositionLift",
"proposition": {
"tags": {
"$has": "fresh"
}
},
"value": 0.023202174419000122
}
]
}
},
{
"$p": 0.001637174819392551,
"$value": "106",
"$why": {
"type": "product",
"factors": [
{
"type": "baseP",
"value": 0.03773584905660377,
"proposition": {
"category": {
"$has": "106"
}
}
},
{
"type": "product",
"factors": [
{
"type": "normalizer",
"name": "exclusiveness",
"value": 1.1015558096595366
},
{
"type": "normalizer",
"name": "trueFalseExclusiveness",
"value": 1.0127697460141756
},
{
"type": "calibration",
"name": "support-tempering(auto)",
"value": 1.6760810594421103
}
]
},
{
"type": "relatedPropositionLift",
"proposition": {
"tags": {
"$has": "fresh"
}
},
"value": 0.02320217441900012
}
]
}
}
]
}Recommend (v2)
POST/api/v2/_recommend
Recommend the values of a field that best advance a goal.
Recommend is a predict with an objective: instead of "what is most likely", it ranks candidates by how much they move a goal proposition — e.g. the products most likely to be purchased.
Body
| Field | Type | Description |
|---|---|---|
from | string | Required. The collection to recommend from. |
recommend | string | Required. The field to recommend. |
goal | object | Required. The objective proposition to maximise — e.g. {"purchase": true}. |
where | object | Optional context/evidence. |
config | object | Inference tuning (config.ai, default group). |
limit | number | Max recommendations (default 10). |
Recommend products toward a goal
Recommend the products most likely to reach a goal — here, being purchased
by a given user. The where sets the context (the user); goal is the
outcome to optimise for.
You can copy-paste the example curl command to your terminal.
Request body
{
"from": "impressions",
"where": {
"context.user": "veronica"
},
"recommend": "product",
"goal": {
"purchase": true
},
"limit": 3
}Request
curl -X POST \
https://shared.aito.ai/db/aito-demo/api/v2/_recommend \
-H 'content-type: application/json' \
-H 'x-api-key: yg4rTlXkqDzm4y8gPeY75HCKaNwfbTQ2si64ONTi' \
-d '
{
"from": "impressions",
"where": { "context.user": "veronica" },
"recommend": "product",
"goal": { "purchase": true },
"limit": 3
}'Response
{
"offset": 0,
"total": 42,
"hits": [
{
"$p": 0.17469602156651695,
"$value": "2000818700008"
},
{
"$p": 0.1430041152201242,
"$value": "6414880021620"
},
{
"$p": 0.10520023908423279,
"$value": "6413200330206"
}
]
}Relate (v2)
POST/api/v2/_relate
Find statistical relations between fields instead of returning rows.
Relate answers "how does one thing relate to another" — it reports the lift and conditional probabilities of the related fields against the where context, rather than ranking rows. A lift above 1.0 means the field is more likely than its base rate in that context.
Body
| Field | Type | Description |
|---|---|---|
from | string | Required. The collection to relate over. |
relate | array | object | Required. The v2 form is an array of field paths to relate to the where known set — e.g. ["purchase"] or ["product.tags", "product.category"]. An object condition (e.g. {"purchase": true}) is also accepted as the v1 form: relate all fields to that condition. |
where | object | The context/known set the listed fields are related to. |
config | object | Inference tuning (config.ai). |
limit | number | Max relations returned (default 10). |
How a field relates to a context
Relate returns statistical relations (lift, probabilities) instead of rows.
The v2 form takes an array of field names to relate to the where known
set. Here we ask how purchase relates to products tagged vegetable — a
lift above 1.0 means those products are more likely than average to be bought.
You can copy-paste the example curl command to your terminal.
Request body
{
"from": "impressions",
"where": {
"product.tags": {
"$has": "vegetable"
}
},
"relate": [
"purchase"
],
"limit": 5
}Request
curl -X POST \
https://shared.aito.ai/db/aito-demo/api/v2/_relate \
-H 'content-type: application/json' \
-H 'x-api-key: yg4rTlXkqDzm4y8gPeY75HCKaNwfbTQ2si64ONTi' \
-d '
{
"from": "impressions",
"where": {
"product.tags": { "$has": "vegetable" }
},
"relate": ["purchase"],
"limit": 5
}'Response
{
"offset": 0,
"total": 2,
"hits": [
{
"n": 90325,
"info": 0.0005322901359249605,
"condition": {
"product.tags": {
"$has": "vegetable"
}
},
"related": {
"purchase": true
},
"fs": {
"fOnNotCondition": 4031,
"n": 90325,
"fOnCondition": 639,
"f": 4670,
"fCondition": 8632
},
"lift": 1.374662085019504
},
{
"n": 90325,
"info": 0.00053229013592495,
"condition": {
"product.tags": {
"$has": "vegetable"
}
},
"related": {
"purchase": false
},
"fs": {
"fOnNotCondition": 77662,
"n": 90325,
"fOnCondition": 7993,
"f": 85655,
"fCondition": 8632
},
"lift": 0.9794830840502661
}
]
}Match (v2)
POST/api/v2/_match
Match a context to the best-fitting values of a field.
Match ranks the candidate values of a field by how well they fit the given context — the frequency-and-fit ranking behind "which products suit this user".
Body
| Field | Type | Description |
|---|---|---|
from | string | Required. The collection to match over. |
match | string | Required. The field whose values are ranked. |
where | object | The context — field conditions and dotted link paths (context.user). |
limit | number | Max results (default 10). |
Match a user to products
Rank the products that best match a context — here, a given user.
You can copy-paste the example curl command to your terminal.
Request body
{
"from": "impressions",
"where": {
"context.user": "larry"
},
"match": "product",
"limit": 5
}Request
curl -X POST \
https://shared.aito.ai/db/aito-demo/api/v2/_match \
-H 'content-type: application/json' \
-H 'x-api-key: yg4rTlXkqDzm4y8gPeY75HCKaNwfbTQ2si64ONTi' \
-d '
{
"from": "impressions",
"where": { "context.user": "larry" },
"match": "product",
"limit": 5
}'Response
{
"offset": 0,
"total": 42,
"hits": [
{
"$p": 0.03636645071658641,
"category": "100",
"cost": 0.586,
"googleClicks": 11,
"googleImpressions": 100,
"id": "2000604700007",
"name": "Cucumber Finland",
"price": 0.9765,
"tags": [
"fresh",
"vegetable"
]
},
{
"$p": 0.03303793576204806,
"category": "100",
"cost": 0.1,
"googleClicks": 12,
"googleImpressions": 100,
"id": "2000818700008",
"name": "Pirkka banana",
"price": 0.166,
"tags": [
"fresh",
"fruit",
"pirkka"
]
},
{
"$p": 0.03271082748717629,
"category": "100",
"cost": 0.774,
"googleClicks": 10,
"googleImpressions": 100,
"id": "6410405060457",
"name": "Pirkka bio cherry tomatoes 250g international 1lk",
"price": 1.29,
"tags": [
"fresh",
"vegetable",
"pirkka",
"tomato"
]
},
{
"$p": 0.03142380061919346,
"category": "101",
"cost": 1.295,
"googleClicks": 11,
"googleImpressions": 100,
"id": "6411402202208",
"name": "Fazer Puikula fullcorn rye bread 9 pcs/500g",
"price": 1.85,
"tags": [
"gluten",
"bread"
]
},
{
"$p": 0.029923824657771932,
"category": "100",
"cost": 0.168,
"googleClicks": 9,
"googleImpressions": 100,
"id": "2000503600002",
"name": "Chiquita banana",
"price": 0.28054,
"tags": [
"fresh",
"fruit"
]
}
]
}Evaluate (v2)
POST/api/v2/_evaluate
Measure how well a v2 query performs on your own data.
_evaluate splits the data into train and test sets, hides the test rows (a transient masked delete, so no test row leaks into its own prediction), runs the query per test row, and reports accuracy, ranking, and probability-quality metrics. On collection tables the test set is chosen with $index — including $index $mod [k, r] for deterministic, leak-free k-fold folds.
Body
| Field | Type | Description |
|---|---|---|
evaluate | object | Required. The query to measure (a _predict/_recommend/… body). |
test / testSource | object | Which rows to test on. { "$index": { "$mod": [k, r] } } is a 1/k fold. Omitting both is a loud 400 — there is no implicit default test set. |
select | array | Metrics to return: accuracy, baseAccuracy, meanRank, geomMeanP, n, … |
Full walkthrough — folds, metrics, config — in the Evaluation guide (v2).
Cross-validate a predict
Measure a category predict on a deterministic 25% fold ($index $mod). No test row
trains its own prediction. See the Evaluation guide.
Request body
{
"test": {
"$index": {
"$mod": [
4,
0
]
}
},
"evaluate": {
"from": "products",
"predict": "category"
}
}Request
curl -X POST \
https://shared.aito.ai/db/aito-demo/api/v2/_evaluate \
-H 'content-type: application/json' \
-H 'x-api-key: yg4rTlXkqDzm4y8gPeY75HCKaNwfbTQ2si64ONTi' \
-d '
{
"test": {
"$index": {
"$mod": [4, 0]
}
},
"evaluate": { "from": "products", "predict": "category" }
}'Response
{
"message": "The evaluated query's 'where' has no {\"$get\": ...} binding, so each prediction is conditioned on nothing and 'accuracy' reflects unconditioned base rates (≈ 'baseAccuracy'), not per-row accuracy. Bind the tested row's evidence fields, e.g. \"where\": { \"someField\": { \"$get\": \"someField\" } }.",
"n": 11,
"testSamples": 11,
"trainSamples": 31,
"features": 235,
"error": 0.9090909090909091,
"baseError": 0.9090909090909091,
"accuracy": 0.09090909090909091,
"baseAccuracy": 0.09090909090909091,
"accuracyGain": 0,
"meanRank": 4.090909090909091,
"baseMeanRank": 4.090909090909091,
"rankGain": 0,
"informationGain": 0,
"mxe": 3.350714024506782,
"h": 3.350714024506782,
"geomMeanP": 0.09802448555185775,
"baseGmp": 0.09802448555185775,
"geomMeanLift": 1,
"meanNs": 2442462.4545454546,
"meanUs": 2442.4624545454544,
"meanMs": 2.4424624545454545,
"medianNs": 1988353,
"medianUs": 1988.353,
"medianMs": 1.988353,
"allNs": [
3543326,
2754322,
2158558,
3351738,
4226401,
1790617,
1765218,
1988353,
1791007,
1817657,
1679890
],
"allUs": [
3543,
2754,
2158,
3351,
4226,
1790,
1765,
1988,
1791,
1817,
1679
],
"allMs": [
3,
2,
2,
3,
4,
1,
1,
1,
1,
1,
1
],
"warmingMs": 1,
"accurateOffsets": [
2
],
"errorOffsets": [
0,
1,
3,
4,
5,
6,
7,
8,
9,
10
],
"cases": [
{
"offset": 0,
"testCase": {
"category": "100",
"cost": 0.1,
"googleClicks": 12,
"googleImpressions": 100,
"id": "2000818700008",
"name": "Pirkka banana",
"price": 0.166,
"tags": [
"fresh",
"fruit",
"pirkka"
]
},
"accurate": false,
"top": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "101"
},
"correct": {
"$p": 0.09523809523809523,
"field": "category",
"feature": "100"
}
},
{
"offset": 1,
"testCase": {
"category": "100",
"cost": 0.774,
"googleClicks": 9,
"googleImpressions": 100,
"id": "6410405093677",
"name": "Pirkka iceberg salad Finland 100g 1st class",
"price": 1.29,
"tags": [
"fresh",
"vegetable",
"pirkka",
"salad"
]
},
"accurate": false,
"top": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "101"
},
"correct": {
"$p": 0.09523809523809523,
"field": "category",
"feature": "100"
}
},
{
"offset": 2,
"testCase": {
"category": "101",
"cost": 0.903,
"googleClicks": 9,
"googleImpressions": 100,
"id": "6413467282508",
"name": "Fazer Puikula fullcorn rye bread 330g",
"price": 1.29,
"tags": [
"gluten",
"bread"
]
},
"accurate": true,
"top": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "101"
},
"correct": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "101"
}
},
{
"offset": 3,
"testCase": {
"category": "102",
"cost": 1.953,
"googleClicks": 10,
"googleImpressions": 100,
"id": "6410405205483",
"name": "Pirkka Finnish beef-pork minced meat 20% 400g",
"price": 2.79,
"tags": [
"meat",
"food",
"protein",
"pirkka"
]
},
"accurate": false,
"top": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "101"
},
"correct": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "102"
}
},
{
"offset": 4,
"testCase": {
"category": "103",
"cost": 1.393,
"googleClicks": 11,
"googleImpressions": 100,
"id": "6412000030026",
"name": "Saarioinen Maksalaatikko liver casserole 400g",
"price": 1.99,
"tags": [
"meat",
"food"
]
},
"accurate": false,
"top": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "101"
},
"correct": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "103"
}
},
{
"offset": 5,
"testCase": {
"category": "104",
"cost": 0.567,
"googleClicks": 12,
"googleImpressions": 100,
"id": "6410405082657",
"name": "Pirkka Finnish semi-skimmed milk 1l",
"price": 0.81,
"tags": [
"lactose",
"drink",
"pirkka"
]
},
"accurate": false,
"top": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "101"
},
"correct": {
"$p": 0.09523809523809523,
"field": "category",
"feature": "104"
}
},
{
"offset": 6,
"testCase": {
"category": "104",
"cost": 1.365,
"googleClicks": 8,
"googleImpressions": 100,
"id": "6408430000258",
"name": "Valio eila™ Lactose-free semi-skimmed milk drink 1l",
"price": 1.95,
"tags": [
"lactose-free",
"drink"
]
},
"accurate": false,
"top": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "101"
},
"correct": {
"$p": 0.09523809523809523,
"field": "category",
"feature": "104"
}
},
{
"offset": 7,
"testCase": {
"category": "108",
"cost": 2.415,
"googleClicks": 9,
"googleImpressions": 100,
"id": "6420101441542",
"name": "Kulta Katriina filter coffee 500g",
"price": 3.45,
"tags": [
"coffee"
]
},
"accurate": false,
"top": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "101"
},
"correct": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "108"
}
},
{
"offset": 8,
"testCase": {
"category": "109",
"cost": 1.533,
"googleClicks": 10,
"googleImpressions": 100,
"id": "6411401015090",
"name": "Fazer Sininen milk chocolate slab 200g",
"price": 2.19,
"tags": [
"candy",
"lactose"
]
},
"accurate": false,
"top": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "101"
},
"correct": {
"$p": 0.09523809523809523,
"field": "category",
"feature": "109"
}
},
{
"offset": 9,
"testCase": {
"category": "111",
"cost": 2.345,
"googleClicks": 11,
"googleImpressions": 100,
"id": "6413200330206",
"name": "Lotus Soft Embo 8 rll toilet paper",
"price": 3.35,
"tags": [
"toilet-paper"
]
},
"accurate": false,
"top": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "101"
},
"correct": {
"$p": 0.07142857142857142,
"field": "category",
"feature": "111"
}
},
{
"offset": 10,
"testCase": {
"category": "115",
"cost": 1.183,
"googleClicks": 10,
"googleImpressions": 100,
"id": "6410402010318",
"name": "Pirkka tuna fish pieces in oil 200g/150g",
"price": 1.69,
"tags": [
"meat",
"food",
"protein",
"pirkka"
]
},
"accurate": false,
"top": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "101"
},
"correct": {
"$p": 0.07142857142857142,
"field": "category",
"feature": "115"
}
}
],
"accurateCases": [
{
"offset": 2,
"testCase": {
"category": "101",
"cost": 0.903,
"googleClicks": 9,
"googleImpressions": 100,
"id": "6413467282508",
"name": "Fazer Puikula fullcorn rye bread 330g",
"price": 1.29,
"tags": [
"gluten",
"bread"
]
},
"accurate": true,
"top": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "101"
},
"correct": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "101"
}
}
],
"errorCases": [
{
"offset": 0,
"testCase": {
"category": "100",
"cost": 0.1,
"googleClicks": 12,
"googleImpressions": 100,
"id": "2000818700008",
"name": "Pirkka banana",
"price": 0.166,
"tags": [
"fresh",
"fruit",
"pirkka"
]
},
"accurate": false,
"top": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "101"
},
"correct": {
"$p": 0.09523809523809523,
"field": "category",
"feature": "100"
}
},
{
"offset": 1,
"testCase": {
"category": "100",
"cost": 0.774,
"googleClicks": 9,
"googleImpressions": 100,
"id": "6410405093677",
"name": "Pirkka iceberg salad Finland 100g 1st class",
"price": 1.29,
"tags": [
"fresh",
"vegetable",
"pirkka",
"salad"
]
},
"accurate": false,
"top": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "101"
},
"correct": {
"$p": 0.09523809523809523,
"field": "category",
"feature": "100"
}
},
{
"offset": 3,
"testCase": {
"category": "102",
"cost": 1.953,
"googleClicks": 10,
"googleImpressions": 100,
"id": "6410405205483",
"name": "Pirkka Finnish beef-pork minced meat 20% 400g",
"price": 2.79,
"tags": [
"meat",
"food",
"protein",
"pirkka"
]
},
"accurate": false,
"top": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "101"
},
"correct": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "102"
}
},
{
"offset": 4,
"testCase": {
"category": "103",
"cost": 1.393,
"googleClicks": 11,
"googleImpressions": 100,
"id": "6412000030026",
"name": "Saarioinen Maksalaatikko liver casserole 400g",
"price": 1.99,
"tags": [
"meat",
"food"
]
},
"accurate": false,
"top": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "101"
},
"correct": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "103"
}
},
{
"offset": 5,
"testCase": {
"category": "104",
"cost": 0.567,
"googleClicks": 12,
"googleImpressions": 100,
"id": "6410405082657",
"name": "Pirkka Finnish semi-skimmed milk 1l",
"price": 0.81,
"tags": [
"lactose",
"drink",
"pirkka"
]
},
"accurate": false,
"top": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "101"
},
"correct": {
"$p": 0.09523809523809523,
"field": "category",
"feature": "104"
}
},
{
"offset": 6,
"testCase": {
"category": "104",
"cost": 1.365,
"googleClicks": 8,
"googleImpressions": 100,
"id": "6408430000258",
"name": "Valio eila™ Lactose-free semi-skimmed milk drink 1l",
"price": 1.95,
"tags": [
"lactose-free",
"drink"
]
},
"accurate": false,
"top": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "101"
},
"correct": {
"$p": 0.09523809523809523,
"field": "category",
"feature": "104"
}
},
{
"offset": 7,
"testCase": {
"category": "108",
"cost": 2.415,
"googleClicks": 9,
"googleImpressions": 100,
"id": "6420101441542",
"name": "Kulta Katriina filter coffee 500g",
"price": 3.45,
"tags": [
"coffee"
]
},
"accurate": false,
"top": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "101"
},
"correct": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "108"
}
},
{
"offset": 8,
"testCase": {
"category": "109",
"cost": 1.533,
"googleClicks": 10,
"googleImpressions": 100,
"id": "6411401015090",
"name": "Fazer Sininen milk chocolate slab 200g",
"price": 2.19,
"tags": [
"candy",
"lactose"
]
},
"accurate": false,
"top": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "101"
},
"correct": {
"$p": 0.09523809523809523,
"field": "category",
"feature": "109"
}
},
{
"offset": 9,
"testCase": {
"category": "111",
"cost": 2.345,
"googleClicks": 11,
"googleImpressions": 100,
"id": "6413200330206",
"name": "Lotus Soft Embo 8 rll toilet paper",
"price": 3.35,
"tags": [
"toilet-paper"
]
},
"accurate": false,
"top": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "101"
},
"correct": {
"$p": 0.07142857142857142,
"field": "category",
"feature": "111"
}
},
{
"offset": 10,
"testCase": {
"category": "115",
"cost": 1.183,
"googleClicks": 10,
"googleImpressions": 100,
"id": "6410402010318",
"name": "Pirkka tuna fish pieces in oil 200g/150g",
"price": 1.69,
"tags": [
"meat",
"food",
"protein",
"pirkka"
]
},
"accurate": false,
"top": {
"$p": 0.11904761904761904,
"field": "category",
"feature": "101"
},
"correct": {
"$p": 0.07142857142857142,
"field": "category",
"feature": "115"
}
}
],
"alpha_binByTopScore": [
{
"meanScore": 0.11904761904761904,
"maxScore": 0.11904761904761904,
"minScore": 0.11904761904761904,
"accuracy": 0.25,
"n": 4,
"accurateOffsets": [
2
],
"errorOffsets": [
0,
1,
3
]
},
{
"meanScore": 0.11904761904761904,
"maxScore": 0.11904761904761904,
"minScore": 0.11904761904761904,
"accuracy": 0,
"n": 4,
"accurateOffsets": [],
"errorOffsets": [
4,
5,
6,
7
]
},
{
"meanScore": 0.11904761904761903,
"maxScore": 0.11904761904761904,
"minScore": 0.11904761904761904,
"accuracy": 0,
"n": 3,
"accurateOffsets": [],
"errorOffsets": [
8,
9,
10
]
}
]
}Estimate (v2)
POST/api/v2/_estimate
Estimate the value of a numeric field from evidence.
Estimate predicts a continuous quantity (e.g. a price) rather than a categorical value, returning the expected value and its distribution.
Body
| Field | Type | Description |
|---|---|---|
from | string | Required. The collection. |
estimate | string | Required. The numeric (Int/Decimal) field to estimate. |
where | object | The evidence. |
select | array | Include estimate for the point estimate. |
Estimate a numeric field
Estimate the price of products matching a condition.
You can copy-paste the example curl command to your terminal.
Request body
{
"from": "products",
"where": {
"tags": {
"$has": "bread"
}
},
"estimate": "price",
"select": [
"estimate"
]
}Request
curl -X POST \
https://shared.aito.ai/db/aito-demo/api/v2/_estimate \
-H 'content-type: application/json' \
-H 'x-api-key: yg4rTlXkqDzm4y8gPeY75HCKaNwfbTQ2si64ONTi' \
-d '
{
"from": "products",
"where": {
"tags": { "$has": "bread" }
},
"estimate": "price",
"select": ["estimate"]
}'Response
{
"estimate": 1.350545069703815
}Aggregate (v2)
POST/api/v2/_aggregate
Compute aggregate statistics over a collection.
Aggregate reduces columns to summary statistics using the field.$op syntax — $mean, $sum, $variance, and related moments — optionally scoped by a where.
Body
| Field | Type | Description |
|---|---|---|
from | string | Required. The collection. |
aggregate | array | Required. A list of "field.$op" terms, e.g. ["price.$mean", "price.$sum"]. |
where | object | Optional filter to aggregate over a subset. |
Aggregate statistics
Compute mean/sum aggregates over columns with the field.$op syntax.
You can copy-paste the example curl command to your terminal.
Request body
{
"from": "products",
"aggregate": [
"price.$mean",
"price.$sum",
"googleClicks.$mean"
]
}Request
curl -X POST \
https://shared.aito.ai/db/aito-demo/api/v2/_aggregate \
-H 'content-type: application/json' \
-H 'x-api-key: yg4rTlXkqDzm4y8gPeY75HCKaNwfbTQ2si64ONTi' \
-d '
{
"from": "products",
"aggregate": ["price.$mean", "price.$sum", "googleClicks.$mean"]
}'Response
{
"googleClicks.$mean": 9.80952380952381,
"googleClicks.$mean.samples": 42,
"googleClicks.$mean.variance": 1.9637188208616578,
"googleClicks.$mean.standardDeviation": 1.4013275209106748,
"googleClicks.$mean.standardError": 0.21622953087059052,
"price.$sum": 70.38304000000001,
"price.$sum.samples": 42,
"price.$mean": 1.6757866666666668,
"price.$mean.samples": 42,
"price.$mean.variance": 0.9422393083365073,
"price.$mean.standardDeviation": 0.9706901196244387,
"price.$mean.standardError": 0.14978073723315463
}Batch (v2)
POST/api/v2/_batch
Run several v2 queries in a single request.
Post a JSON array of query bodies; each runs through the same Query2 machinery as _query and the results are returned in order. Batching amortises request overhead when a page needs several predictions at once.
Body
A JSON array of query objects — each the body you would post to _query/_predict/_recommend/etc.
[
{ "from": "products", "where": { "name": "rye bread" }, "predict": "category" },
{ "from": "products", "where": { "name": "rye bread" }, "predict": "tags" }
]
Run several queries in one request
Post an array of queries; each runs through the same Query2 machinery as _query
and the results come back in order.
Request body
[
{
"from": "products",
"where": {
"name": "rye bread"
},
"predict": "category"
},
{
"from": "products",
"where": {
"name": "rye bread"
},
"predict": "tags"
}
]Request
curl -X POST \
https://shared.aito.ai/db/aito-demo/api/v2/_batch \
-H 'content-type: application/json' \
-H 'x-api-key: yg4rTlXkqDzm4y8gPeY75HCKaNwfbTQ2si64ONTi' \
-d '
[
{
"from": "products",
"where": { "name": "rye bread" },
"predict": "category"
},
{
"from": "products",
"where": { "name": "rye bread" },
"predict": "tags"
}
]'Response
[
{
"offset": 0,
"total": 11,
"hits": [
{
"$p": 0.9729371230219164,
"$value": "101"
},
{
"$p": 0.0033219857319227723,
"$value": "102"
},
{
"$p": 0.0033219857319227723,
"$value": "103"
},
{
"$p": 0.003305953340962574,
"$value": "100"
},
{
"$p": 0.003305953340962574,
"$value": "104"
},
{
"$p": 0.003305953340962574,
"$value": "108"
},
{
"$p": 0.0027701548656267837,
"$value": "109"
},
{
"$p": 0.002227782360484964,
"$value": "111"
},
{
"$p": 0.002227782360484964,
"$value": "115"
},
{
"$p": 0.00163766295237681,
"$value": "106"
}
]
},
{
"offset": 0,
"total": 25,
"hits": [
{
"$p": 0.4962860612870516,
"$value": "gluten"
},
{
"$p": 0.48674349016827606,
"$value": "bread"
},
{
"$p": 0.0022722516257175397,
"$value": "food"
},
{
"$p": 0.0018429336402213446,
"$value": "sausage"
},
{
"$p": 0.0018429336402213446,
"$value": "with-bread"
},
{
"$p": 0.0005506164819255847,
"$value": "candy"
},
{
"$p": 0.0005506164819255847,
"$value": "carbohydrate"
},
{
"$p": 0.0005506164819255847,
"$value": "coffee"
},
{
"$p": 0.0005506164819255847,
"$value": "drink"
},
{
"$p": 0.0005506164819255847,
"$value": "fresh"
}
]
}
]Schema API
The Schema API v2 provides endpoints for managing table schemas. It supports both CollectionDb (document-oriented) and TableDb (row-based) table types.
Endpoints:
GET /api/v2/schema- List tablesGET /api/v2/schema/{table}- Get table schemaPUT /api/v2/schema/{table}- Create tablePUT /api/v2/schema/{table}/{column}- Add or alter a columnPOST /api/v2/schema/{table}/_plan- Dry-run a schema changePOST /api/v2/schema/{table}/_apply- Apply a schema changeDELETE /api/v2/schema/{table}- Delete tablePOST /api/v2/data/{table}- Insert documentPOST /api/v2/data/{table}/batch- Batch insert
Schema migration & maintenance
Beyond creating and dropping tables, v2 collections can evolve their schema and data in place — no full reload:
- Add or alter a column —
PUT /api/v2/schema/{table}/{column}adds a column (backfilling existing rows from an optionalvalue), or, if the column already exists, alters its type/analyzer and re-indexes the data. Type-compatible changes only: wideningint → long → decimal, andstring ↔ textwith an analyzer. - Plan & apply, declaratively —
POST …/{table}/_plandry-runs a target schema and returns aterraform plan-style diff (per column: add / rebuild / drop / rejected / no-op) without touching anything.POST …/{table}/_applyconverges the table to that target atomically, refusing lossy changes and requiring?confirm=truefor column drops. - Modify rows —
POST /api/v2/data/_modifyappliesinsert/update(withupsert) /deleteoperations, matched by awhereclause, in one commit. - Bulk backfill a column —
POST /api/v2/data/{table}/_backfillsets column values on many existing rows at once, matched by a business key, using a compact columnar body — the fast way to populate a newly-added column.
These work on both CollectionDb (v2) and, where noted, legacy TableDb tables. See the Schema Migration & Maintenance guide for the full walkthrough with request/response examples.
Add a column with a default:
curl -X PUT \
'https://your-instance.aito.ai/api/v2/schema/users/tier' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"type": "String", "value": "free"}'
Bulk-backfill it by key:
curl -X POST \
'https://your-instance.aito.ai/api/v2/data/users/_backfill' \
-H 'x-api-key: YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{"key": "id", "keys": [1, 2, 3],
"columns": {"tier": ["gold", "silver", "bronze"]}}'
List Tables (v2)
GET/api/v2/schema
List all tables in the database.
Returns schema information for all tables, including column definitions and types.
Response Format
The response is an object where each key is a table name and the value contains the table schema:
{
"tableName": {
"type": "collection" | "table",
"columns": {
"columnName": { "type": "String" | "Int" | "Decimal" | "Boolean" | "Text" | "Json" }
}
}
}
Table Types
| Type | Description |
|---|---|
collection | CollectionDb table - optimized for document storage |
table | TableDb table - traditional row-based storage |
List all tables
Returns a list of all tables in the database with their schema information.
You can copy-paste the example curl command to your terminal.
Request
curl -X GET \
https://shared.aito.ai/db/aito-demo/api/v2/schema \
-H 'x-api-key: yg4rTlXkqDzm4y8gPeY75HCKaNwfbTQ2si64ONTi'Response
{
"schema": {
"prompts": {
"type": "table",
"columns": {
"assignee": {
"type": "String",
"nullable": true,
"link": "employees.Name"
},
"tags": {
"type": "String[]",
"nullable": true
},
"sentiment": {
"type": "String",
"nullable": true
},
"prompt": {
"type": "Text",
"analyzer": "english"
},
"urgency": {
"type": "String",
"nullable": true
},
"categories": {
"type": "String[]",
"nullable": true
},
"answer": {
"type": "Int",
"nullable": true,
"link": "answers.id"
},
"type": {
"type": "String"
}
}
},
"visits": {
"type": "table",
"columns": {
"purchases": {
"type": "String[]"
},
"prev": {
"type": "String",
"nullable": true,
"link": "this.id"
},
"week": {
"type": "Int"
},
"id": {
"type": "String"
},
"weekday": {
"type": "String"
},
"day": {
"type": "Int"
},
"user": {
"type": "String",
"link": "users.id"
},
"month": {
"type": "Int"
}
}
},
"glCodes": {
"type": "table",
"columns": {
"Department": {
"type": "String"
},
"GLCode": {
"type": "String"
},
"Name": {
"type": "String"
}
}
},
"impressions": {
"type": "table",
"columns": {
"context": {
"type": "String",
"link": "contexts.id"
},
"product": {
"type": "String",
"link": "products.id"
},
"purchase": {
"type": "Boolean"
}
}
},
"products": {
"type": "table",
"columns": {
"googleImpressions": {
"type": "Int"
},
"name": {
"type": "Text",
"analyzer": "english"
},
"tags": {
"type": "String[]"
},
"price": {
"type": "Decimal"
},
"id": {
"type": "String"
},
"cost": {
"type": "Decimal"
},
"category": {
"type": "String"
},
"googleClicks": {
"type": "Int"
}
}
},
"price_history": {
"type": "table",
"columns": {
"similar_products_on_sale": {
"type": "Int"
},
"competitor_avg_price": {
"type": "Decimal"
},
"weather_temp": {
"type": "Decimal"
},
"name": {
"type": "Text",
"analyzer": "english"
},
"list_price": {
"type": "Decimal"
},
"promotional_placement": {
"type": "String"
},
"days_since_delivery": {
"type": "Int"
},
"timestamp": {
"type": "String"
},
"day_of_week": {
"type": "String"
},
"tags": {
"type": "String[]"
},
"category_name": {
"type": "String"
},
"is_holiday_week": {
"type": "Boolean"
},
"year": {
"type": "Int"
},
"units_sold": {
"type": "Int"
},
"brand": {
"type": "String"
},
"days_until_expiry": {
"type": "Int"
},
"category_products_count": {
"type": "Int"
},
"week": {
"type": "Int"
},
"date": {
"type": "String"
},
"discount_percentage": {
"type": "Decimal"
},
"sale_price": {
"type": "Decimal"
},
"margin_percentage": {
"type": "Decimal"
},
"units_in_stock": {
"type": "Int"
},
"product_id": {
"type": "String",
"link": "products.id"
},
"category": {
"type": "String"
},
"purchase_cost": {
"type": "Decimal"
},
"is_weekend": {
"type": "Boolean"
},
"month": {
"type": "Int"
}
}
},
"invoices": {
"type": "table",
"columns": {
"ReceiverAddress": {
"type": "Text",
"analyzer": "english"
},
"InvoiceType": {
"type": "String",
"nullable": true
},
"AccountNumber": {
"type": "String"
},
"TotalAmount": {
"type": "Decimal"
},
"Processor": {
"type": "String",
"nullable": true,
"link": "employees.Name"
},
"VATAmount": {
"type": "Decimal"
},
"Acceptor": {
"type": "String",
"nullable": true,
"link": "employees.Name"
},
"Description": {
"type": "Text",
"analyzer": "english"
},
"PaymentDueDate": {
"type": "String"
},
"GLCode": {
"type": "String",
"nullable": true,
"link": "glCodes.GLCode"
},
"InvoiceID": {
"type": "String"
},
"InvoiceDate": {
"type": "String"
},
"SenderAddress": {
"type": "Text",
"analyzer": "english"
},
"SenderName": {
"type": "Text",
"analyzer": "english"
},
"InvoiceNumber": {
"type": "String"
},
"ProductName": {
"type": "Text",
"analyzer": "english"
},
"ReceiverName": {
"type": "Text",
"analyzer": "english"
},
"PaymentMethod": {
"type": "String"
}
}
},
"contexts": {
"type": "table",
"columns": {
"query": {
"type": "Text",
"analyzer": "english"
},
"week": {
"type": "Int"
},
"id": {
"type": "String"
},
"prevBasket": {
"type": "String[]"
},
"visit": {
"type": "String",
"link": "visits.id"
},
"weekday": {
"type": "String"
},
"type": {
"type": "String"
},
"queryPhrase": {
"type": "String"
},
"day": {
"type": "Int"
},
"basket": {
"type": "String[]"
},
"user": {
"type": "String",
"link": "users.id"
},
"month": {
"type": "Int"
}
}
},
"users": {
"type": "table",
"columns": {
"id": {
"type": "String"
},
"tags": {
"type": "String[]"
}
}
},
"answers": {
"type": "table",
"columns": {
"answer": {
"type": "Text",
"analyzer": "english"
},
"id": {
"type": "Int"
}
}
},
"employees": {
"type": "table",
"columns": {
"Department": {
"type": "String"
},
"Name": {
"type": "String"
},
"Role": {
"type": "String"
},
"Superior": {
"type": "String",
"nullable": true
}
}
}
}
}Get Table Schema (v2)
GET/api/v2/schema/{table}
Get the schema for a specific table.
Returns detailed schema information including column names, types, and table type.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
table | string | The name of the table. |
Response Format
{
"type": "collection" | "table",
"columns": {
"columnName": { "type": "String" | "Int" | "Decimal" | "Boolean" | "Text" | "Json" }
}
}
Get schema for a specific table
Returns the schema definition for the specified table.
You can copy-paste the example curl command to your terminal.
Request
curl -X GET \
https://shared.aito.ai/db/aito-demo/api/v2/schema/products \
-H 'x-api-key: yg4rTlXkqDzm4y8gPeY75HCKaNwfbTQ2si64ONTi'Response
{
"type": "table",
"columns": {
"googleImpressions": {
"type": "Int"
},
"name": {
"type": "Text",
"analyzer": "english"
},
"tags": {
"type": "String[]"
},
"price": {
"type": "Decimal"
},
"id": {
"type": "String"
},
"cost": {
"type": "Decimal"
},
"category": {
"type": "String"
},
"googleClicks": {
"type": "Int"
}
}
}Create Table (v2)
PUT/api/v2/schema/{table}
Create a new table with the specified schema.
Creates a table using either CollectionDb or TableDb storage, depending on the type field.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
table | string | The name of the table to create. |
Request Body
{
"type": "collection" | "table",
"columns": {
"columnName": { "type": "String" | "Int" | "Decimal" | "Boolean" | "Text" | "Json" },
"linkColumn": { "type": "String", "link": "targetTable.keyColumn" }
}
}
Table Types
| Type | Description |
|---|---|
collection | Creates a CollectionDb table - optimized for document storage with flexible schema |
table | Creates a TableDb table - traditional row-based storage |
Column Types
| Type | Description |
|---|---|
String | Short text values |
Text | Long text with full-text search support |
Int | Integer numbers |
Decimal | Floating-point numbers |
Boolean | True/false values |
Json | JSON objects |
Link Fields
Link fields create relationships between tables, enabling cross-table queries using dotted path notation.
Defining a link:
{
"type": "collection",
"columns": {
"processorId": { "type": "String", "link": "users.id" }
}
}
Querying linked fields:
{
"from": "invoices",
"where": { "processorId.role": "Manager" },
"select": ["amount", "processorId.name"]
}
The link property specifies targetTable.keyColumn - the table and column that this field references.
Response
Returns 201 Created on success with the table name.
Add or alter column (v2)
PUT/api/v2/schema/{table}/{column}
Add a column to an existing table, or alter an existing column in place.
- Add — when the column is new, existing rows are backfilled from the optional
valuedefault. A non-nullable column with novalueis rejected on a non-empty table (make it"nullable": trueto backfill withnull). - Alter — when the column already exists, its type/analyzer is changed and the data re-indexed. This is a CollectionDb (v2) capability; supported changes are type-compatible: widening
Int → Long → Decimal, andString ↔ Text(analyzer). Legacy v1 (TableDb) tables do not support in-place alter.
To set new values across many rows afterwards, use a bulk backfill. A read-write API key is required.
Plan schema change (v2)
POST/api/v2/schema/{table}/_plan
Dry-run a declarative schema change — the terraform plan to _apply.
The body is a target table schema (same shape as create). The response is a classified diff of current vs target: per column an action of add, rebuild, drop, rejected or noop, plus a summary (change count, rows affected, whether it's additive-only, lossy, rejected, or needs confirmation).
_plan is read-only — the schema is not changed. CollectionDb (v2) only. A read API key is required.
Apply schema change (v2)
POST/api/v2/schema/{table}/_apply
Declaratively converge a table to a target schema — the terraform apply to _plan.
The body is a target table schema; _apply adds, rebuilds and drops columns atomically to match it. It refuses rejected/lossy type changes, and refuses column drops (data loss) unless ?confirm=true is passed. The response is the applied plan (same shape as _plan).
CollectionDb (v2) only. A read-write API key is required.
Delete Table (v2)
DELETE/api/v2/schema/{table}
Delete a table and all its data.
Permanently removes the specified table from the database. This operation cannot be undone.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
table | string | The name of the table to delete. |
Response
Returns 200 OK on success with the deleted table name.
Warning
This operation permanently deletes the table and all data within it. Make sure to backup any important data before deletion.
Insert Document (v2)
POST/api/v2/data/{table}
Insert a single document into a table.
Adds one document to the specified table. The document fields should match the table schema.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
table | string | The name of the table to insert into. |
Request Body
A JSON object containing the document fields and values:
{
"field1": "value1",
"field2": 123,
"field3": true
}
Response
{
"status": "inserted",
"table": "tableName",
"count": 1
}
Notes
- Works with both CollectionDb and TableDb tables
- Field names should match the table schema
- Values are automatically converted to the appropriate column types
Batch Insert (v2)
POST/api/v2/data/{table}/batch
Batch insert multiple documents into a table.
Adds multiple documents to the specified table in a single operation. This is more efficient than inserting documents one at a time.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
table | string | The name of the table to insert into. |
Request Body
A JSON array of documents:
[
{ "field1": "value1", "field2": 123 },
{ "field1": "value2", "field2": 456 },
{ "field1": "value3", "field2": 789 }
]
Response
{
"status": "inserted",
"table": "tableName",
"count": 3
}
Notes
- Works with both CollectionDb and TableDb tables
- All documents in the batch should have consistent field names
- Use batch insert for better performance when adding many documents
- Each document's values are automatically converted to the appropriate column types
Modify data (v2)
POST/api/v2/data/_modify
Apply one or more atomic modification operations to v2 tables (CollectionDb or TableDb) through the unified modification engine.
A single request may carry one operation object, or { "operations": [ ... ] } to apply several in one commit. Supported operations:
- insert —
{ "into": "t", "insert": { ... } } - update —
{ "update": "t", "set": { ... }, "where": { ... }, "upsert": true? } - delete —
{ "from": "t", "delete": { ... } } - backfill —
{ "backfill": "t", "key": "id", "keys": [...], "columns": { "col": [...] }, "upsert": false? }— bulk-set column values on existing rows by key (see also the_backfillsugar endpoint) - optimize, rename, copy, warm
A read-write API key is required.
Bulk backfill (v2)
POST/api/v2/data/{table}/_backfill
Bulk-set column values on existing rows, matched by a business key — the compact way to populate a column with new values after adding it.
The body is columnar: a key column name plus index-aligned keys and columns value arrays, so it stays small for large jobs (no per-row field-name repetition). All rows are applied in a single commit. A key that matches no row is a no-op unless upsert is true.
This is sugar over the backfill operation of _modify, and works on both CollectionDb (v2) and legacy TableDb tables. A read-write API key is required.
List envs
GET/api/v2/_envs
List the environments that exist in the database.
Identical behavior and response shape as the v1 list endpoint; the v2 path is provided so v2-only clients have a stable URL.
A read API key is sufficient.
See the Environments guide for the URL pattern, when to use envs, and promote semantics.
Branch env
POST/api/v2/_envs
Branch a new environment from an existing one.
Identical behavior, request body, and naming rules as the v1 branch endpoint.
A read-write API key is required.
After creation, target the new env via /env/{name}/api/v2/.... See the Environments guide.
Delete env
DELETE/api/v2/_envs/{env}
Drop the named environment.
Identical behavior as the v1 delete endpoint. Deleting env.master returns 400; reserved (_-prefixed) names are also rejected; missing envs return 404.
A read-write API key is required.
Promote env into master
POST/api/v2/_envs/{env}/promote
Atomically swap the named env into master.
Identical semantics as the v1 promote endpoint: swap-not-move, source preserved, single-transaction.
A read-write API key is required.
List releases
GET/api/v2/_releases
List the published releases of the database — each an immutable, named pointer to a committed head, returned with its content hash.
A read API key is sufficient.
See the Releases guide for how releases differ from environments, the publish/retire lifecycle, and mountable repositories.
Publish release
POST/api/v2/_releases
Freeze the database's current committed head as a new immutable release release.<name>, returning the frozen head hash.
A release is immutable: re-publishing an existing name returns 409 (retire it first). Reserved (_-prefixed) names return 400, as does publishing before the database has any committed state. Names follow the same rules as env names.
A read-write API key is required. See the Releases guide.
Retire release
DELETE/api/v2/_releases/{name}
Retire (unpin) a release. The head it pinned becomes garbage-collection eligible again unless another ref still reaches it. Retiring a release that does not exist returns 404.
A read-write API key is required.
List refs
GET/api/v2/_refs
List every ref in the database — branches (environments, including the default env.master) and releases together — each with its role, mutable flag, and head hash. The default branch is marked default: true.
This is the git-style plumbing read beneath _envs and _releases, so a console can render the whole version graph from one call. Filter with ?role=branch or ?role=release. Available on both v1 and v2 (read-only).
A read API key is sufficient. See the Releases guide.
Create or move a ref
PUT/api/v2/_refs/{ref}
Create or move a ref — the generic write plumbing the _envs/_releases porcelain is sugar over (v2 only; _refs is read-only on v1).
PUT /api/v2/_refs/{branch}with{ "from": "env.master" }creates a branch from another branch; add?force=trueto move an existing branch.PUT /api/v2/_refs/release.{name}freezes the current head as a release.
Invariants are enforced in the core, identical across every surface: protected refs (_…, system) → 403; re-publishing a release → 409; moving an existing branch without force → 409; a branch write missing from → 400. main/master alias the default branch.
A read-write API key is required. See the Releases guide.
Delete or retire a ref
DELETE/api/v2/_refs/{ref}
Delete a branch or retire a release (v2 only). A release.{name} ref is retired; any other ref is deleted as a branch.
Deleting the default (master) branch returns 400; protected refs (_…, system) return 403; a missing ref returns 404.
A read-write API key is required.