Quickstart

Start Zero-1.
Describe what data your frontend needs.

~
$ ANTHROPIC_API_KEY=sk-ant-... bun run index.ts ✓ Zero-1 running → http://localhost:7777 ✓ Web console → http://localhost:7777/__generate ✓ MCP server → bun run mcp.ts
a storefront — products, customers, and orders. each order shows the customer name. 200 orders.
~
3 resources · 270 records · products 20 records · customers 50 records · orders 200 records orders.customerId → customers.id orders.customerName ← customers.name (denormalized, auto-synced)

Or wire Zero-1 into your AI agent via MCP — the agent gets every operation as a callable tool.

~/.cursor/mcp.json

  "mcpServers" 
    "z1" 
      "command" "bun"
      "args" "run" "/path/to/z1/mcp.ts"
    
  
rename name to fullName on customers
~
orders.customerName carries a copy of this field 200 records would go stale — syncing first, then renaming customers.name → fullName orders.customerName 200 records re-synced
add a shippingAddress field to orders
~
orders.shippingAddress added — 200 records seeded

It's a real API.
Curl it.

Every resource you describe becomes a live endpoint with full CRUD — GET, POST, PUT, PATCH, DELETE. IDs are auto-assigned. FK references are always valid. Data is generated from field names, not random garbage.

~
$ curl 'localhost:7777/posts?sort=likes:desc&limit=3' [ { "id": 42, "title": "How I built my side project", "likes": 2841, "published": true }, { "id": 7, "title": "10 lessons from shipping fast", "likes": 1204, "published": true }, { "id": 23, "title": "Why I chose Bun over Node", "likes": 891, "published": true } ] # Filter by field + free-text search, stacked $ curl 'localhost:7777/posts?role=admin&q=launch&sort=likes:desc&limit=10' # Create — id is auto-assigned $ curl -X POST localhost:7777/posts \ -H 'Content-Type: application/json' \ -d '{"userId": 3, "title": "My first post", "likes": 0}' { "id": 201, "userId": 3, "title": "My first post", "likes": 0 } # Partial update $ curl -X PATCH localhost:7777/posts/201 \ -H 'Content-Type: application/json' \ -d '{"likes": 42}' { "id": 201, "userId": 3, "title": "My first post", "likes": 42 } # Delete returns 204 No Content $ curl -X DELETE localhost:7777/posts/201

Filtering, sorting, text search, and pagination are all described in natural language when you set up a scenario — the query behaviour follows from how you described the data. The query API reflects exactly what you asked for.

a community feed — users, posts with upvote counts and topic tags, and comment threads. 50 users, 200 posts, 400 comments. seed it heavy.
a saas app with companies, users, and monthly billing records. power-law distribution on billing amounts — a few companies spend most of the money.
Framework Agnostic

Works with whatever
you're already using.

Zero-1 is plain HTTP — point your data layer at localhost:7777 and it works. No SDK, no adapter, no wrapper. Swap the base URL when your real backend is ready.

api.js
// Swap BASE when your real backend is ready
const BASE = 'http://localhost:7777';

// Fetch a list
const posts = await fetch(`${BASE}/posts?sort=likes:desc&limit=10`)
  .then(r => r.json());

// Create a record
const created = await fetch(`${BASE}/posts`, {
  method:  'POST',
  headers: { 'Content-Type': 'application/json' },
  body:    JSON.stringify({ userId: 1, title: 'Hello', likes: 0 }),
}).then(r => r.json());

// Delete
await fetch(`${BASE}/posts/42`, { method: 'DELETE' });
PostList.jsx
import { useState, useEffect } from 'react';

const BASE = 'http://localhost:7777';

export default function PostList() {
  const [posts,   setPosts]   = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch(`${BASE}/posts?sort=likes:desc`)
      .then(r    => r.json())
      .then(data => { setPosts(data); setLoading(false); });
  }, []);

  if (loading) return <p>Loading…</p>;
  return posts.map(p => (
    <div key={p.id}>{p.title}</div>
  ));
}
PostList.jsx
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';

const BASE = 'http://localhost:7777';

