---
title: Projects
description: Manage your OpenPanel projects programmatically. Create, read, update, and delete projects using the Manage API.
---

## Authentication

To authenticate with the Projects API, you need to use your `clientId` and `clientSecret` from a root client. Root clients have organization-wide access.

For detailed authentication information, see the [Authentication](/docs/api/authentication) guide.

Include the following headers with your requests:
- `openpanel-client-id`: Your OpenPanel root client ID
- `openpanel-client-secret`: Your OpenPanel root client secret

## Base URL

All Projects API requests should be made to:

```
https://api.openpanel.dev/manage/projects
```

## Endpoints

### List Projects

Retrieve all projects in your organization.

```
GET /manage/projects
```

#### Example Request

```bash
curl 'https://api.openpanel.dev/manage/projects' \
  -H 'openpanel-client-id: YOUR_ROOT_CLIENT_ID' \
  -H 'openpanel-client-secret: YOUR_ROOT_CLIENT_SECRET'
```

#### Response

```json
{
  "data": [
    {
      "id": "my-project",
      "name": "My Project",
      "organizationId": "org_123",
      "domain": "https://example.com",
      "cors": ["https://example.com", "https://www.example.com"],
      "crossDomain": false,
      "allowUnsafeRevenueTracking": false,
      "filters": [],
      "types": ["website"],
      "eventsCount": 0,
      "createdAt": "2024-01-15T10:30:00.000Z",
      "updatedAt": "2024-01-15T10:30:00.000Z",
      "deleteAt": null
    }
  ]
}
```

### Get Project

Retrieve a specific project by ID.

```
GET /manage/projects/{id}
```

#### Path Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | string | The ID of the project |

#### Example Request

```bash
curl 'https://api.openpanel.dev/manage/projects/my-project' \
  -H 'openpanel-client-id: YOUR_ROOT_CLIENT_ID' \
  -H 'openpanel-client-secret: YOUR_ROOT_CLIENT_SECRET'
```

#### Response

```json
{
  "data": {
    "id": "my-project",
    "name": "My Project",
    "organizationId": "org_123",
    "domain": "https://example.com",
    "cors": ["https://example.com"],
    "crossDomain": false,
    "allowUnsafeRevenueTracking": false,
    "filters": [],
    "types": ["website"],
    "eventsCount": 0,
    "createdAt": "2024-01-15T10:30:00.000Z",
    "updatedAt": "2024-01-15T10:30:00.000Z",
    "deleteAt": null
  }
}
```

### Create Project

Create a new project in your organization. A default write client is automatically created with the project.

```
POST /manage/projects
```

#### Request Body

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | Yes | Project name (minimum 1 character) |
| `domain` | string \| null | No | Primary domain for the project (URL format or empty string) |
| `cors` | string[] | No | Array of allowed CORS origins (default: `[]`) |
| `crossDomain` | boolean | No | Enable cross-domain tracking (default: `false`) |
| `types` | string[] | No | Project types: `website`, `app`, `backend` (default: `[]`) |

#### Project Types

- `website`: Web-based project
- `app`: Mobile application
- `backend`: Backend/server-side project

#### Example Request

```bash
curl -X POST 'https://api.openpanel.dev/manage/projects' \
  -H 'openpanel-client-id: YOUR_ROOT_CLIENT_ID' \
  -H 'openpanel-client-secret: YOUR_ROOT_CLIENT_SECRET' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "My New Project",
    "domain": "https://example.com",
    "cors": ["https://example.com", "https://www.example.com"],
    "crossDomain": false,
    "types": ["website"]
  }'
```

#### Response

