# Build an app

> Scaffold, define, test and push your first Coline app.

Source: https://docs.coline.app/sdk

A Coline app installs into a workspace as a set of capabilities: tools that Kairo and people can run, file types with their own previews and editors, home screens, schedules and more. Coline supplies sign-in, members, permissions, files, search, notifications, realtime, AI and billing, so an app doesn't build any of that. Apps run on Coline by default, with no servers to deploy.

The packages are open source under MIT: [`@colineapp/sdk`](https://www.npmjs.com/package/@colineapp/sdk), [`@colineapp/ui`](https://www.npmjs.com/package/@colineapp/ui) and [`create-coline-app`](https://www.npmjs.com/package/create-coline-app).

The current release is SDK 2.1: `@colineapp/sdk` 2.1.3, `@colineapp/ui` 2.1.4, `@colineapp/app-runtime` 2.1.0 and `create-coline-app` 2.9.5. SDK 2 brought the app model on this page: one `defineApp` for permissions, surfaces, tools, file types, schedules, jobs, endpoints and connected accounts, with every call checked against what the workspace granted. SDK 2.1 adds [Coline Sites](/sites): any app can also be a public website, with visitor sign-in (Google or Coline), typed collections, uploads, endpoints on the site's own paths, React hooks and Coline's components in light and dark.

## Before you start

You need Node.js and npm. To push, sign in with `npx coline-app login` as an admin of the workspace. In CI, use a workspace [API key](/authentication/api-keys) with the `apps.write` scope instead.

## 1\. Scaffold

```bash
npx create-coline-app my-app
cd my-app && npm install && npm run check
```

Pick a template with `--template`:

| Template                | You get                                                                                                                          |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `backendless` (default) | A hosted React app with a file type, a Kairo tool and UI examples                                                                |
| `editor`                | A file type with a React block editor: autosave, conflict handling, previews and search                                          |
| `integration`           | A GitHub OAuth connection, a webhook endpoint, a resumable sync job, an hourly schedule and a dashboard                          |
| `external-backend`      | Tools and screens served by your own Node server, signed by Coline                                                               |
| `site`                  | A public website with visitor rules, a site tool and a home screen for the team. Deploys without an account; see [Sites](/sites) |

Every project includes an `AGENTS.md` and an agent skill with the full app contract, so coding agents can build with it.

## 2\. Define the app

`app.config.ts` is the whole contract: permissions, surfaces, tools, file types and handlers.

```ts
import { z } from "zod/v4";
import { defineApp, defineFileType, defineTool, ui } from "@colineapp/sdk/v2";

const todoFileType = defineFileType({
  metadata: {
    typeKey: "todos.todo",
    name: "Todo",
    indexable: true,
    surfaces: { preview: { tier: "tree" }, inline: { tier: "tree" } },
  },
  runtime: {
    renderPreview: (ctx) =>
      ui.stack([ui.heading(ctx.file.name), ui.badge(String(ctx.document.status ?? "open"))]),
    renderInline: (ctx) => ui.text(ctx.file.name),
    index: {
      title: (doc) => String(doc.title ?? "Todo"),
      body: (doc) => String(doc.notes ?? ""),
    },
  },
});

const createTodo = defineTool({
  name: "todos.create_todo",
  description: "Create a todo with a title.",
  input: z.object({ title: z.string().min(1) }),
  effect: "write", // enforced as a ceiling at runtime
  execute: async (input, ctx) => {
    const file = await ctx.coline.files.create({
      typeKey: "todos.todo",
      name: input.title,
      document: { title: input.title, status: "open" },
    });
    return {
      output: { fileId: file.fileId },
      card: ui.reference({ kind: "file", id: file.fileId }),
    };
  },
});

export default defineApp({
  key: "todos",
  name: "Todos",
  permissions: ["files.read", "files.write", "ai.tools", "search.index"],
  hosting: { default: "coline" },
  surfaces: { home: { tier: "tree" } },
  files: [todoFileType],
  tools: [createTodo],
  handlers: {
    renderHome: async (ctx) => {
      const { files } = await ctx.coline.files.list({ typeKey: "todos.todo" });
      return ui.stack([
        ui.heading("Todos"),
        ...files.map((file) => ui.fileCard({ title: file.name, fileId: file.fileId })),
      ]);
    },
  },
});
```

One definition produces two artifacts. The **manifest** says what the app may do; it's what reviewers and workspace admins approve. The **runtime bundle** is what the app does. Every call it makes is checked against the manifest, the workspace's grants, the acting person's access and the tool's declared effect.

## 3\. Test

`@colineapp/sdk/testing` runs the app against an in-memory workspace with the same API and permission checks as production.

```ts
import { expect, it } from "vitest";
import { createTestWorkspace } from "@colineapp/sdk/testing";
import app from "./app.config";

it("creates a todo through the Kairo tool", async () => {
  const ws = createTestWorkspace(app);
  const result = await ws.invokeTool("todos.create_todo", { title: "Ship it" });
  expect(result.card).not.toBeNull();
  expect(ws.files.byType("todos.todo")).toHaveLength(1);
});
```

`npm run check` validates types, the manifest, the logic bundle and the React bundle without uploading anything.

## 4\. Push

```bash
npx coline-app login      # once: opens a browser, pick the workspace
npx coline-app push
```

The CLI builds the logic bundle, ships client source for React surfaces, and uploads a version. It lands as a draft that you can install privately in your workspace right away, or submit for review to list in the App Store. See [develop and publish](/sdk/publishing).

## Next

<Cards>
  <Card title="How apps run" description="UI tiers and where your logic runs." href="/sdk/runtime" />

  <Card title="Capability API" description="Files, storage, members, notifications and the network." href="/sdk/capabilities" />

  <Card title="UI" description="Tree surfaces and React surfaces." href="/sdk/ui" />

  <Card title="Full stack" description="Schedules, events, webhooks, jobs and connected accounts." href="/sdk/full-stack" />

  <Card title="Sites" description="Put the app on the web at its own address, with visitors." href="/sites" />
</Cards>