export default function PostList() {
  const qc = useQueryClient();

  const { data: posts = [], isLoading } = useQuery({
    queryKey: ['posts'],
    queryFn:  () => fetch(`${BASE}/posts?sort=likes:desc`).then(r => r.json()),
  });

  const create = useMutation({
    mutationFn: (body) => fetch(`${BASE}/posts`, {
      method:  'POST',
      headers: { 'Content-Type': 'application/json' },
      body:    JSON.stringify(body),
    }).then(r => r.json()),
    onSuccess: () => qc.invalidateQueries({ queryKey: ['posts'] }),
  });
}
PostList.vue
<script setup>
import { ref, onMounted } from 'vue';

const BASE  = 'http://localhost:7777';
const posts = ref([]);

onMounted(async () => {
  posts.value = await fetch(`${BASE}/posts?sort=likes:desc`)
    .then(r => r.json());
});

async function create(payload) {
  const post = await fetch(`${BASE}/posts`, {
    method:  'POST',
    headers: { 'Content-Type': 'application/json' },
    body:    JSON.stringify(payload),
  }).then(r => r.json());
  posts.value.unshift(post);
}
</script>
posts.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

const BASE = 'http://localhost:7777';

@Injectable({ providedIn: 'root' })
export class PostsService {
  constructor(private http: HttpClient) {}

  list(): Observable<Post[]> {
    return this.http.get<Post[]>(`${BASE}/posts?sort=likes:desc`);
  }

  create(body: Partial<Post>): Observable<Post> {
    return this.http.post<Post>(`${BASE}/posts`, body);
  }
}

Data that reacts.
Not just sits there.

Endpoints can react to each other. Describe the side-effect logic inline in your scenario — create an order and stock decrements, post a comment and the count increments, delete a user and their content is removed. Zero-1 wires it so your mock API behaves like the real thing.

i'm building a storefront. when i POST an order, subtract the quantity from the product's stock. when it hits zero, flip the status to "out_of_stock".
~
$ curl localhost:7777/products/12 { "id": 12, "name": "Wireless Headphones", "stock": 7, "status": "in_stock" } $ curl -X POST localhost:7777/orders \ -H 'Content-Type: application/json' \ -d '{"productId": 12, "quantity": 7}' { "id": 88, "productId": 12, "quantity": 7, "status": "confirmed" } $ curl localhost:7777/products/12 { "id": 12, "stock": 0, "status": "out_of_stock" } stock went from 7 → 0, status flipped automatically
when a comment is created, increment the post's commentCount. when it reaches 100, mark the post as trending.
when a user is deleted, remove all their posts and comments.

Change the schema
while it's running.

Describe what changed. Zero-1 sends only the schema to Claude (not the data), applies the diff, and backfills all existing records locally — O(records) time, flat ~1024-token API call regardless of dataset size.

~
# Add, rename, and remove fields $ curl -X POST localhost:7777/__generate/evolve \ -H 'Content-Type: application/json' \ -d '{"name": "posts", "changes": "add a views counter, rename likes to reactions, drop published"}' { "updated": 200, "added": ["views"], "renamed": { "likes": "reactions" }, "removed": ["published"] } # Add an auth guard inline — no separate /configure call needed $ curl -X POST localhost:7777/__generate/evolve \ -H 'Content-Type: application/json' \ -d '{"name": "posts", "changes": "require X-API-Key on all write requests"}' { "updated": 200, "headerRules": 1 } # Add chaos inline too $ curl -X POST localhost:7777/__generate/evolve \ -H 'Content-Type: application/json' \ -d '{"name": "orders", "changes": "add a status field (pending/confirmed/cancelled). fail 10% of POSTs with 503."}' { "updated": 80, "added": ["status"], "chaosRules": 1 }

Added fields get context-aware generated values — views gets realistic view counts, email gets real-looking addresses. Unknown fields fall back to type-appropriate random values.

Want pagination?

Describe what you need — cursor, offset, or page-based — in plain English. Every response field name is under your control. Zero-1 configures the envelope so it matches whatever your real API will produce.

i need the posts feed paginated — 20 per page, newest first. wrap in "items" and give me a "nextToken" field.
~
$ curl 'localhost:7777/posts?limit=20&sort=createdAt:desc' { "items": [ { "id": 200, "title": "Shipping without a backend", "reactions": 1204 }, { "id": 199, "title": "Why I prototype with mock APIs", "reactions": 892 }, ... ], "nextToken": "eyJpZCI6MTgwfQ", "total": 200 }
cursor-based pagination on products, 20 per page, use after as cursor param, edges for data array.
page-based, 25 users per page, page number as param, results array, include totalPages and total count.