```json
{
  "data": {
    "id": "my-new-project",
    "name": "My New Project",
    "organizationId": "org_123",
    "domain": "https://example.com",
    "cors": ["https://example.com", "https://www.example.com"],
    "crossDomain": false,
    "allowUnsafeRevenueTracking": false,
    "filters": [],
    "types": ["website"],
    "eventsCount": 0,
    "createdAt": "2024-01-15T10:30:00.000Z",
    "updatedAt": "2024-01-15T10:30:00.000Z",
    "deleteAt": null,
    "client": {
      "id": "fa0c2780-55f2-4d9e-bea0-da2e02c7b1a9",
      "secret": "sec_6c8ae85a092d6c66b242"
    }
  }
}
```

**Important**: The `client.secret` is only returned once when the project is created. Store it securely immediately.

### Update Project

Update an existing project's configuration.

```
PATCH /manage/projects/{id}
```

#### Path Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | string | The ID of the project |

#### Request Body

All fields are optional. Only include fields you want to update.

| Parameter | Type | Description |
|-----------|------|-------------|
| `name` | string | Project name (minimum 1 character) |
| `domain` | string \| null | Primary domain (URL format, empty string, or null) |
| `cors` | string[] | Array of allowed CORS origins |
| `crossDomain` | boolean | Enable cross-domain tracking |
| `allowUnsafeRevenueTracking` | boolean | Allow revenue tracking without client secret |

#### Example Request

```bash
curl -X PATCH 'https://api.openpanel.dev/manage/projects/my-project' \
  -H 'openpanel-client-id: YOUR_ROOT_CLIENT_ID' \
  -H 'openpanel-client-secret: YOUR_ROOT_CLIENT_SECRET' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Updated Project Name",
    "crossDomain": true,
    "allowUnsafeRevenueTracking": false
  }'
```

#### Response

```json
{
  "data": {
    "id": "my-project",
    "name": "Updated Project Name",
    "organizationId": "org_123",
    "domain": "https://example.com",
    "cors": ["https://example.com"],
    "crossDomain": true,
    "allowUnsafeRevenueTracking": false,
    "filters": [],
    "types": ["website"],
    "eventsCount": 0,
    "createdAt": "2024-01-15T10:30:00.000Z",
    "updatedAt": "2024-01-15T11:00:00.000Z",
    "deleteAt": null
  }
}
```

### Delete Project

Soft delete a project. The project will be scheduled for deletion after 24 hours.

```
DELETE /manage/projects/{id}
```

#### Path Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `id` | string | The ID of the project |

#### Example Request

```bash
curl -X DELETE 'https://api.openpanel.dev/manage/projects/my-project' \
  -H 'openpanel-client-id: YOUR_ROOT_CLIENT_ID' \
  -H 'openpanel-client-secret: YOUR_ROOT_CLIENT_SECRET'
```

#### Response

```json
{
  "success": true
}
```

**Note**: Projects are soft-deleted. The `deleteAt` field is set to 24 hours in the future. You can cancel deletion by updating the project before the deletion time.

## Error Handling

The API uses standard HTTP response codes. Common error responses:

### 400 Bad Request

```json
{
  "error": "Bad Request",
  "message": "Invalid request body",
  "details": [
    {
      "path": ["name"],
      "message": "String must contain at least 1 character(s)"
    }
  ]
}
```

### 401 Unauthorized

```json
{
  "error": "Unauthorized",
  "message": "Manage: Only root clients are allowed to manage resources"
}
```

### 404 Not Found

```json
{
  "error": "Not Found",
  "message": "Project not found"
}
```

### 429 Too Many Requests

Rate limiting response includes headers indicating your rate limit status.

## Rate Limiting

The Projects API implements rate limiting:
- **20 requests per 10 seconds** per client
- Rate limit headers included in responses
- Implement exponential backoff for retries

## Notes

- Project IDs are automatically generated from the project name using a slug format
- If a project ID already exists, a numeric suffix is added
- CORS domains are automatically normalized (trailing slashes removed)
- The default client created with a project has `type: 'write'`
- Projects are scoped to your organization - you can only manage projects in your organization
- Soft-deleted projects are excluded from list endpoints
