# How to Create a New Kaneo Project: Complete API and Web Client Guide

> Create a new Kaneo project using the API. Learn how to send a POST request to /api/project with your project name and workspace ID. Get started with Kaneo today.

- Repository: [kaneo.app/kaneo](https://github.com/usekaneo/kaneo)
- Tags: how-to-guide
- Published: 2026-08-05

---

**You create a new Kaneo project by sending a `POST` request to `/api/project` with a `name` and `workspaceId`, authenticated via Bearer token.**

In Kaneo, a **project** serves as the top-level container for tasks, columns, and labels. Whether you're building a custom integration or extending the web interface, understanding the project creation flow is essential. This guide walks through the API endpoint, database schema, and React hooks that power this feature in the [usekaneo/kaneo](https://github.com/usekaneo/kaneo) open-source project management platform.

## API Endpoint: Creating Projects via HTTP

The canonical way to create a new Kaneo project is through the REST API endpoint defined in [`apps/api/src/project/index.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/project/index.ts).

### Request Requirements

| Requirement | Details |
|-------------|---------|
| Method | `POST` |
| URL | `/api/project` |
| Authentication | Bearer token (device flow or API key) |
| Content-Type | `application/json` |

### Request Payload

The payload is validated using **Valibot** and requires two fields:

- **`name`** (string): Display name for the project
- **`workspaceId`** (string): CUID2 identifier of the parent workspace

### cURL Example

```bash
curl -X POST "$KANEO_API_URL/api/project" \
  -H "Authorization: Bearer $YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name":"Q4 Marketing Campaign","workspaceId":"cuid-workspace-123"}'

```

### Expected Response

```json
{
  "id": "cuid-project-xyz",
  "name": "Q4 Marketing Campaign",
  "workspaceId": "cuid-workspace-123",
  "createdAt": "2026-08-05T12:34:56.789Z",
  "updatedAt": "2026-08-05T12:34:56.789Z"
}

```

The endpoint is registered with **Hono** and protected by Better Auth middleware as defined in [`apps/api/src/auth/middleware.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/auth/middleware.ts). No request reaches the project creation logic without passing authentication.

## Database Schema: How Projects Are Stored

Projects persist in PostgreSQL through the Drizzle ORM schema defined in [`apps/api/src/database/schema.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/database/schema.ts). The `projectTable` structure includes:

- **Primary key**: CUID2 auto-generated identifier
- **Timestamps**: `createdAt` and `updatedAt` with automatic updates
- **Foreign key**: `workspaceId` referencing `workspaceTable` with `onDelete: cascade` and `onUpdate: cascade`

This cascade behavior ensures that deleting a workspace automatically removes all associated projects, maintaining referential integrity without manual cleanup.

## Web Client Integration: React Hooks and Fetchers

The Kaneo web application abstracts API calls into typed fetchers and TanStack Query mutations.

### Fetcher Layer

The `createProject` function in [`apps/web/src/fetchers/project/create-project.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/web/src/fetchers/project/create-project.ts) wraps the API client:

```typescript
import { apiClient } from "@/lib/api-client";

export async function createProject(data: {
  name: string;
  workspaceId: string;
}) {
  const response = await apiClient.post("/project", data);
  return response.json();
}

```

### React Mutation Hook

The `useCreateProject` hook in [`apps/web/src/hooks/mutations/project/use-create-project.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/web/src/hooks/mutations/project/use-create-project.ts) exposes this as a full-featured mutation with loading states, error handling, and automatic cache invalidation:

```tsx
import { useCreateProject } from "@/hooks/mutations/project/use-create-project";

function NewProjectButton({ workspaceId }: { workspaceId: string }) {
  const createProject = useCreateProject();

  const handleClick = async () => {
    await createProject.mutateAsync({ 
      name: "Website Redesign", 
      workspaceId 
    });
  };

  return (
    <button 
      onClick={handleClick} 
      disabled={createProject.isLoading}
    >
      {createProject.isLoading ? "Creating…" : "Create Project"}
    </button>
  );
}

```

**Key behaviors:**
- `isLoading` provides immediate UI feedback
- `mutateAsync` returns a Promise for sequential operations
- On success, the project list query invalidates automatically

## Running the Complete Stack Locally

Kaneo uses **Turborepo** to orchestrate the monorepo. Start both API and web servers:

```bash
pnpm install
pnpm dev

```

This concurrently launches:
- API server at `http://localhost:1337` (configurable via `KANEO_API_URL`)
- Vite dev server for the web interface

For production deployment, reference the [`compose.yml`](https://github.com/usekaneo/kaneo/blob/main/compose.yml) at the repository root for Docker Compose configuration.

## Key Implementation Files

| Component | Path |
|-----------|------|
| API route handler | [`apps/api/src/project/index.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/project/index.ts) |
| Database schema | [`apps/api/src/database/schema.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/database/schema.ts) |
| Web API fetcher | [`apps/web/src/fetchers/project/create-project.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/web/src/fetchers/project/create-project.ts) |
| React mutation hook | [`apps/web/src/hooks/mutations/project/use-create-project.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/web/src/hooks/mutations/project/use-create-project.ts) |
| Authentication middleware | [`apps/api/src/auth/middleware.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/auth/middleware.ts) |

## Summary

- **Create a new Kaneo project** via `POST /api/project` with `name` and `workspaceId`
- **Authentication is mandatory** — include a valid Bearer token from Better Auth
- **API is fully typed** through Valibot validation and Drizzle schema
- **Web client abstracts complexity** through `createProject` fetcher and `useCreateProject` hook
- **Local development** runs both services via `pnpm dev` in Turborepo

## Frequently Asked Questions

### What authentication methods does Kaneo accept for project creation?

Kaneo accepts **Bearer tokens** from two sources: device flow authentication for interactive users and **API keys** for service accounts. The Better Auth middleware in [`apps/api/src/auth/middleware.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/auth/middleware.ts) validates tokens on every request to `/api/project`. No anonymous project creation is permitted.

### Can I create a project without using the React web interface?

Yes. The API endpoint is **client-agnostic**. Any HTTP client — curl, Python `requests`, Node.js `fetch`, or an MCP server — can POST to `/api/project` with proper authentication. The web interface is one consumer of many possible clients.

### What happens to projects when I delete a workspace?

Projects cascade automatically. The `projectTable` schema defines `onDelete: cascade` on the `workspaceId` foreign key, so deleting a workspace removes all child projects, tasks, and related data in a single database transaction.

### Does Kaneo support bulk project creation?

The core API handles single project creation. For bulk operations, implement client-side batching by calling `POST /api/project` multiple times, or extend the API route in [`apps/api/src/project/index.ts`](https://github.com/usekaneo/kaneo/blob/main/apps/api/src/project/index.ts) to accept an array payload with a custom validation schema.