Need a header guard
on a route?

Describe the rule — bearer token, API key, custom header. Zero-1 enforces it. Your frontend gets real 401s to handle. Guards can also inject response headers on every matching request, so you can simulate X-Request-Id, WWW-Authenticate, rate limit headers — whatever your real API sends back.

require a bearer token on POST, PATCH and DELETE. return 401 if it's missing.
~
$ curl -X POST localhost:7777/posts \ -d '{"userId": 3, "title": "test"}' { "error": "Missing or invalid Authorization header" } ← 401 $ curl -X POST localhost:7777/posts \ -H 'Authorization: Bearer my-token' \ -d '{"userId": 3, "title": "test"}' { "id": 202, "userId": 3, "title": "test", "likes": 0 } ← 201
require X-API-Key on all write requests. inject X-Request-Id and X-RateLimit-Remaining into every response.
lock down the admin endpoints with a bearer token. anyone else gets a 403 with a message saying they don't have permission.

JWT claims.
Isolate records by user.

Map a JWT claim to a resource field and Zero-1 enforces per-user record isolation automatically. No middleware to write — pass a Bearer token, get back only the records that belong to you.

~
# Declare: "for /posts, filter by userId matching the JWT sub claim" $ curl -X POST localhost:7777/__generate/scoping \ -H 'Content-Type: application/json' \ -d '{"resource": "posts", "field": "userId", "claim": "sub"}' { "id": 1, "resource": "posts", "field": "userId", "claim": "sub" } # GET with a JWT whose "sub" is "42" → only posts where userId=42 $ curl localhost:7777/posts \ -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...' # sub=42 [ { "id": 7, "userId": 42, "title": "My post" }, { "id": 15, "userId": 42, "title": "Another post" } ] # GET /posts/3 where post.userId ≠ 42 → 403 $ curl localhost:7777/posts/3 \ -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...' { "error": "Forbidden." } ← 403 # POST auto-populates the owner field from the JWT claim $ curl -X POST localhost:7777/posts \ -H 'Authorization: Bearer ...' \ -d '{"title": "New post"}' { "id": 201, "userId": 42, "title": "New post" } ← userId injected from JWT # No JWT? Scoping rules are skipped — open access $ curl localhost:7777/posts ← returns all records
MethodPathDescription
GET/__generate/scopingList all active scoping rules
POST/__generate/scopingAdd a rule: { resource, field, claim }
DELETE/__generate/scopingClear all scoping rules
DELETE/__generate/scoping/:idRemove a specific rule

Inline the parent.
Expand any FK field.

Add ?expand=field to any GET request and Zero-1 replaces the FK value with the full parent record. Multiple fields, comma-separated. No N+1 — all resolved in one pass.

~
# Without expansion $ curl localhost:7777/orders/1 { "id": 1, "userId": 5, "productId": 12, "total": 49.99 } # With expansion — FK IDs replaced with full parent records $ curl 'localhost:7777/orders/1?expand=userId,productId' { "id": 1, "userId": { "id": 5, "name": "Alice Chen", "email": "alice@example.com" }, "productId": { "id": 12, "name": "Wireless Headphones", "price": 49.99 }, "total": 49.99 } # Works on collection endpoints too $ curl 'localhost:7777/orders?expand=userId' [ { "id": 1, "userId": { "id": 5, "name": "Alice Chen" }, ... } ]

Expansion uses the live knowledge graph to resolve FK edges. If the FK target resource doesn't exist or the referenced ID is not found, the field is left as-is.

Upload a file.
Get a real URL back.

POST multipart form data to any resource endpoint. Zero-1 stores the bytes in memory, replaces the file field with a /__uploads/{id} URL, and serves the original bytes with the correct Content-Type when fetched.

~
# Upload an avatar image alongside the user record $ curl -X POST localhost:7777/users \ -F 'name=Alice Chen' \ -F 'role=admin' \ -F 'avatar=@photo.jpg' { "id": 42, "name": "Alice Chen", "role": "admin", "avatar": "http://localhost:7777/__uploads/a3f8c1d2" } # The URL serves the real image bytes $ curl localhost:7777/__uploads/a3f8c1d2 > avatar.jpg # Works on PATCH too — replace an existing file field $ curl -X PATCH localhost:7777/users/42 \ -F 'avatar=@new-photo.png' { "id": 42, "avatar": "http://localhost:7777/__uploads/b9d4e7f1" }

