The Coline App SDK: apps, tools, file types and surfaces that run inside Coline workspaces, and public websites with Coline Sites. TypeScript, with an in-memory test workspace. MIT.
TypeScript SDK for building apps on Coline.
npm install @colineapp/sdk
import { ColineApp, ui, actions, createHandler } from "@colineapp/sdk";
const app = new ColineApp({
key: "com.acme.todos",
name: "Acme Todos",
description: "Track todos inside Coline.",
permissions: ["app.home.read", "files.read", "files.write"],
hosting: { mode: "external", baseUrl: "https://your-app.example.com" },
});
// Define a file type your app owns
app.defineFileType({
typeKey: "todo",
name: "Todo",
description: "A todo item",
storage: "coline_document",
indexable: true,
homeRenderMode: "cached",
});
// Render the app's home screen
app.onHomeRender((ctx) =>
ui.stack([
ui.heading("Todos"),
ui.text(`Workspace: ${ctx.workspace.name}`),
ui.button("New Todo", {
action: actions.createFile({
name: "Untitled Todo",
typeKey: "todo",
document: { status: "open" },
}),
}),
]),
);
// Render an individual file
app.onFileRender((ctx) =>
ui.stack([
ui.heading(ctx.file.title ?? "Todo"),
ui.badge(String(ctx.document["status"] ?? "open")),
]),
);
The SDK gives you one handler that routes all Coline traffic:
const handler = createHandler({
app,
secret: process.env.COLINE_DELIVERY_SECRET!,
onWebhook: (event) => {
console.log("Webhook:", event.type);
},
});
Mount it on any framework:
// Hono
const server = new Hono();
server.all("/coline/*", (c) => handler(c.req.raw));
// Next.js App Router (app/coline/[...path]/route.ts)
export const GET = handler;
export const POST = handler;
// Cloudflare Workers / Bun / Deno
export default { fetch: handler };
// Express (with raw body parsing)
app.all("/coline/*", async (req, res) => {
const response = await handler(toWebRequest(req));
res.status(response.status).json(await response.json());
});
Start your app, then go to coline.app/developers/apps/new.
http://localhost:3100)GET /coline/manifestThat's it. The console pulls your manifest, permissions, file types, and notification channels directly from the SDK — no copy-pasting JSON.
For store-listed apps, submit for review from the app detail page. For private apps, install directly into your workspace.
createHandler() handles these automatically:
| Route | Method | Purpose |
|---|---|---|
/coline/manifest | GET | Returns your app manifest (used by the developer console) |
/coline/render/home | POST | Renders your app's home screen |
/coline/render/file | POST | Renders a file owned by your app |
/coline/actions | POST | Handles UI action callbacks (button clicks, etc.) |
/coline/events | POST | Receives webhook events |
All POST routes are verified using your delivery secret.
For server-to-server calls (creating files, sending notifications, etc.):
import { ColineApiClient } from "@colineapp/sdk";
const client = new ColineApiClient({
baseUrl: "https://coline.app",
apiKey: process.env.COLINE_API_KEY!,
});
// Fluent scoped handles
const ws = client.workspace("acme");
const app = ws.app("com.acme.todos");
// Create a file
const { file } = await app.createFile({
name: "Buy milk",
typeKey: "todo",
document: { status: "open" },
});
// Update it
await app.file(file.id).updateDocument({ status: "done" });
// Send a notification
await app.sendNotification({
channelKey: "updates",
title: "Todo completed",
body: "Buy milk is done!",
recipients: [{ userId: "user_123" }],
});
Verify and handle webhook events with typed payloads:
import { webhooks } from "@colineapp/sdk";
// In your webhook handler:
const event = await webhooks.constructEvent(rawBody, request.headers, secret);
switch (event.type) {
case "message.created":
console.log(event.data.plaintext);
break;
case "task.created":
console.log(event.data.title, event.data.priority);
break;
}
Or use createHandler({ onWebhook }) for automatic verification.
Build native Coline UI with the ui helper:
import { ui, actions } from "@colineapp/sdk";
ui.heading("Title");
ui.text("Body text");
ui.badge("Status");
ui.button("Click me", { action: actions.openFile("file_123") });
ui.stack([...components], { gap: "md" });
ui.row([...components], { gap: "sm" });
ui.divider();
ui.image({ src: "https://...", alt: "Photo" });
For direct spec-driven access:
import { createAppPlatformClient } from "@colineapp/sdk";
const client = createAppPlatformClient({
baseUrl: "https://coline.app",
apiKey: process.env.COLINE_API_KEY!,
});
const { data } = await client.GET("/api/v1/apps");
pnpm sdk:generate:openapi