POST /v1/projects
Create a project, typically one per end-customer.
POST
/v1/projectsPhase 1stableidempotent
- Auth
- Bearer
- Scope
- projects:write
Create a project to hold one end-customer's brand context, layers, influencers, social accounts, and generated content. Set customerExternalId to your partner-side customer handle so later lookups don't require Layers IDs.
The call is synchronous and idempotent via the Idempotency-Key header. It provisions the project shell; downstream ingestion (GitHub, website, App Store) is a separate, async step.
Providing appDescription here also kicks off two background workflows:
- Keyword research. Layers' research agent curates the project's
TikTok hashtag bank over 4–5 minutes. Observe via
GET /v1/projects/:id/keywords(refreshedAtflips fromnullwhen the bank lands). Force a re-run withPOST /v1/projects/:id/keywords/refresh. - First influencer. Layers generates a default persona (name,
gender, visual identity) anchored on the project's brand context.
Observe via
GET /v1/projects/:id/influencers— the row appears withstatus: "pending"immediately and flips throughtrainingtostatus: "ready"in ~1 minute. If your customer needs a different persona, create additional influencers withPOST /v1/projects/:id/influencers.
Both auto-triggers fire-and-forget — failures don't fail the create.
Headers
Idempotency-Keystring (UUID)optionalOptional but strongly recommended on every POST. Same key + same body replays the cached response (`409 IDEMPOTENCY_CONFLICT` on body mismatch). Without it, retries after a connection error create duplicate projects. See [Idempotency](/docs/api/operational/idempotency).
Body
namestringrequiredInternal display name. 3–30 chars.customerExternalIdstringoptionalYour internal customer handle. Unique per organization. Looked up via ?customerExternalId=.ownerEmailstringoptionalNotification email. Defaults to the API key's registered owner.timezonestringrequiredIANA timezone. Controls cron schedule resolution. Use `"UTC"` if you have no preference.primaryLanguagestringoptionaldefault: enBCP-47 language tag (e.g. `en`, `pt-BR`).appNamestringoptionalProduct name the generator anchors hooks and captions on. 3–30 chars. Required before `GET /v1/projects/:id/content/hooks` returns a bank — and the strongest single lever on content quality.appDescriptionstringoptionalProduct pitch the planner uses for hooks and captions. 100–1000 chars. Same precondition for `/content/hooks`.taglinestringoptionalShort one-liner (≤ 80 chars) used in end-cards and overlays.brandVoicestringoptionalCaption tone preset. Defaults to `authentic` when omitted.One of:authentic,witty,professional,warm,casual,educationaltargetGenderstringoptionalAudience gender. Drives influencer persona defaults at create time.One of:all,female,malemetadataobjectoptionalOpaque JSON, ≤ 8KB. Round-tripped unchanged.
Example request
curl https://api.layers.com/v1/projects \
-H "Authorization: Bearer lp_..." \
-H "Idempotency-Key: 4c1a2e92-7b18-4c4b-9b2a-d7a3f8b1c210" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Coffee",
"customerExternalId": "acme-coffee",
"timezone": "America/Los_Angeles",
"primaryLanguage": "en",
"ownerEmail": "growth@gicgrowth.com",
"appName": "Acme Coffee",
"appDescription": "Daily ritual coffee subscriptions for runners and night-shift workers. Single-origin beans from named farms, roasted weekly, and shipped on a cadence that matches how you actually drink coffee — so the bag never goes stale and you never run out before a hard workout.",
"tagline": "Coffee that shows up before you run out.",
"brandVoice": "warm",
"targetGender": "all"
}'const project = await layers.projects.create(
{
name: "Acme Coffee",
customerExternalId: "acme-coffee",
timezone: "America/Los_Angeles",
primaryLanguage: "en",
ownerEmail: "growth@gicgrowth.com",
appName: "Acme Coffee",
appDescription:
"Daily ritual coffee subscriptions for runners and night-shift workers. Single-origin beans from named farms, roasted weekly, and shipped on a cadence that matches how you actually drink coffee — so the bag never goes stale and you never run out before a hard workout.",
tagline: "Coffee that shows up before you run out.",
brandVoice: "warm",
targetGender: "all",
},
{ idempotencyKey: crypto.randomUUID() }
);project = layers.projects.create(
name="Acme Coffee",
customer_external_id="acme-coffee",
timezone="America/Los_Angeles",
primary_language="en",
owner_email="growth@gicgrowth.com",
app_name="Acme Coffee",
app_description=(
"Daily ritual coffee subscriptions for runners and night-shift workers. "
"Single-origin beans from named farms, roasted weekly, and shipped on a "
"cadence that matches how you actually drink coffee — so the bag never "
"goes stale and you never run out before a hard workout."
),
tagline="Coffee that shows up before you run out.",
brand_voice="warm",
target_gender="all",
idempotency_key=str(uuid.uuid4()),
)Response
201Created
{
"id": "prj_9cb958b5-11b5-4e30-8675-5d075d52da7c",
"organizationId": "org_2481fa5c-a404-44ed-a561-565392499abc",
"name": "Acme Coffee iOS",
"status": "active",
"customerExternalId": "acme-coffee",
"timezone": "America/Los_Angeles",
"primaryLanguage": "en",
"ownerEmail": "growth@gicgrowth.com",
"appName": "Acme Coffee",
"appDescription": "Daily ritual coffee subscriptions for runners and night-shift workers. Single-origin beans from named farms, roasted weekly, and shipped on a cadence that matches how you actually drink coffee — so the bag never goes stale and you never run out before a hard workout.",
"tagline": "Coffee that shows up before you run out.",
"brandVoice": "warm",
"targetGender": "all",
"platformIosBundleId": null,
"platformAndroidBundleId": null,
"platformWebDomain": null,
"ingestState": {
"github": null,
"website": null,
"appstore": null
},
"metadata": null,
"createdAt": "2026-04-18T19:02:11.959888+00:00",
"updatedAt": "2026-04-18T19:02:11.959888+00:00"
}Errors
| Status | Code | When |
|---|---|---|
| 422 | VALIDATION | name empty, timezone invalid, metadata > 8KB. |
| 401 | UNAUTHENTICATED | Missing or invalid key. |
| 403 | FORBIDDEN_SCOPE | Key lacks projects:write. |
| 409 | IDEMPOTENCY_CONFLICT | Same Idempotency-Key replayed with a different body. |
| 409 | CONFLICT | customerExternalId already in use on another project. |
| 422 | VALIDATION_FAILED | Body includes id (resource ids are server-minted — see Idempotency). |
| 429 | RATE_LIMITED | Write budget exhausted. |
See also
PATCH /v1/projects/:id- update fields after creation- Getting started - the canonical project → influencer → content flow
- Idempotency - replay semantics