# Projects (/docs/api/concepts/projects)



A project represents a single end-customer's product. Their app, their website, their brand, their social presence — one project. You create projects on behalf of your customers, and everything downstream (influencers, generated content, connected social accounts, SDK telemetry, ads metrics) nests inside that project.

If you've worked with Stripe, think of a project the way you'd think of a Connect account: it's the tenant boundary for a single customer, inside your org.

## Creating a project [#creating-a-project]

You create one with `POST /v1/projects`. `name` and `timezone` are required:

```json
{
  "name": "Acme Fitness",
  "customerExternalId": "acme-prod-987",
  "timezone": "America/Los_Angeles"
}
```

* `name` — human label shown in your customer's surface and in the Layers admin.
* `customerExternalId` — your stable handle for this customer. Stored as free text on `metadata.partner.customerExternalId`; the server does not enforce uniqueness. Make it unique within your org on your side so `GET /v1/projects?customerExternalId=...` is unambiguous.
* `timezone` — IANA timezone. Feeds scheduling and per-day metrics rollups.

Set an `Idempotency-Key` header to make retries safe — same key + same body replays the original response; different body → `409 IDEMPOTENCY_CONFLICT`. See [idempotency](/docs/api/operational/idempotency).

<Callout>
  Creating a project is synchronous (`201 Created` with the full project row). Ingesting the brand (a GitHub repo, a website, an App Store listing) is the async step that runs behind a [job](/docs/api/concepts/jobs). A fresh project has no brand context until you run one of the ingest endpoints.
</Callout>

## What lives inside a project [#what-lives-inside-a-project]

| Resource                                              | Purpose                                                                                                                                                               |
| ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Brand context                                         | App name, description, tagline, voice, ICP, keywords, primary language. Populated by `POST /v1/projects/:id/ingest/github`, `/ingest/website`, or `/ingest/appstore`. |
| SDK apps                                              | One per platform (iOS, Android, web). Each has its own ingest endpoint and CAPI config.                                                                               |
| [Influencers](/docs/api/concepts/influencers)         | Brand-ambassador personas that drive UGC generation.                                                                                                                  |
| [Content items](/docs/api/concepts/content-items)     | Generated videos, slideshows, UGC remixes.                                                                                                                            |
| [Social accounts](/docs/api/concepts/social-accounts) | Connected (user-owned OAuth) and leased (Layers-owned) accounts available for publishing.                                                                             |
| Scheduled posts                                       | The queue of outgoing posts against those accounts.                                                                                                                   |
| [Approval policy](/docs/api/concepts/approval)        | `requires_approval` + `first_n_posts_blocked`. Lives on the project row.                                                                                              |
| Metrics                                               | Organic performance, ads performance, top-performers, conversions.                                                                                                    |

## Customer scoping [#customer-scoping]

Every project carries `customerExternalId` — a free-text field you should make unique within your org (the server does not enforce uniqueness). It lets you unambiguously answer "which of my customers does this project belong to" without trusting the caller.

**Lookup.** `GET /v1/projects?customerExternalId=acme-prod-987` returns the project.

**Assertion.** Read the project first via `GET /v1/projects/:projectId` and assert the returned `customerExternalId` matches what your code expects before issuing follow-up calls. Every project-scoped route (`/v1/projects/:projectId/...`) is server-side restricted to projects inside your org — attempts to touch other orgs' projects return `404 NOT_FOUND`, so a stolen or confused projectId can't leak data, but only your assertion protects against confused-deputy bugs in your own code.

## Mutability and archival [#mutability-and-archival]

* `PATCH /v1/projects/:id` updates user-editable fields: `name`, `timezone`, `primaryLanguage`, brand fields, `metadata`. System fields like `id`, `organizationId`, `createdAt` are immutable.
* `DELETE /v1/projects/:id` is a soft archive. It flips `status` to `archived`, cancels any queued scheduled posts, and stops the project from being surfaced in list endpoints. The underlying data stays; if you need a hard purge, that's an ops request.
* Archived projects can't receive new content generations, new social connections, or new ingests. You can still read historical metrics.
