Coline Docs
App SDK

Build an app

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

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, @colineapp/ui and 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: 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 with the apps.write scope instead.

1. Scaffold

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

Pick a template with --template:

TemplateYou get
backendless (default)A hosted React app with a file type, a Kairo tool and UI examples
editorA file type with a React block editor: autosave, conflict handling, previews and search
integrationA GitHub OAuth connection, a webhook endpoint, a resumable sync job, an hourly schedule and a dashboard
external-backendTools and screens served by your own Node server, signed by Coline
siteA public website with visitor rules, a site tool and a home screen for the team. Deploys without an account; see 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.

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.

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

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.

Next

On this page