---
title: "Create a project"
method: POST
path: "/api/mcp/projects"
tags: ["Projects"]
---

# Create a project

`POST /api/mcp/projects`

## Request

- Base URL: `https://app.searchable.com`
- URL: `https://app.searchable.com/api/mcp/projects`
- Auth: HTTP bearer

Create a project and set it up as the app's Add project flow does: the project on the workspace's plan cadence, its competitors, brand profile, default location and onboarding record, counted toward the workspace's project quota. It requires the same details the app asks for (brand name and summary, reach, country, at least one competitor); `generatePrompts: true` starts the app's prompt generation in the background, and the prompts are tracked within the prompt allowance. Requires a connection minted with the `project:provision` capability (not the bare `write` scope); a project-bound credential is refused outright, since creating a project is a workspace-level action. Supports `Idempotency-Key` replay; a reused key with a different body returns 409 `idempotency_conflict` instead of replaying the first project. A duplicate domain in the same workspace returns 409 `duplicate_domain`.

## Headers

- `Idempotency-Key` string

## Request body

- object
  - `workspaceId` string, required: A workspace from `GET /api/mcp/workspaces` whose `canCreateProjects` is true.
  - `domain` string, required: The brand's website domain, e.g. example.com
  - `brandName` string, required: The brand's name, as it's referred to in prompts and reports
  - `brandSummary` string, required: 2-3 sentences: what the brand sells, who it serves, and what makes it distinct
  - `brandNameDisambiguation` string: When the name is ambiguous, a short note on which brand this is; empty when the name is distinctive
  - `reach` 'global' | 'national-global' | 'nationwide' | 'regional' | 'local', required: Where the brand sells; national-global is a primary market plus international reach
  - `country` string, required: Where most customers are: an ISO 3166-1 alpha-2 code, e.g. GB
  - `city` string: The city, for regional or local reach, e.g. Austin. A state's name here is taken as the state. A US city in more than one state needs `state`, or it is not placed
  - `state` string: The state, for regional or local reach: alone for the whole state (e.g. California), or with `city` for the state the city is in (e.g. Oregon for Portland). A US code such as TX works; elsewhere write the name
  - `language` string: Language for the prompts, e.g. en-GB; defaults from the country
  - `competitors` union[], required: Direct competitors, each a website domain or {domain, name}
    - union
      - string
      - object
        - `domain` string, required
        - `name` string
  - `brandAliases` string[]: Other names the brand goes by, counted as mentions of it
  - `name` string: Project name; defaults to the brand name
  - `description` string
  - `generatePrompts` boolean, required: Start the app's prompt generation in the background (tracked prompts, within the prompt allowance)

## Response `200`

Project created.

- CreateProjectResponse
  - `project` ProjectLifecycleRecord, required: The project shape create_project returns — curated, not the raw internal row (no userId, settings, workspaceId, or pitch fields; see the sibling ProjectListItem, which strips workspaceId the same way).
    - `id` string, uuid, required
    - `name` string, required
    - `domain` string, nullable
    - `description` string, nullable
    - `iconUrl` string, nullable
    - `isActive` boolean, required
    - `currentRevision` integer, required: Optimistic-concurrency counter — pass as `expectedRevision` on the next write to detect a concurrent change.
    - `createdAt` string, date-time
    - `updatedAt` string, date-time
  - `success` boolean, required
  - `promptGeneration` 'started' | 'not_requested'
  - `location` object: The project's default location as saved: the country, a state (`region` with no `city`), or a city in its state.
    - `countryCode` string, required
    - `city` string, nullable, required
    - `region` string, nullable, required
    - `label` string, required: The place as the app names it, e.g. California, United States
  - `notes` string[]: Present only when a state or city that was sent could not be placed: a state that matches no known state; a city that was not recognised; a US city found in more than one state; a state or city that could not be checked, because the lookup failed or did not answer, which is never reported as not recognised. The default location is then the whole country.

## Other responses

- `400`: Error response. `code` is the machine-readable reason — see the [error reference](/api/errors) for the full catalog and remediation per code.
- `401`: Error response. `code` is the machine-readable reason — see the [error reference](/api/errors) for the full catalog and remediation per code.
- `403`: Error response. `code` is the machine-readable reason — see the [error reference](/api/errors) for the full catalog and remediation per code.
- `409`: Another request with the same `Idempotency-Key` is still executing. `Retry-After` (5 seconds) is always present — retry with the SAME key once it elapses.
- `429`: Rate limited. Usually the per-API-key REST limit (600 requests/minute); on Google-backed endpoints it can also relay an upstream Google rate limit. `Retry-After` (seconds) is present when the limit was applied by Searchable's own rate limiter; it may be absent when an upstream provider rate-limits.

## Changes

- **2026-11-03** `28a5e2a65cd3`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/searchable/apis/searchable-rest-api/changes/api/mcp/projects/post.md)

---

[API](https://skmtc.dev/searchable/apis/searchable-rest-api.md) - [All operations](https://skmtc.dev/searchable/apis/searchable-rest-api/llms.txt) - [OpenAPI document](https://skmtc.dev/searchable/apis/searchable-rest-api/revisions/a1cc70e46e73?raw)
