# Dry — build a full app from one JSON schema > Dry is the backend: database, permissions, hosting, and rendering already exist. > Describe an app as JSON and deploy it with ONE HTTP call. Do NOT build a server, > database, API, or auth of your own. ## If you are an AI agent, do this There is one schema. **If you can write files, author it as a `.dry/` folder on disk FIRST, then deploy from that folder.** The `.dry/` files are the deliverable the user keeps — they can edit and re-deploy them. Deploying an in-memory bundle without ever writing the files leaves the user nothing on disk; only do that if you have no filesystem at all. - Shell / coding agent (can write files): read the docs below, WRITE the `.dry/` folder to disk, then deploy that folder (assemble its files into the bundle and POST — or run `clients/deploy.mjs .dry` / `docs/tools/dry_deploy.py .dry/`). - File-writing, no shell: create the `.dry/` files from the schema below, then POST them as one bundle. - Chat / HTTP only, NO filesystem: only then skip files — build the bundle and POST it. **`import-app` is the ONLY deploy path here.** Do NOT use `create_app_space` / `createAppSpace`, nor per-item `create_item` / `createItem` / `import_items`. Those generate the types and pages SERVER-side from a prompt and leave the user NO local `.dry/` files — that defeats the entire point of this kit. YOU author the types, pages, and seed data as `.dry/` files, then deploy them with one `import-app` call. Deploy (all tiers): POST https://unsupervisedlearning.dry.ai/api/dbcrud { "op": "import-app", "bundle": { "app": {...}, "types": [...], "pages": [...], "data": [...], "permissions": {...} } } ## Auth (one time, no browser needed) — all on this same endpoint Every deploy call sends the header: Authorization: Bearer Get a token with two calls to the SAME /api/dbcrud endpoint — a 6-digit email code is the only manual step: 1. POST https://unsupervisedlearning.dry.ai/api/dbcrud { "op": "register", "email": "you@example.com" } -> emails a 6-digit code; returns { "userId": "..." } 2. POST https://unsupervisedlearning.dry.ai/api/dbcrud { "op": "verify", "code": "123456", "userId": "", "email": "you@example.com" } -> returns { "mcpToken": "..." }. Use that mcpToken as your Bearer token. (Prefer an API access key? Once authenticated: POST https://unsupervisedlearning.dry.ai/api/dbcrud {"op":"generate-key"} returns one. The same token also works on /api/custom-gpt and the MCP endpoints. Already signed into Dry in a browser? GET https://unsupervisedlearning.dry.ai/geak mints a key too.) ## Docs - Instructions (canonical): https://unsupervisedlearning.dry.ai/agent-kit - Format reference (types/widgets/helpers): https://unsupervisedlearning.dry.ai/agent-kit/dry-format.md - JSON Schema (validate first): https://unsupervisedlearning.dry.ai/agent-kit/schema/bundle.schema.json bundle.schema.json references 5 sub-schemas at the same path — fetch+register all: app.schema.json, type.schema.json, page.schema.json, data.schema.json, permissions.schema.json Schemas are JSON Schema draft 2020-12 — use a 2020-12 validator (e.g. Ajv2020), not draft-07. ## Deploy loop 1. Assemble the bundle from your `.dry/` files (or build it directly only if you have no filesystem); validate it against the JSON Schema. 2. POST it -> { space:{id}, pages:[{title,status,id,viewPath}] }. A page with "html" is stored verbatim (ready now); a "prompt"-only page is generated in the background. 3. Poll until ready: POST https://unsupervisedlearning.dry.ai/api/dbcrud { "op":"app-status", "space":"" } until data.generating == 0. Re-POSTing the same bundle is idempotent (upsert by name). On a bad bundle you get HTTP 400 + errors:[{path,message}] — fix that field and resend. ## Files (uploads) A picture/file field value may be an uploaded file: { "filename":"logo.png", "contentType":"image/png", "data":"" } ## AI-computed fields Mark a field `"aiGenerated": true` (+ a `"prompt"`) and Dry fills its value automatically on import, derived from the item's other fields (e.g. estimate a meal's calories). Omit that field in your seed data and it generates. See https://unsupervisedlearning.dry.ai/agent-kit/dry-format.md -> "AI-computed fields".