Defining a schema for a record type

Collections let you define what the metadata of a record type should look like. You register a type (for example customers) as a collection, attach a JSON Schema for its metadata, and choose how strictly Runtype validates writes: not at all, with warnings, or by rejecting nonconforming writes. Registering a collection changes nothing until you opt in, so you can adopt schemas gradually on a live workspace.

What a collection is

A collection is a registration for one record type. Its slug matches the record type field, and it carries:

  • A display name, description, and icon for the dashboard.
  • An optional metadata schema describing the fields records of this type should have.
  • A validation mode: off (default), warn, or enforce.
  • A schema version and history: every schema change bumps the version and appends a history entry.

Records of a type with no collection, or a collection without a schema, behave exactly as before.

The schema dialect

Collection schemas use a constrained subset of JSON Schema so they stay predictable and portable:

  • The root is an object with named properties and an optional required list.
  • Property types: string, number, boolean, array, and object.
  • One level of nesting: an object property can have scalar properties, but not deeper objects.
  • Strings support enum and a format (for example date-time, email, uri).
  • Not supported: $ref, oneOf, pattern, and other advanced keywords. Schemas using them are rejected with per-issue details.
Example schema
1{
2 "type": "object",
3 "properties": {
4 "tier": { "type": "string", "enum": ["free", "pro", "enterprise"] },
5 "mrr": { "type": "number" },
6 "signedUpAt": { "type": "string", "format": "date-time" },
7 "isActive": { "type": "boolean" }
8 },
9 "required": ["tier"]
10}

Validation modes

ModeNonconforming writeRecord stamp
offProceeds, no validationUntouched
warnProceeds, response includes schemaWarningsschemaValid: false
enforceRejected with 422 schema_validation_failed and field errorsNot written

In warn and enforce mode, conforming writes stamp the record with schemaValid: true. You can filter records by the stamp with the schemaValid pseudo-field in record filters to find nonconforming records.

For a type that already has records, move through the modes in order:

  1. Register the collection. Creation defaults to mode off, so nothing changes.
  2. Infer a schema from your data. Runtype samples the most recently updated records and proposes a schema with per-field confidence. Nothing is saved.
  3. Save the schema in warn mode. Writes keep succeeding; nonconforming ones return schemaWarnings and stamp schemaValid: false.
  4. Dry-run existing records. The validate-existing check reports how many current records would fail, with examples, without writing anything.
  5. Switch to enforce. The update response includes an enforceCheck summary of existing records so you can confirm the flip is safe. From then on, bad writes fail with a 422.

Working with collections

Collections are available from every Runtype surface. The examples below walk the adoption path for a customers type.

REST

Create the collection
$curl -X POST https://api.runtype.com/v1/collections \
> -H "Authorization: Bearer $RUNTYPE_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{ "slug": "customers", "displayName": "Customers" }'
Infer a schema from existing records
$curl -X POST https://api.runtype.com/v1/collections/customers/infer-schema \
> -H "Authorization: Bearer $RUNTYPE_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{ "sample": 500 }'

The response proposes a schema plus a fields array with each field’s type, confidence, presence, and sample values. Review it, then save it:

Save the schema in warn mode
$curl -X PATCH https://api.runtype.com/v1/collections/customers \
> -H "Authorization: Bearer $RUNTYPE_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{ "schema": { "type": "object", "properties": { "tier": { "type": "string" } }, "required": ["tier"] }, "validationMode": "warn" }'
Dry-run existing records
$curl -X POST https://api.runtype.com/v1/collections/customers/validate-existing \
> -H "Authorization: Bearer $RUNTYPE_API_KEY"
Enforce
$curl -X PATCH https://api.runtype.com/v1/collections/customers \
> -H "Authorization: Bearer $RUNTYPE_API_KEY" \
> -H "Content-Type: application/json" \
> -d '{ "validationMode": "enforce" }'

Once enforcing, a nonconforming record write returns:

422 response
1{
2 "error": "schema_validation_failed",
3 "collection": "customers",
4 "schemaVersion": 2,
5 "details": [
6 {
7 "code": "REQUIRED_FIELD_MISSING",
8 "message": "Instance does not have required property \"tier\".",
9 "field": "tier",
10 "path": "metadata.tier"
11 }
12 ]
13}

SDK

TypeScript SDK
1import { RuntypeClient } from '@runtypelabs/sdk'
2
3const client = new RuntypeClient({ apiKey: process.env.RUNTYPE_API_KEY })
4
5await client.collections.create({ slug: 'customers', displayName: 'Customers' })
6
7const proposal = await client.collections.inferSchema('customers', { sample: 500 })
8if (proposal.schema) {
9 await client.collections.update('customers', {
10 schema: proposal.schema,
11 validationMode: 'warn',
12 })
13}
14
15const dryRun = await client.collections.validateExisting('customers')
16if (dryRun.failed === 0) {
17 const result = await client.collections.update('customers', { validationMode: 'enforce' })
18 console.log(result.enforceCheck)
19}

CLI

CLI
$runtype collections create --slug customers --name "Customers"
$runtype collections infer customers --output customers-schema.json
$runtype collections update customers --schema-file customers-schema.json --mode warn
$runtype collections validate customers
$runtype collections update customers --mode enforce
$runtype collections list --count

MCP

Agents connected to the Runtype MCP server can manage collections with the list_collections, get_collection, create_collection, update_collection, delete_collection, infer_collection_schema, and validate_collection_records tools. The same operations are available as runtype.listCollections(...) and friends in Code Mode.

Evolving a schema

Schema updates are classified when you save them:

  • Additive changes (new optional fields, widened enums) are always allowed.
  • Breaking changes (removing a field, adding a required field, narrowing a type or enum) are rejected while the resulting mode is enforce, with code BREAKING_SCHEMA_CHANGE_REQUIRES_WARN_MODE. Drop to warn, migrate your records, then re-enforce.

Every schema change bumps schemaVersion and appends to the history, which you can read with includeHistory=true on the get endpoint.

Collection slugs are immutable. To rename a type you register a new collection and migrate records to the new type.

Deleting a collection

Deleting a collection removes the registration only. Records of the type are not touched; they revert to schemaless behavior. Deleting records is a separate, explicit operation.

Deleting a collection permanently discards its schema and version history.

Next steps