How it works
Everything revolves around one database per workspace (backed by Turso, a managed libSQL/SQLite service) that holds your tables. Each table row can be linked to a GOBL document through its automaticsilo_entry_id column, and data moves between rows and documents through four workflow actions:
Each action is configured with a JQ query that maps columns onto GOBL fields, or the other way around. You can test your queries in the online JQ playground before saving them.
Before you start, you’ll want at least one workflow to process your documents.
Set up your database
1
Enable the Tables app
In the Console, open the apps directory by clicking the icon next to Apps in the sidebar. Find Tables in the list of available apps and enable it in your workspace. A database is created for you automatically the first time you open the app.
2
Open the app
Once enabled, Tables appears in the sidebar under Apps. Opening it gives you:
- a list of the tables in your database,
- a SQL editor for running queries,
- your database connection details for direct access from your own tools.
3
Create a table
Create a table either from a template or by defining your own columns.Templates are the quickest way to start with invoice data:
- Simple: one row per invoice with a single line item. Best for straightforward, one-line documents.
- Multi-row: one row per line item, plus a view that aggregates the rows of each invoice back into a single record.
- Multi-table: normalized
suppliers,customers,invoices, andinvoice_linestables with a view that reassembles a full invoice.
ordering_code uniqueness constraint so the same document can’t be inserted twice, and comes with a suggested import query (see Import below).Every table automatically includes an auto-incrementing
id primary key and an optional silo_entry_id column that links a row back to the GOBL document it produced or came from. You don’t need to add these yourself.Load data into a table
There are three ways to get data into a table:- SQL editor: run
INSERTstatements (or any other SQL) directly from the app. Handy for seeding reference data and for quick edits. - Direct connection: generate database credentials from the app and connect with any libSQL/SQLite-compatible client to bulk-load data from your own systems.
- Export step: let a workflow write rows for you as documents are processed (see Export below).
Use tables in your workflows
The four actions are added as steps inside your own workflows. Each step is configured with the fields described below.Import: turn a row into a document
The Import step reads one table row and builds a GOBL document from it, saving the result as a silo entry that the rest of the workflow can process. Configure it with:- Schema: the GOBL schema to produce:
bill/invoice,bill/payment,bill/status, ororg/party. - Scope: whether the query output is a Document or a full Envelope.
- Query definition: the JQ that maps columns onto GOBL fields. Use Load suggested JQ from template to start from the query that ships with the table’s template.
- Allow invalid envelopes: store the result even if it doesn’t fully pass schema validation.
$schema for you):
Export: write a document back to a table
The Export step writes fields from the document into a table row, for reporting, reconciliation, or to capture values produced earlier in the workflow (such as a tax authority reference number). Configure it with:- Target table: where to write the row.
- Scope: Document or Envelope.
- Query definition: JQ that maps GOBL fields onto table columns.
- Upsert key: one or more columns used to match an existing row. If a match is found the row is updated in place; otherwise a new row is inserted. Leave empty to always insert.
silo_entry_id returns the current document’s ID and workspace_id returns the workspace ID.
silo_entry_id, re-running the workflow updates the same row instead of creating duplicates.
Merge: enrich a document from a table
The Merge step looks up a row and patches its values into the document currently being processed. This is useful for pulling in reference data such as payment terms, customer details, or tax rates. Configure it with:- Source table: the table to look up.
- Scope: Document or Envelope.
- Key query: JQ that returns
{ column: value }pairs used to find the matching row (aWHEREclause). - Patch query: JQ that returns the updated document. The matched row is available as the
$rowvariable. - Required: when on, the step fails if no row matches. When off, the step is simply skipped and the workflow continues.
- Allow invalid envelopes: save the patched document even if it doesn’t fully pass schema validation.
Create batch: process many rows at once
The Create batch step fans a workflow out over a whole table, running one job per row. It pairs naturally with an Import step to load a large set of rows as individual documents. Configure it with:- Workflow to run for each row: the workflow executed once per matching row.
- Filter (optional): a SQL
WHEREclause that selects which rows to process, for examplestatus = 'pending'.
Query and browse your data
At any time you can open the SQL editor in the app to runSELECT queries, inspect a table’s schema, and page through its rows. This is the fastest way to check what a workflow wrote, debug a query, or spot-fix a value.
Participate in our community
Ask and answer questions about the Tables app →