Skip to main content
The Tally Query Protocol is the DataSource contract as JSON over HTTP. tally-server serves it from Node, tally/laravel serves it from Laravel, and connectHttpSource in tally-adapters consumes it. Any server that answers these four routes backs a grid.

Routes

Every route sits under one base URL, such as /api/accounts. Every body carries version: 1, except responses to /query and /rows.

Schema

GET /schema describes the grid: its columns, what the server does itself and the key field.
schema-response.ts
connectHttpSource builds the client schema from this response, so the columns are defined once, on the server.

Query

POST /query takes the query of the current view and the page, and answers with rows and the summaries the grid asked for.
query.ts
A facet is either { kind: 'terms', terms: [{ value, count }], distinct } or { kind: 'histogram', min, max, bins: [{ start, end, count }] }. The column type decides which. The grid only sends what the capabilities allow. A filter that contains a flag is never sent, and a filter with a search is not sent to a source without search. The grid applies those itself. See Query language reference for every operator a filter can carry.

Rows

PATCH /rows applies the mutations the grid committed, in order.
mutation.ts
The result is { accepted, rows?, errors? }. rows are the canonical rows after the write, and the grid replaces its copies with them. errors are { key?, column?, message }. A rejected mutation is rolled back in the grid. See Mutations and undo. A read-only server answers 405 with the code read_only.

Events

GET /events is a server-sent event stream. Each change is one patch event whose data is a RowPatch:
Upserts replace the row with the same key or add it, and deletes lists keys to remove. tally-server writes a : ping comment every 15 seconds to keep the connection open, and broadcasts every accepted mutation to every open stream. The client reconnects when the stream drops. A server that reports live: false in its capabilities gets no event stream from the client.

Errors

Errors share one body:
Each detail starts with the path of one problem. The checks behind invalid_query cover the columns, operators, value kinds, operator arity, groupable columns, granularity on date columns only, aggregates and facets. connectHttpSource turns an error body into an HttpSourceError with status, code, message and details.

Validating requests

tally-protocol holds a Zod schema for every body and validates a request against a Schema. A server in TypeScript does not need tally-server to speak the protocol:
server.ts
The wire schemas are exported too: queryRequestSchema, queryResponseSchema, mutationRequestSchema, mutationResultSchema, rowPatchSchema and schemaResponseSchema.

Other languages

The same contract ships as JSON Schema for servers in any language. Import a file from tally-protocol/schemas/*.json or call toJsonSchema(name). Every document is on the JSON Schema page.

Conformance

A backend is conformant when it answers the same rows, totals, groups, aggregates and facets as the in-memory executor of tally-core for a fixture dataset. runConformance runs every case against your handler and lists what differed, by path. See Writing your own source and Testing with conformance.