# Create a project

Create a project, optionally binding a connected repository. Reusing the same clientOperationId makes retries idempotent.

`POST /api/projects`

Required permission: `project:create`.

## Parameters

| Name | In | Type | Required | Description |

| --- | --- | --- | --- | --- |

| `Idempotency-Key` | header | `string` | No | Required for writes authenticated by a service credential and for service-account lifecycle writes. Other session writes may opt in. Reuse the key and payload for retries. Settled keys remain reserved for at least seven days; a transient 4xx releases its key. Changed payloads return 409. Match any legacy clientOperationId/clientMessageId in the body. |

## Request body

| Field | Type | Required | Constraints | Description |
| --- | --- | --- | --- | --- |
| `name` | `string` | Yes | min length 1, pattern \S | — |
| `description` | `string \| null` | No | — | — |
| `defaultBranch` | `string \| null` | No | — | — |
| `repositoryId` | `string \| null` | No | min length 1, pattern \S | — |
| `sourceControlConnectionId` | `string` | No | min length 1, pattern \S | — |
| `baseBranch` | `string \| null` | No | min length 1, pattern \S | — |
| `previewPort` | `integer \| string` | No | — | Preview port from 3000 to 9999. Numeric strings are accepted and normalized to an integer. |
| `previewPort.Option 1` | `integer` | No | min 3000, max 9999 | — |
| `previewPort.Option 2` | `string` | No | pattern ^[3-9][0-9]{3}$ | — |
| `prebuildsEnabled` | `boolean` | No | — | — |
| `prebuildsDeferred` | `boolean` | No | true | Create the project with prebuilds off without recording a user opt-out, so the first successful workspace setup may still turn them on. |
| `setupScript` | `string \| null` | No | — | — |
| `runScript` | `string \| null` | No | — | — |
| `archiveScript` | `string \| null` | No | — | — |
| `instructions` | `string \| null` | No | max length 100000 | — |
| `defaultModel` | `string \| null` | No | min length 1, max length 1024, pattern \S | — |
| `reasoningEffort` | `string \| null` | No | off · none · minimal · low · medium · high · xhigh · max ·  | — |
| `agentSpeed` | `string \| null` | No | standard · fast ·  | — |
| `prReviewAutofixDefault` | `boolean \| null` | No | — | — |
| `clientOperationId` | `string` | No | min length 1, max length 64, pattern \S | — |

```json
{
  "name": "Storefront",
  "description": "Customer-facing web application",
  "repositoryId": "repo_987654321",
  "baseBranch": "main",
  "clientOperationId": "project-import-2026-08-10"
}
```

## Example request

```bash
curl --request POST \
  --url https://api.hoplite.sh/api/projects \
  --header "X-Api-Key: $HOPLITE_API_KEY" \
  --header 'Idempotency-Key: project-import-2026-08-10' \
  --header 'Content-Type: application/json' \
  --data '{
  "name": "Storefront",
  "description": "Customer-facing web application",
  "repositoryId": "repo_987654321",
  "baseBranch": "main",
  "clientOperationId": "project-import-2026-08-10"
}'
```

## 201 response

The project was created.

Content type: `application/json`. Response type: `ProjectResponse`.

