OriginChain docs
reference · schemas

Schemas

A schema is the shape of your data - like the columns of a spreadsheet. You write it as a small TOML file, send it to OriginChain once, and then you can save and query rows that follow that shape.

Three pages, three reading paths. This page is the overview - read it first. Tutorial walks you through building one from scratch, step by step. Reference is the field-by-field manual you reach for once you know what you're looking for.

The smallest possible schema.

This is the minimum viable schema - one table with three columns. Save it as manifest.toml.

# manifest.toml - the smallest schema you can write.
namespace   = "shop"
table       = "customers"
primary_key = ["id"]

[[columns]]
name = "id"
ty   = "str"
required = true

[[columns]]
name = "email"
ty   = "str"

[[columns]]
name = "signup_ms"
ty   = "u64"
what each piece means
Piece What it does
namespace A logical grouping for related tables. Like a Postgres schema. Use one per product area: shop, analytics, auth.
table The table's name. You query it as namespace.table - here, shop.customers.
primary_key A list of column names that uniquely identify a row. Most tables use a single ID column; put it in the array.
[[columns]] One block per column. name = the column name, ty = the type. Set required = true on columns that can't be null.

Six column types - that's all.

OriginChain keeps its type system small on purpose. There's no ulid, no decimal, no timestamp type. You map your data to one of six base types and the rest is convention.

Type Use it for
str Any text. Names, emails, IDs (including ULIDs and UUIDs - they travel as their canonical text form), category labels, status strings, JSON blobs you don't need to query into.
i64 Signed 64-bit integers. Use for money stored in minor units (cents, paise, satoshis), quantities, refundable counters. Never use floats for money.
u64 Unsigned 64-bit integers. Use for timestamps (epoch milliseconds or microseconds - pick one and stick with it), monotonic counters, sequence numbers.
f64 Double-precision float. Use for rates, ratios, percentages, scientific measurements - things where rounding doesn't matter.
bool A single yes/no value.
bytes Opaque binary blob. Use for compressed payloads, encoded vectors stored alongside rows, anything that's already a byte sequence.

Register a schema.

Once you've written your manifest.toml, send it to OriginChain. After this succeeds, the table exists and you can start saving rows.

POST /v1/tenants/:t/schemas
curl -X POST "https://$OC_HOST/v1/tenants/$OC_TENANT/schemas" \
  -H "Authorization: Bearer $OC_TOKEN" \
  -H "Content-Type: text/plain" \
  --data-binary @manifest.toml
common mistakes
  • Wrong Content-Type. The body is TOML, not JSON. Use Content-Type: text/plain.
  • Forgetting primary_key. Every schema needs at least one column listed in the top-level primary_key = ["..."] array.
  • Wrong column type. Only the six types above work. ulid, decimal, timestamp, vector, string, and int all return 400.

What you can add beyond the basics.

The minimal schema above lets you save and read rows. Add any of these blocks when you need more:

What is not in the schema.

Two things people expect to declare on the schema but don't:

  • Vector embeddings. There is no [[extractions.vector]] block. Vectors are stored on a separate runtime endpoint (POST /vector/:table/put) and reference rows by primary key. See Vector tables.
  • Full-text indexes. There is no [[extractions.fts]] block. Full-text indexes live on their own runtime endpoint (POST /fts/:table/:field). Tokenizer and analyzer pipeline live on the index call, not the schema. See FTS tables.

The schema is purely the row contract. Vector and full-text are auxiliary indexes that link back to rows by primary key.

Where to go next.