File bytes live in memory alongside all other Zero-1 state. They are included in fork snapshots and global export/import. Uploads are served at GET /__uploads/:id with the original Content-Type and filename.

Full OAuth 2.0 provider,
from a description.

Describe the flow. Zero-1 runs a real OAuth 2.0 / OIDC provider locally — auth code, PKCE, client credentials, scopes, refresh tokens, introspection, OIDC discovery. No JWT library, no crypto package. Token signing via Web Crypto API (HS256).

i'm testing oauth in my SPA. three scopes: read for guests, write for editors, admin for everything. use pkce.
~
# Configure from a NL description $ curl -X POST localhost:7777/__generate/oauth/configure \ -H 'Content-Type: application/json' \ -d '{"description": "authorization code flow with PKCE. scopes: read, write, admin. three clients: web-app (read/write), mobile (read), admin-tool (admin). JWT tokens, 1h expiry."}' { "flow": "authorization_code", "pkceRequired": true, "scopes": ["read","write","admin"], "clients": 3 } # Start the auth code flow — auto-approves, returns code in redirect $ curl -G 'localhost:7777/__generate/oauth/authorize' \ --data-urlencode 'response_type=code' \ --data-urlencode 'client_id=web-app' \ --data-urlencode 'redirect_uri=http://localhost:3000/callback' \ --data-urlencode 'code_challenge=abc123' \ --data-urlencode 'code_challenge_method=S256' \ --data-urlencode 'scope=read write' → 302 Location: http://localhost:3000/callback?code=xyz_auth_code # Exchange the code for a token $ curl -X POST localhost:7777/__generate/oauth/token \ -d 'grant_type=authorization_code&code=xyz_auth_code&code_verifier=my_verifier&client_id=web-app' { "access_token": "eyJhbGciOiJIUzI1NiJ9...", "token_type": "Bearer", "expires_in": 3600, "scope": "read write", "refresh_token": "rt_..." } # Introspect to verify $ curl -X POST localhost:7777/__generate/oauth/introspect \ -d 'token=eyJhbGciOiJIUzI1NiJ9...' { "active": true, "scope": "read write", "client_id": "web-app", "exp": 1735689600 } # OIDC discovery — auto-generated from config $ curl localhost:7777/__generate/oauth/.well-known/openid-configuration

Registered: /authorize · /token · /revoke · /introspect · /userinfo · /.well-known/openid-configuration · /.well-known/jwks.json. Per-client scope enforcement included.

client credentials flow for a machine-to-machine integration. scopes: data:read and data:write. two clients: service-a and service-b, each with different scope access.
hr tool oauth — employees can read, managers can read and write, admins get everything. authorization code with pkce. auto-approve the consent screen.

Make it fail
on purpose.

