Layers
Partner APIConcepts

Projects

A project is one of your end-customers. Content, influencers, social accounts, and metrics all scope to it.

View as Markdown

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

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

{
  "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.

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. A fresh project has no brand context until you run one of the ingest endpoints.

What lives inside a project

ResourcePurpose
Brand contextApp name, description, tagline, voice, ICP, keywords, primary language. Populated by POST /v1/projects/:id/ingest/github, /ingest/website, or /ingest/appstore.
SDK appsOne per platform (iOS, Android, web). Each has its own ingest endpoint and CAPI config.
InfluencersBrand-ambassador personas that drive UGC generation.
Content itemsGenerated videos, slideshows, UGC remixes.
Social accountsConnected (user-owned OAuth) and leased (Layers-owned) accounts available for publishing.
Scheduled postsThe queue of outgoing posts against those accounts.
Approval policyrequires_approval + first_n_posts_blocked. Lives on the project row.
MetricsOrganic performance, ads performance, top-performers, conversions.

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

  • 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.

On this page