| Field | Type | Required | Constraints | Description |
| --- | --- | --- | --- | --- |
| `ok` | `boolean` | Yes | constant true | — |
| `project` | `Project` | Yes | — | — |
| `project.id` | `string` | Yes | — | — |
| `project.name` | `string` | Yes | — | — |
| `project.description` | `string \| null` | No | — | — |
| `project.taskCode` | `string` | No | — | — |
| `project.repos` | `RepoRef[]` | No | — | — |
| `project.repos.repoFullName` | `string` | Yes | min length 1, pattern \S | — |
| `project.repos.branch` | `string` | No | min length 1, pattern \S | — |
| `project.githubInstallationId` | `string` | No | — | — |
| `project.defaultBranch` | `string \| null` | No | — | — |
| `project.previewPort` | `integer` | Yes | min 3000, max 9999 | — |
| `project.setupScript` | `string \| null` | No | — | — |
| `project.runScript` | `string \| null` | No | — | — |
| `project.archiveScript` | `string \| null` | No | — | — |
| `project.instructions` | `string \| null` | No | — | — |
| `project.defaultModel` | `string \| null` | No | — | — |
| `project.reasoningEffort` | `string \| null` | No | off · none · minimal · low · medium · high · xhigh · max ·  | — |
| `project.agentSpeed` | `string \| null` | No | standard · fast ·  | — |
| `project.prReviewAutofixDefault` | `boolean \| null` | No | — | — |
| `project.sandboxSpec` | `ProjectSandboxSpec \| null` | No | — | — |
| `project.sandboxSpec.Option 1` | `ProjectSandboxSpec` | No | — | — |
| `project.sandboxSpec.Option 1.defaultCpu` | `integer \| null` | No | min 1 | — |
| `project.sandboxSpec.Option 1.defaultMemoryGiB` | `integer \| null` | No | min 1 | — |
| `project.sandboxSpec.Option 1.maxCpu` | `integer \| null` | No | min 1 | — |
| `project.sandboxSpec.Option 1.maxDiskGiB` | `integer \| null` | No | min 1 | — |
| `project.sandboxSpec.Option 1.maxMemoryGiB` | `integer \| null` | No | min 1 | — |
| `project.sandboxSpec.Option 1.staffMaxCpu` | `integer \| null` | No | min 1 | — |
| `project.sandboxSpec.Option 1.staffMaxDiskGiB` | `integer \| null` | No | min 1 | — |
| `project.sandboxSpec.Option 1.staffMaxMemoryGiB` | `integer \| null` | No | min 1 | — |
| `project.sandboxSpec.Option 1.runtimeProfile` | `string \| null` | No | docker-compose ·  | — |
| `project.sandboxSpec.Option 2` | `null` | No | — | — |
| `project.framework` | `string \| null` | No | — | — |
| `project.prebuildsEnabled` | `boolean` | No | — | — |
| `project.prebuildsPreference` | `string \| null` | No | enabled · disabled ·  | Explicit user intent behind prebuildsEnabled. null means the platform decided (automatic enablement after a successful workspace setup detected a supported lockfile). |
| `project.createdAt` | `string<date-time>` | No | — | — |
| `project.updatedAt` | `string<date-time>` | No | — | — |

```json
{
  "ok": true,
  "project": {
    "id": "proj_01JQ75",
    "name": "Storefront",
    "description": "Customer-facing web application",
    "defaultBranch": "main",
    "previewPort": 3000
  }
}
```

## Response headers

| Header | Type | Statuses | Description |

| --- | --- | --- | --- |

| `RateLimit` | `string` | `201`, `400`, `401`, `402`, `403`, `409`, `413`, `429`, `500`, `501`, `503` | Current ingress policy, remaining requests, and seconds until reset, for example `"ingress";r=599;t=60`. |

| `RateLimit-Policy` | `string` | `201`, `400`, `401`, `402`, `403`, `409`, `413`, `429`, `500`, `501`, `503` | Ingress quota and window, for example `"ingress";q=600;qu="requests";w=60`. |

| `X-RateLimit-Limit` | `integer` | `201`, `400`, `401`, `402`, `403`, `409`, `413`, `429`, `500`, `501`, `503` | Maximum requests allowed in the current rolling window. |

| `X-RateLimit-Remaining` | `integer` | `201`, `400`, `401`, `402`, `403`, `409`, `413`, `429`, `500`, `501`, `503` | Requests remaining in the current rolling window. |

| `X-RateLimit-Reset` | `integer` | `201`, `400`, `401`, `402`, `403`, `409`, `413`, `429`, `500`, `501`, `503` | Unix timestamp when the current rolling window resets. |

| `x-request-id` | `string` | `201`, `400`, `401`, `402`, `403`, `409`, `413`, `429`, `500`, `501`, `503` | Request correlation ID. |

| `Retry-After` | `integer` | `429`, `503` | Seconds until another request should be attempted. |

| `X-Retry-After` | `integer` | `429`, `503` | Legacy retry delay in seconds. |

## Status codes

- `201` — The project was created.

- `400` — The project payload or selected model is invalid.

- `401` — The API key is missing, invalid, or lacks the operation's permission.

- `402` — The organization does not have access to the requested product capability.

- `403` — The key's current user no longer has the required organization membership or role.

- `409` — The idempotency key conflicts with an existing project operation.

- `413` — The JSON request body exceeds 1 MiB.

- `429` — The API-key rate limit has been exceeded.

- `500` — The API encountered an unexpected failure.

- `501` — The deployment has not configured the requested capability.

- `503` — A required API dependency, including the rate limiter, is unavailable.