Layers
Partner APIConcepts

Content items

Containers are the unit of generated content. Each container holds media, captions, and scheduling metadata for one creative.

View as Markdown

A content item — or, to use the field name, a content container — is one generated creative. It holds the rendered media, the captions, the hook, the format, the influencer that voiced it, and the approval state. When we talk about "a piece of content" in this API, we mean a container.

Generation is async. You POST a brief, you get back a job envelope with a jobId and a containerId, and you poll either the job envelope for progress or the container for the final shape.

Lifecycle

Container status values (from the ContainerStatus enum):

  • queued — job accepted, not yet running.
  • generating — generation job is running. Media assets are empty until complete.
  • completed — generation finished. Media and captions are populated. If the project's approval policy requires review, the container's approvalStatus will be pending.
  • failed — generation failed. Check lastError on the container.
  • canceled — container was canceled (by explicit cancel or project archive).

Approval status is independent of container status — it lives on approvalStatus and takes values not_required, pending, approved, or rejected.

Creating one

POST /v1/projects/:projectId/content
Content-Type: application/json
X-Api-Key: $LAYERS_API_KEY
Idempotency-Key: 7c2f1a3e-0b4c-4a11-9f7e-33c0a2c1bd55

{
  "format": "auto",
  "brief": {
    "hook": "The calendar app that finally respects your evenings",
    "targetPlatforms": ["tiktok", "instagram"],
    "influencerId": "inf_01HX9Y6K7EJ4T2ABCDEF",
    "themeTags": ["launch"],
    "referenceMediaIds": ["med_01HX9Y6K7EJ4T2ABCDEF"],
    "language": "en-US"
  }
}
202
{
  "jobId": "job_01HX9Y6K7EJ4T2ABCDEF01234",
  "kind": "content_generate",
  "status": "running",
  "stage": "queued",
  "projectId": "254a4ce1-f4ca-42b1-9e36-17ca45ef3d39",
  "containerId": "cnt_01HX9Y6K7EJ4T2ABCDEF",
  "locationUrl": "/v1/jobs/job_01HX9Y6K7EJ4T2ABCDEF01234",
  "startedAt": "2026-04-18T12:04:11.000Z"
}

Pair Idempotency-Key with every generate call. A retry inside the 24-hour window replays the original 202; a conflicting body returns 409 IDEMPOTENCY_CONFLICT rather than billing you for a duplicate job.

Formats

formatWhen to use
video_remixEdited video cut from your reference media + generated overlays.
slideshow_remixMulti-image slideshow with typographic hooks.
ugc_remixUGC-style influencer video, voiced by the selected persona.
autoServer picks based on reference media and brand signals. The safest default.

Reading a container

GET /v1/content/:containerId
→ 200
{
  "id": "cnt_01HX9Y6K7EJ4T2ABCDEF",
  "projectId": "254a4ce1-f4ca-42b1-9e36-17ca45ef3d39",
  "status": "completed",
  "approvalStatus": "approved",
  "format": "video_remix",
  "influencerId": "inf_01HX9Y6K7EJ4T2ABCDEF",
  "brief": { "hook": "The calendar app that finally...", "targetPlatforms": ["tiktok"] },
  "assets": [
    {
      "id": "med_01HX9Y6K7EJ4T2ABCDEF",
      "kind": "video",
      "url": "https://media.layers.com/...",
      "width": 1080,
      "height": 1920,
      "durationMs": 27500
    }
  ],
  "captions": [
    { "platform": "tiktok", "text": "..." }
  ],
  "lastError": null,
  "createdAt": "2026-04-18T12:04:11.000Z",
  "updatedAt": "2026-04-18T12:07:33.000Z"
}

Media URLs on assets[].url are long-lived permanent URLs. If you need a short-lived signed URL (your infra wants to re-host), use GET /v1/content/:id/assets/:assetId.

Regenerating, cloning, rejecting

  • POST /v1/content/:id/regenerate — remix the same container. Takes an optional patch that overrides brief fields. Returns 202 with a new jobId; the container id is preserved. Good for "same brief, different render."
  • POST /v1/content/:id/clone-from-post — fork or reimagine an existing platform post (yours or a top performer). Produces a new container with a new id.
  • POST /v1/content/:id/reject — reject through the approval gate. Optionally kicks off a regeneration atomically.

Listing

GET /v1/projects/:projectId/content?status=completed&format=video_remix&limit=25

Filters: status, format, cursor, limit. Cursor-paginated. Pass status=completed when you're rendering a library view; combine with an approval query to drive your review UX.

Progress during generation

For an in-flight container, both endpoints tell you the same story from different angles:

  • GET /v1/jobs/:jobId — canonical progress. Use this by default.
  • GET /v1/content/:id/progress — same shape, scoped to the container. Useful if you've lost the jobId.

On this page