# GET /v1/content/:containerId (/docs/api/reference/content/get-container)



<Endpoint method="GET" path="/v1/content/:containerId" scope="content:read" phase="1" />

Returns the full container record: generation status, approval status, hook, caption, and the rendered media assets. Safe to call while a generation job is in-flight - fields that aren't populated yet are present as `null` or empty arrays. For live progress, prefer [`GET /v1/content/:containerId/progress`](/docs/api/reference/content/get-progress) or [`GET /v1/jobs/:jobId`](/docs/api/reference/jobs/get-job).

## Path parameters [#path-parameters]

<Parameters
  rows="[
  { name: 'containerId', type: 'string (cnt_uuid)', required: true, description: 'The container id — the `cnt_`-prefixed id returned by create/list (the bare uuid is also accepted).' },
]"
/>

## Request [#request]

<Tabs items="['curl', 'TypeScript', 'Python']">
  <Tab value="curl">
    ```sh title="terminal"
    curl https://api.layers.com/v1/content/{containerId} \
      -H "Authorization: Bearer $LAYERS_API_KEY"
    ```
  </Tab>

  <Tab value="TypeScript">
    ```ts title="get-container.ts"
    const res = await fetch(
      `https://api.layers.com/v1/content/${containerId}`,
      { headers: { 'Authorization': `Bearer ${process.env.LAYERS_API_KEY}` } },
    );
    const container = await res.json();
    ```
  </Tab>

  <Tab value="Python">
    ```py title="get_container.py"
    import os, httpx

    r = httpx.get(
        f"https://api.layers.com/v1/content/{container_id}",
        headers={"Authorization": f"Bearer {os.environ[\'LAYERS_API_KEY\']}"},
    )
    container = r.json()
    ```
  </Tab>
</Tabs>

## Responses [#responses]

<Response status="200" description="Full container record.">
  ```json
  {
    "id": "cnt_7d18b9a1...",
    "projectId": "prj_01HX...",
    "status": "completed",
    "format": "ugc-remix",
    "hook": "wait for it...\nthis simple habit changed everything about my mindset 🧠",
    "influencerId": "inf_4a8e1bc2...",
    "sourceTiktokId": null,
    "mediaId": "med_01HZ...",
    "caption": "Thirty days, one run at a time…",
    "firstComment": null,
    "preview": {
      "kind": "video",
      "primaryUrl": "https://media.layers.com/.../ast_01HXZ9/video.mp4",
      "videoUrl": "https://media.layers.com/.../ast_01HXZ9/video.mp4",
      "thumbnailUrl": "https://media.layers.com/.../ast_01HXZ9/thumb.jpg",
      "hlsUrl": null,
      "durationMs": 9200,
      "aspectRatio": "9:16"
    },
    "assets": [
      {
        "assetId": "ast_01HXZ9...",
        "kind": "video",
        "url": "https://media.layers.com/.../ast_01HXZ9/video.mp4",
        "thumbnailUrl": "https://media.layers.com/.../ast_01HXZ9/thumb.jpg"
      }
    ],
    "approvalStatus": "approved",
    "creativeType": "generated",
    "adsEnrollment": "auto",
    "createdAt": "2026-04-17T13:10:00Z",
    "completedAt": "2026-04-17T13:14:22Z",
    "failedAt": null,
    "lastError": null
  }
  ```
</Response>

<Response status="404" description="Container not in this org.">
  ```json
  { "error": { "code": "NOT_FOUND", "message": "Container not found.", "requestId": "req_..." } }
  ```
</Response>

## Echo fields [#echo-fields]

The partner's creative inputs are echoed back on read so you can drive
your own UI off a single GET without keeping a side-table.

| Field            | Populates for                                                                       | Notes                                                                                                                                            |
| ---------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `hook`           | `slideshow-builder`, `ugc-remix`                                                    | The literal string you sent on POST — line breaks and emoji round-trip exactly. `null` on remix formats (they adapt source overlays internally). |
| `influencerId`   | every format that took an `influencerId` or resolved one via a wired social account | Prefixed `inf_<uuid>`.                                                                                                                           |
| `sourceTiktokId` | `video-remix`, `slideshow-remix`                                                    | The raw TikTok id you sent. `null` on builder / ugc formats.                                                                                     |
| `mediaId`        | `ugc-remix` only                                                                    | Partner-uploaded app-demo source clip, prefixed `med_<uuid>`. `null` on every other format.                                                      |

## Origin fields [#origin-fields]

Every container carries two origin fields:

| Field           | Values                              | Notes                                                                                                                       |
| --------------- | ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| `creativeType`  | `generated` \| `uploaded`           | How the container was filled. Uploaded content is partner- or user-supplied finished media; everything else is `generated`. |
| `adsEnrollment` | `auto` \| `opted_in` \| `opted_out` | Generated content is `auto` (legacy enrollment). Uploads are born `opted_out` and are not used in paid campaigns.           |

Uploaded containers additionally carry `platformFit` — computed per-platform fit from the probed media:

```json
"platformFit": [
  { "platform": "tiktok", "ok": false, "issues": ["800x800 image resolution too low (min 1080px on the short edge)"] },
  { "platform": "instagram", "ok": false, "issues": ["800x800 image resolution too low (min 1080px on the short edge)"] }
]
```

`platformFit` is advisory; scheduling or publishing to an unfit platform returns `422` with the same issues. It is absent on generated containers (no probe metadata). On uploaded containers, `format` is `null` and `hook` is always `null` — hook is a generation-only field and never applies to uploaded media. See Upload finished content for the full semantics.

## Container status [#container-status]

<Parameters
  title="Container status"
  rows="[
  { name: 'queued', type: 'status', description: 'Created, awaiting the generator.' },
  { name: 'processing', type: 'status', description: 'Generation in-flight. Assets populate on completion.' },
  { name: 'completed', type: 'status', description: 'Ready to approve and schedule.' },
  { name: 'failed', type: 'status', description: 'See `lastError.code` and `lastError.message`. Call generate again with a fresh `hook` to retry.' },
  { name: 'canceled', type: 'status', description: 'Workflow was canceled. Call generate again to try again.' },
]"
/>

## Approval status [#approval-status]

<Parameters
  title="Approval status"
  rows="[
  { name: 'not_required', type: 'status', description: 'Project policy does not require approval - content can be scheduled immediately.' },
  { name: 'pending', type: 'status', description: 'Default when the project requires approval. Blocks scheduling.' },
  { name: 'approved', type: 'status', description: 'Cleared for scheduling and publishing.' },
  { name: 'rejected', type: 'status', description: 'Blocked from scheduling. Call generate again with a fresh `hook` to try again.' },
]"
/>

## Errors [#errors]

| Code              | When                          |
| ----------------- | ----------------------------- |
| `NOT_FOUND`       | Container id not in this org. |
| `FORBIDDEN_SCOPE` | Key lacks `content:read`.     |

## The `preview` object [#the-preview-object]

Every completed container carries a normalized `preview` object — the canonical "render this in my UI" surface. Render off `preview`, not off `assets[]` (which is the underlying file inventory). See [Preview object](/docs/api/concepts/preview-object) for the per-`kind` field-population matrix.

| Field          | Type                                | Notes                                                                                                                                                                                                           |
| -------------- | ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `kind`         | `"slideshow" \| "video" \| "image"` | Discriminator. Pick rendering mode off this.                                                                                                                                                                    |
| `primaryUrl`   | string                              | The first/hero URL — fine to drop directly into `<img>` or `<video>` based on `kind`.                                                                                                                           |
| `thumbnailUrl` | string \| null                      | First slide for slideshows. For video kinds the poster-frame extraction is best-effort and may be `null` on a freshly completed container — render the `videoUrl` directly and let the browser produce a frame. |
| `imageUrls`    | string\[]                           | Populated when `kind = "slideshow"`. Ordered.                                                                                                                                                                   |
| `videoUrl`     | string                              | Populated when `kind = "video"`. Direct MP4 URL.                                                                                                                                                                |
| `hlsUrl`       | string \| null                      | Adaptive-bitrate HLS manifest. Returns `null` until the HLS pipeline ships.                                                                                                                                     |
| `durationMs`   | integer \| null                     | Video duration in milliseconds. `null` for slideshow/image kinds and on video kinds when probing didn't run (treat as informational; the asset is still playable).                                              |
| `aspectRatio`  | string                              | `"9:16"` for verticals, `"1:1"` for squares, etc.                                                                                                                                                               |

## See also [#see-also]

* [List containers on a project](/docs/api/reference/content/list-containers)
* [Fetch a signed asset URL](/docs/api/reference/content/get-asset)
* [Approve a container](/docs/api/reference/approval/approve-content)
* [Schedule publishing](/docs/api/reference/publishing/schedule-content)
* [Preview object reference](/docs/api/concepts/preview-object)
