Skip to content

JSON wire format

The canonical machine representation of a SemQL query, key by key.

The JSON form is what implementations exchange. Every text query compiles to it, and a client may submit either.

Top level

{
  "match": { },
  "namespace": { },
  "window": "P30D",
  "limit": 50
}
Key Required Description
match yes The boolean expression
namespace no Namespace selector
window no ISO 8601 duration
limit no Positive integer

Expressions

Boolean composition uses and, or and not. A bare clause object is itself a valid expression, so a single-clause query needs no wrapper.

{ "and": [ { "distance": { } }, { "direction": { } } ] }
{ "or":  [ { "distance": { } }, { "direction": { } } ] }
{ "not": { "distance": { } } }

and and or take at least two children; not takes exactly one. See composition.

Clauses

{ "distance":  { "anchor": "payment failure", "within": 0.8 } }
{ "direction": { "toward": ["customer frustration"], "cone": 0.4 } }
{ "contrast":  { "attract": ["enterprise"], "repel": ["free tier"], "within": 0.75 } }

Field tables live with each clause: DISTANCE, DIRECTION, CONTRAST.

Anchors

A string, which the host embeds, or an array of numbers, which is already an embedding:

"payment reconciliation failure"
[0.182, -0.041, 0.389, 0.057]

See anchors for when each is appropriate.

Namespace selector

{
  "include": ["org:acme-corp", "org:acme-eu"],
  "exclude": ["org:acme-staging"],
  "global": true,
  "all": false
}

The text keywords GLOBAL and ALL map to the two booleans. See modifiers.

Durations

The JSON form is always ISO 8601 — "PT30S", "PT15M", "PT24H", "P7D", "P28D". The text parser also accepts the shorthand (30s, 15m, 24h, 7d, 4w), but the serializer only ever emits ISO 8601.

A round trip therefore normalizes: 4w comes back as "P28D".

Conversion is lossless, with one exception

The two forms are interconvertible, except for one field.

metric on DISTANCE exists only in JSON. There is no METRIC keyword in the text grammar, so:

The published specification states twice that the two representations are losslessly interconvertible. For every field except metric, they are.

If you use a non-default metric, keep the query in JSON and do not round-trip it through text.