---
title: "Creates a project in the resolved organization."
method: POST
path: "/api/v1/orgs/{org}/projects"
tags: ["Projects"]
---

# Creates a project in the resolved organization.

`POST /api/v1/orgs/{org}/projects`

## Request

- Base URL: `https://api.featureflip.io`
- URL: `https://api.featureflip.io/api/v1/orgs/{org}/projects`
- Auth: API key in header `Authorization`

Requires at least Member. A project past the
plan's project cap is a 403 `plan_limit_reached`.

## Path parameters

- `org` string, required

## Request body

- PublicCreateProjectRequest: Request body for `POST /api/v1/orgs/{org}/projects`. Named distinctly from the dashboard's own `CreateProjectRequest` — both controllers share one Swagger document (see `Program.cs`'s `AddSwaggerGen``DocInclusionPredicate` override), and Swashbuckle's default schemaId is the bare type name, so an identical name in either namespace throws a schemaId collision at `--export-openapi` time.
  - `key` string, nullable, required
  - `name` string, nullable, required
  - `description` string, nullable

## Response `201`

Created

- ProjectResponse: Public representation of a project, returned by `GET /api/v1/orgs/{org}/projects` (list), `GET .../projects/{project}` (detail), and the body of a successful create/update. Drops the dashboard-only nested `Environments` collection from ProjectDto — the public API surfaces environments via their own endpoint (Task 6), not embedded here. UpdatedAt is only populated from ProjectDto (the detail/create shape); ProjectListItemDto doesn't carry it, so list rows leave it `null`. DefaultFlagLifetimeDays is the project's default flag lifetime in days, or null (#3194).
  - `id` string, uuid, required
  - `key` string, nullable, required
  - `name` string, nullable, required
  - `description` string, nullable
  - `createdAt` string, date-time, required
  - `updatedAt` string, date-time, nullable
  - `defaultFlagLifetimeDays` integer, nullable
  - `_actions` object: Capability hints: which operations the authenticated caller may perform on this resource, each with allowed + optional reason. Computed from the caller's role and the resource's state. Injected at runtime; safe to ignore. Present only on top-level resource responses — nested occurrences (e.g. a rule inside a targeting-config response) do not carry it.

## Other responses

- `400`: The request is malformed, or it carries an If-Match header, which this endpoint can't evaluate. Send no If-Match, or `*`.
- `401`: Authentication is required, or the supplied API token is invalid or expired.
- `403`: Forbidden
- `429`: Rate limit exceeded. Retry after the interval indicated by the Retry-After header.

---

[API](https://skmtc.dev/featureflip/apis/management-api.md) · [All operations](https://skmtc.dev/featureflip/apis/management-api/llms.txt) · [OpenAPI document](https://skmtc.dev/featureflip/apis/management-api/revisions/8a77e8581a45?raw)
