standards
Quick startIntroduction

Quick start

Five minutes from an empty workspace to a typed record, with one API key, one object, one sync and one write.


You need a Standards workspace, Node 18 or newer, and a terminal. Everything below runs on a server or on your machine, never in a browser.

Create an API key

In the Standards app, open the API keys area and create one. Copy the secret when it appears; it is shown once. The key inherits your roles; syncing a schema needs architect:update.

Keep the key in an environment variable. createStandards refuses an API key inside browser code.

Install the SDK

pnpm add @stndrds/client

One package: the builders, createStandards, defineSchemaSource and the error classes are all exported from @stndrds/client.

Declare an object

src/standards/schema.ts
import { defineSchemaSource, object, select, text } from "@stndrds/client";

export const incident = object({ name: "incidents", label: "Incident" })
  .attribute(text({ name: "title", label: "Title" }).required())
  .attribute(
    select({ name: "severity", label: "Severity" }).options([
      { value: "minor", label: "Minor" },
      { value: "major", label: "Major" },
    ]),
  )
  .labelExpression("{{ title }}");

export const source = defineSchemaSource("quickstart", { objects: [incident] });

One file holds your schema. .labelExpression() is mandatory: it is how a record is displayed in lists and relations, and build() throws without it.

Sync it

src/standards/client.ts
import { createStandards } from "@stndrds/client";
import { source } from "./schema";

export const standards = createStandards({
  baseUrl: "https://api.standards.new/v1",
  apiKey: process.env.STANDARDS_API_KEY!,
});

const result = await standards.schema.sync(source);
// first run:  { applied: true,  hash: "3f9c…" }
// no change:  { applied: false, hash: "3f9c…" }  one GET, nothing written

Open the app. incidents now has a table, a form and your two attributes. Objects you push this way are system objects: your users can add attributes and views on top, but cannot delete or retype yours. Try it now: on the object, use Add attribute to add a resolvedAt date, then save the table filtered on it as a new view. Run the sync again: { applied: false }, and both are still there.

Write and read a record

const incidents = standards.from(incident);

await incidents.create({ title: "API latency", severity: "major" });

const { records } = await incidents
  .eq("severity", "major")
  .orderBy("createdAt", "desc")
  .limit(10)
  .fetch();

severity only accepts "minor" or "major"; TypeScript tells you before the API does. Every record comes back with createdAt and updatedAt revived as Date.

The same object, from the other doors

You did not build an API. It is already there, and the CLI and MCP see the object you just synced.

From the CLI, save the instance once, then list the records:

standards login --url https://api.standards.new/v1 --name prod --key stndrds_your_api_key
standards records list incidents --format table

From an agent, connect Claude Code to the workspace, then ask it to list the major incidents:

claude mcp add --transport http standards https://api.standards.new/v1/mcp

Next steps

  • Records: every filter, sort and pagination option on a typed query.
  • Sync: what a sync changes, what it never touches, and how conflicts are reported.
  • Status page cookbook: the same three steps grown into a deployed app.