Inject failures, latency, and rate limits into any endpoint. Describe the chaos inline in your scenario, or add it on top of a live API. Rules are additive — latency stacks, error rates evaluate independently. Chaos never affects /__generate/* meta endpoints.

fail 20% of POST /comments with a 503. add 100–400ms latency to GET /posts.
~
$ curl -X POST localhost:7777/comments \ -d '{"postId": 42, "body": "great post"}' { "error": "Service Unavailable" } ← 503 · 20% of POSTs fail $ curl -X POST localhost:7777/comments \ -d '{"postId": 42, "body": "great post"}' { "id": 401, "postId": 42, "body": "great post" } ← 201 · retry succeeded $ curl localhost:7777/posts ← responds in 100–400ms [...]
simulate a degraded network — 30% of all requests get an extra 500ms–2s of latency. rate limit search to 5 requests per minute.
fail exactly one POST /orders with a 500 then auto-remove the rule — i want to test that my retry only fires once.
MCP · Model Context Protocol

Build your API
directly in Cursor.

Zero-1 ships a zero-config MCP server. Add it to Cursor, VS Code, or any MCP-compatible editor and create endpoints, inject chaos, fork workspaces — all from the chat panel, without leaving your editor.

1
Start Zero-1
ANTHROPIC_API_KEY=sk-ant-... bun run index.ts — server starts on port 7777.
2
Register the MCP server in Cursor
Add this to ~/.cursor/mcp.json (or .claude/mcp.json for Claude Code):
~/.cursor/mcp.json

  "mcpServers" 
    "z1" 
      "command" "bun"
      "args" "run" "/path/to/z1/mcp.ts"
    
  
3
Ask Claude to build your API
Open the Cursor chat panel and describe what you need. Claude calls Zero-1's tools directly — no copy-paste, no terminal switching.
Cursor — Claude chat
You i'm building a reviews table. i need an endpoint with productId, userId, rating 1–5, body text and a verified flag. seed 50 rows. Claude ⠸ calling create_scenario... Done. Your endpoint is live at localhost:7777/reviews Sample record: { "id": 1, "productId": 12, "userId": 4, "rating": 4, "body": "Solid build, fast shipping.", "verified": true } Point your table component at that URL. Supports ?sort=rating:desc ?q= ?limit= ?page= You add 15% slow responses so i can test my skeleton loader actually shows up. Claude ⠸ calling add_chaos... Done. GET /reviews delays 200–800ms on 15% of hits. Refresh a few times and your loader will trigger.

Fork the state.
Wreck it safely.

Clone the entire running state into an isolated workspace. Route requests with X-Workspace and only that fork mutates.

~
$ curl -X POST localhost:7777/__generate/fork \ -d '{"name": "qa-branch"}' { "id": "a7f3b2c1", "name": "qa-branch", "records": 650 } $ curl -X DELETE localhost:7777/posts/42 \ -H 'X-Workspace: a7f3b2c1' ← only fork mutates $ curl localhost:7777/posts/42 { "id": 42, "title": "How I built my side project" } ← still there

See how everything
connects.

Zero-1 maintains a live knowledge graph of every resource, foreign-key edge, and denormalized field copy it has observed. Zero config — it builds itself from traffic. Open the visual explorer or query the JSON API.

~
# Full graph dump — resources, FK edges, provenance edges $ curl localhost:7777/__generate/graph { "resources": [ { "name": "users", "fields": [ ... ] }, ... ], "fkEdges": [ { "from": "orders", "fromField": "userId", "to": "users", "toField": "id" } ], "provenanceEdges": [ { "child": "orders", "childField": "buyerName", "parent": "users", "parentField": "name", "viaFK": "userId" } ] } # Impact analysis before a schema change $ curl -X POST localhost:7777/__generate/graph/analyze \ -d '{"resource": "users", "fields": ["id"], "typeChange": {"field": "id", "fromType": "number", "toType": "string"}}' { "safe": false, "fkImpact": [ { "resource": "orders", "field": "userId", "reason": "FK references users.id" } ], "summary": "1 FK reference(s) would break: orders.userId." }

Open /__generate/graph-view for an interactive D3 force graph — click any node or edge for details, drag to rearrange, live auto-refresh every 3 seconds.

MethodPathDescription
GET/__generate/graphFull graph: nodes, FK edges, provenance edges
POST/__generate/graph/analyzeImpact report: { resource, fields[], typeChange? }
GET/__generate/graph-viewInteractive D3 visualization

Import a spec.
Export your state.

Point Zero-1 at an existing OpenAPI or Swagger spec and it imports the resources directly. Export your current state as JSON to share with a teammate or restore later.

~
$ curl -X POST localhost:7777/__generate/openapi \ -d '{"url": "https://petstore3.swagger.io/api/v3/openapi.json"}' { "imported": ["pets", "store", "users"], "records": 90 } $ curl localhost:7777/__generate/export > session.json $ curl -X POST localhost:7777/__generate/import -d @session.json { "imported": 3, "message": "State restored." }

What are you building?

Type what your project actually needs. These are examples.

i'm building a saas dashboard. companies with teams inside them, members with different permission levels. a big activity feed i can scroll through. lock everything behind an api key — and start returning 429s if they hammer the feed too hard.
storefront with products and stock levels. when someone orders something, subtract from stock — flip it to out_of_stock when it hits zero. make checkout fail about a third of the time so i can actually test my error state.
hr tool for a client demo — employees, departments, time-off requests. i need real oauth2 running locally so i can test my auth flow end to end. authorization code with pkce, a few permission scopes, auto-approve the consent.
blog with authors, posts, and comments. bump the post's comment count whenever someone comments. writes need a bearer token. and make maybe 1 in 5 comment posts fail — i've never actually tested if my retry logic works.