> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apostra.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create creative manifest

> Upload assets and create a creative manifest under a campaign

`POST /api/v2/buyer/campaigns/{campaignId}/creatives/create`

Creates a manifest from uploaded files plus JSON metadata in a single `multipart/form-data` request. Apostra auto-classifies assets, processes HTML to rewrite local references to CDN URLs, inserts ADCP tracking macros, and generates a preview. The body is a `metadata` JSON part plus zero or more `files` parts; each file pairs with the matching `assets[].filename`.

<Note>
  To stage a creative before a campaign exists, use
  [Create advertiser creative master](/v2/buyer/creatives/tasks/create-advertiser-creative-manifest)
  instead — same body, advertiser scope, no delivery until you assign it.
</Note>

<Note>
  Propagation to sales agents is asynchronous. On campaign execute, the manifest syncs to each agent that supports its format as a `creative_sync` task — poll `sync_status` to confirm receipt. See [Tasks](/v2/guides/tasks) for the async polling model.
</Note>

<Note>
  The MCP upload Task is a separate browser-direct path for JPEG, PNG, and MP4 files. It returns a private `assetRef`, not a Creative. Use V3 `save_creative` with `sourceAssets`, the campaign's exact `formatOptionRef`, and values for every required media, copy, and destination slot to assemble and attach the Creative. The 50 MB multipart limit below does not apply to that Task.
</Note>

## Request

<CodeGroup>
  ```bash curl theme={null}
  curl -X POST https://api.apostra.com/api/v2/buyer/campaigns/cmp_987654321/creatives/create \
    -H "Authorization: Bearer $SCOPE3_API_KEY" \
    -H "Content-Type: multipart/form-data" \
    -F 'metadata={
      "name": "Q2 hero video",
      "message": "30s product reveal — premium tone, 2-second hook",
      "template_id": "video_standard",
      "url_asset": { "url": "https://acme.com/promo", "url_type": "clickthrough" },
      "assets": [
        { "filename": "hero-30s.mp4", "asset_type": "VIDEO", "label": "main_video" }
      ]
    };type=application/json' \
    -F "files=@hero-30s.mp4" \
    -F "files=@companion-banner.png"
  ```

  ```bash With a VAST tag theme={null}
  curl -X POST https://api.apostra.com/api/v2/buyer/campaigns/cmp_987654321/creatives/create \
    -H "Authorization: Bearer $SCOPE3_API_KEY" \
    -H "Content-Type: multipart/form-data" \
    -F 'metadata={
      "name": "VAST premium",
      "template_id": "video_vast",
      "url_asset": { "url": "https://ad.doubleclick.net/ddm/vast/acme", "url_type": "vast", "vast_version": "4.2" }
    };type=application/json'
  ```
</CodeGroup>

## Parameters

The `metadata` JSON part carries:

