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

# Creating promotions

> Create a promotion from uploaded files or inline text, and create new versions when content is revised.

A promotion is a piece of content submitted for compliance review. You create one by combining your uploaded files (or inline text) with classification: the products, channels, and jurisdiction that determine which rules apply.

## Create a promotion

<CodeGroup>
  ```bash curl theme={null}
  curl -s -X POST "$API_BASE/v1/promotions" \
    -H "Authorization: Bearer $ADCLEAR_API_KEY" \
    -H "X-External-User-ID: $USER_ID" \
    -H "Content-Type: application/json" \
    -d '{
      "uploadIds": ["a1b2c3d4-..."],
      "fileFormat": "pdf",
      "promotionName": "Summer Campaign 2026",
      "channelIds": ["<channel-id>"],
      "productIds": ["<product-id>"],
      "jurisdictionId": "<jurisdiction-id>",
      "metadata": { "workfrontProjectId": "PROJ-123", "workfrontTaskId": "TASK-456" }
    }'
  ```

  ```typescript TypeScript theme={null}
  const res = await fetch(`${API_BASE}/v1/promotions`, {
    method: "POST",
    headers,
    body: JSON.stringify({
      uploadIds: ["a1b2c3d4-..."],
      fileFormat: "pdf",
      promotionName: "Summer Campaign 2026",
      channelIds: ["<channel-id>"],
      productIds: ["<product-id>"],
      jurisdictionId: "<jurisdiction-id>",
      metadata: { workfrontProjectId: "PROJ-123", workfrontTaskId: "TASK-456" },
    }),
  });
  const { promotionId, versionId } = await res.json();
  ```

  ```python Python theme={null}
  res = requests.post(f"{API_BASE}/v1/promotions", headers=headers, json={
      "uploadIds": ["a1b2c3d4-..."],
      "fileFormat": "pdf",
      "promotionName": "Summer Campaign 2026",
      "channelIds": ["<channel-id>"],
      "productIds": ["<product-id>"],
      "jurisdictionId": "<jurisdiction-id>",
      "metadata": {"workfrontProjectId": "PROJ-123", "workfrontTaskId": "TASK-456"},
  })
  promotion_id, version_id = res.json()["promotionId"], res.json()["versionId"]
  ```
</CodeGroup>

**Response (201 Created):**

```json theme={null}
{
  "promotionId": "promo-uuid-...",
  "versionId": "version-uuid-...",
  "version": 1,
  "metadata": { "workfrontProjectId": "PROJ-123", "workfrontTaskId": "TASK-456" },
  "correlationId": "corr-uuid-..."
}
```

Required fields are `fileFormat`, `promotionName`, `channelIds` (at least one), `productIds` (at least one), and `jurisdictionId`.

### Metadata

The `metadata` field accepts an arbitrary string-to-string map (up to 20 keys). Use it to pass through your own reference IDs, such as Workfront project and task IDs. Adclear stores it with the promotion and echoes it back in **every** webhook payload, so you can correlate Adclear events to your system without maintaining a separate mapping. Do not store PII, credentials, or sensitive data.

### Text promotions

When `fileFormat` is `"text"`, provide the `content` field instead of `uploadIds`. The `content` field accepts plain text or HTML.

```json theme={null}
{
  "content": "<p>Summer savings: earn up to 5% AER on our fixed-rate ISA.</p>",
  "fileFormat": "text",
  "promotionName": "ISA Summer Campaign 2026",
  "channelIds": ["<channel-id>"],
  "productIds": ["<product-id>"],
  "jurisdictionId": "<jurisdiction-id>"
}
```

### Handling upload conflicts

If a referenced file hasn't finished uploading, the request returns `409 Conflict` with a `details` entry of type `upload_not_ready`. Poll `GET /v1/uploads/{uploadId}` until each upload's `status` is `completed`, then retry. Use exponential back-off (1s, 2s, 4s, up to \~30s). Small files complete within seconds; files over 100 MB may take longer.

## Submit a revision

If a promotion needs changes after review, upload the new file(s) (see [File upload](/developer/file-upload)), then create a new version on the same promotion. The new version inherits the promotion's name, channels, products, and jurisdiction. Supply only the new files and any fields you want to override.

```http theme={null}
POST https://public-api.adclear.ai/v1/promotions/{promotionId}/versions

{
  "uploadIds": ["<new-upload-id>"],
  "fileFormat": "pdf"
}
```

The response mirrors create, with an incremented `version`. Then trigger [evaluation](/developer/evaluation) on the new version. Each version is evaluated independently, and previous results are retained for audit.

### Create-version fields

| Field                     | Required    | Description                                                                                             |
| ------------------------- | ----------- | ------------------------------------------------------------------------------------------------------- |
| `uploadIds`               | Conditional | Upload IDs for the revised content. Required for file-based formats; omit when `fileFormat` is `"text"` |
| `content`                 | Conditional | Inline text or HTML. Required when `fileFormat` is `"text"`; omit for file-based formats                |
| `fileFormat`              | Yes         | `pdf`, `image`, `video`, `audio`, `text`, or `document`                                                 |
| `deadline`                | No          | Updated deadline (ISO 8601). Retained if omitted                                                        |
| `caption`                 | No          | Updated caption. Retained if omitted                                                                    |
| `details`                 | No          | Updated details. Retained if omitted                                                                    |
| `type`                    | No          | `single`, `batch`, `carousel`, or `variations`. Inherited if omitted                                    |
| `linkedClaimIds`          | No          | Updated claim IDs. Retained if omitted                                                                  |
| `targetMarketIds`         | No          | Updated target market IDs. Retained if omitted                                                          |
| `applyToAllTargetMarkets` | No          | Applies to all target markets regardless of `targetMarketIds`                                           |
| `metadata`                | No          | Updated metadata. The original is retained and echoed in webhooks if omitted                            |

**Inherited vs. overridable.** `promotionName`, `channelIds`, `productIds`, and `jurisdictionId` are set once at promotion creation and can't change per version. Everything else above can be overridden; omitted fields carry forward from the previous version.

## Frequently asked questions

<AccordionGroup>
  <Accordion title="Which fileFormat value should I use?" icon="circle-question">
    | File type                                                                 | `fileFormat`                                            |
    | ------------------------------------------------------------------------- | ------------------------------------------------------- |
    | `.pdf`                                                                    | `pdf`                                                   |
    | `.jpg`, `.png`, `.gif`, `.webp`, `.svg`, `.bmp`, `.tiff`                  | `image`                                                 |
    | `.mp4`, `.mov`, `.avi`, `.webm`, `.mkv`, `.wmv`                           | `video`                                                 |
    | `.mp3`, `.wav`, `.aac`, `.ogg`, `.flac`, `.m4a`                           | `audio`                                                 |
    | `.doc`, `.docx`, `.xls`, `.xlsx`, `.ppt`, `.pptx`, `.txt`, `.csv`, `.rtf` | `document`                                              |
    | Inline text or HTML (no file upload)                                      | `text` (use the `content` field instead of `uploadIds`) |

    The `fileFormat` must match the file you uploaded. A `.pdf` upload uses `fileFormat: "pdf"`, not `"document"`.
  </Accordion>

  <Accordion title="Can I delete or archive a promotion via the API?" icon="circle-question">
    No. The API doesn't support deleting or archiving promotions. They're retained for compliance audit purposes.
  </Accordion>
</AccordionGroup>

For cross-cutting errors (401, 403, 429, 502), see [Errors & rate limits](/developer/errors).