| Field               | Type   | Required | Notes                                                                                                                                                                                                                                                                                                                                           |
| ------------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`              | string | No       | Manifest name (max 255). Auto-generated if omitted                                                                                                                                                                                                                                                                                              |
| `message`           | string | No       | Creative brief / direction text (max 5000)                                                                                                                                                                                                                                                                                                      |
| `template_id`       | string | No       | ADCP format ID (`display_image`, `display_html`, `video_standard`) or vendor tag                                                                                                                                                                                                                                                                |
| `format_id`         | object | No       | `{ agent_url, id, width?, height?, duration_ms? }` — explicit ADCP format reference                                                                                                                                                                                                                                                             |
| `format_kind`       | string | No       | AdCP 3.1 canonical kind (e.g. `image_carousel`, `video_hosted`). Alternative to `format_id`. A URL-free canonical image is returned with its validated dimensions in `params` rather than a synthetic `format_id`                                                                                                                               |
| `format_option_ref` | object | No       | Pins an exact canonical option. Use `{ scope: "product", format_option_id }` for an option declared by a campaign product, or `{ scope: "publisher", publisher_domain, format_option_id }` for a publisher catalog option. A product pin resolves and returns its declared `format_kind`                                                        |
| `url_asset`         | object | No       | Single URL asset: `{ url, url_type, vast_version? }`. `url_type` is `clickthrough`, `tracker_pixel`, `tracker_script`, or `vast`. Set `vast_version` (`2.0`, `3.0`, `4.0`, `4.1`, `4.2`, `4.3`) on a `vast` asset — a tag with no declared version cannot be delivered to a managed ad server. Declaring it on any other `url_type` is rejected |
| `url_assets`        | array  | No       | Slot-assigned URL assets for multi-slot formats (max 50): `{ asset_id, url, url_type? }`                                                                                                                                                                                                                                                        |
| `text_assets`       | array  | No       | Slot-assigned text assets, e.g. native headline (max 50): `{ asset_id, content }`                                                                                                                                                                                                                                                               |
| `assets`            | array  | No       | Per-file metadata: `{ filename, asset_type?, label?, slot_asset_id? }`. `asset_type` is one of `IMAGE`, `VIDEO`, `AUDIO`, `HTML`, `JAVASCRIPT`, `CSS`, `TEXT`, `URL`, `VAST`, `FONT`, `LOGO`, `DOCUMENT`. Auto-classified if omitted                                                                                                            |
| `cards`             | array  | No       | Carousel cards for `image_carousel` (2–10): `{ filename` or `url, headline?, description?, cta?, landing_page_url? }`                                                                                                                                                                                                                           |
| `frequencyCaps`     | array  | No       | Buyer-defined caps: `{ max_impressions, window: { interval, unit } }`                                                                                                                                                                                                                                                                           |

Form parts: `files=@<path>` (one per uploaded asset). Limits: 50 MB per file, 20 files per request.

## Response

```json theme={null}
{
  "creative_id": "cm_abcdef",
  "campaign_id": "cmp_987654321",
  "name": "Q2 hero video",
  "message": "30s product reveal — premium tone, 2-second hook",
  "template_id": "video_standard",
  "brand_domain": "acme.com",
  "format_id": { "id": "video_standard", "agent_url": "https://agent.example.com" },
  "preview_url": "https://storage.googleapis.com/creatives/cm_abcdef/preview.html",
  "assets": [
    {
      "asset_id": "asset_001",
      "name": "main_video",
      "original_filename": "hero-30s.mp4",
      "asset_type": "VIDEO",
      "content_type": "video/mp4",
      "file_size": 8421376,
      "file_size_status": "verified",
      "public_url": "https://cdn.example.com/creatives/cm_abcdef/hero-30s.mp4",
      "asset_source": "USER_UPLOADED",
      "created_at": "2026-06-07T15:04:00Z"
    }
  ],
  "tracking": {
    "impression_tracker_url": "https://track.scope3.com/imp?cid={CAMPAIGN_ID}",
    "click_tracker_url": "https://track.scope3.com/clk?rurl=https%3A%2F%2Facme.com%2Fpromo",
    "supported_macros": ["CAMPAIGN_ID", "DEVICE_TYPE", "COUNTRY"]
  },
  "sync_status": { "synced": false, "agent_count": 0 },
  "created_at": "2026-06-07T15:04:00Z",
  "updated_at": "2026-06-07T15:04:00Z"
}
```

Returns `201`. `sync_status.synced` is `false` until the campaign executes and the manifest propagates. When a manifest with the same `(campaign_id, name)` already exists, the existing one is returned with `already_exists: true` and `ignored_files` set — use [Update creative manifest](/v2/buyer/creatives/tasks/update-creative-manifest) to add assets to it.

For a URL-free canonical image, the response carries its complete identity as
`"format_kind": "image"` and `"params": { "width": 1200, "height": 628 }`.
It omits `format_id` because no legacy agent-owned format was selected. An
imported legacy manifest may retain `format_id` as provenance, but
`format_kind` and `params` remain authoritative for matching.

In this V2 REST response, `file_size_status` is a VIDEO-only evidence marker. `verified` means that the VIDEO `file_size` is authoritative; a VIDEO `file_size` of `0` without the marker is a compatibility sentinel for unavailable evidence. IMAGE and AUDIO file-size behavior and provenance are unchanged, so do not infer their evidence status from the marker's absence.

MCP and internal manifest results do not use this REST compatibility mapping, so unavailable evidence may appear as `file_size: null` with `file_size_status` absent.

## Errors

* `400 VALIDATION_ERROR` — a `files` part with no matching `assets[].filename`, more than one `url_type: clickthrough` URL asset, file over 50 MB, or unsupported MIME type.
* `400 VALIDATION_ERROR` — a product-scoped `format_option_ref` is missing from the campaign, conflicts across campaign products, or disagrees with an explicit `format_kind`.
* `400 VALIDATION_ERROR` — a slot-bound asset targets an undeclared slot, has the wrong asset type, or conflicts with another input on the same slot. V3 `save_creative` also rejects a `sourceAssets` composition that leaves a required canonical slot empty.
* `404 NOT_FOUND` — `campaignId` does not exist or is not visible to the authenticated account.

See [Errors](/v2/reference/errors) for the full error contract.

## Related

<CardGroup cols={2}>
  <Card title="Creative overview" href="/v2/object-guides/creative" icon="image">
    Manifest fields, asset types, and tracking
  </Card>

  <Card title="Get creative manifest" href="/v2/buyer/creatives/tasks/get-creative-manifest" icon="magnifying-glass">
    Read the created manifest
  </Card>

  <Card title="Tasks" href="/v2/guides/tasks" icon="list-check">
    Async creative\_sync polling
  </Card>
</CardGroup>
