> For the complete documentation index, see [llms.txt](https://docs.adreform.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.adreform.com/reference/api-reference/create-an-ad.md).

# Create an Ad

{% hint style="info" %}
This feature is currently in beta. Contact <support@adreform.com> if you're interested in trying the beta.
{% endhint %}

{% openapi src="/files/QzHD2SJJhBbr3N2fe3uR" path="/api/v1/orgs/{organization\_slug}/ads" method="post" %}
[ad-reform-swagger.yaml](https://2718083151-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FWXKnh5nreVaQejzmqBUM%2Fuploads%2Fgit-blob-62087cc88de5585e2f431015233e37e65369350e%2Fad-reform-swagger.yaml?alt=media)
{% endopenapi %}

## Overview

This endpoint allows you to create ads independently from the screenshot request workflow. This is useful when you want to:

* Pre-create ads before requesting screenshots
* Manage ad inventory separately from screenshot generation
* Build integrations that separate ad upload from screenshot workflows

## Idempotent Behavior

This endpoint is fully idempotent when using `lookup_key`:

* If an ad with the specified `lookup_key` already exists in the campaign, we'll return the existing ad with a `200 OK` status
* The `media` in the request is ignored when returning an existing ad
* New ads are created with a `201 Created` status

This makes it safe to retry requests without risking duplicate ad creation.

## Disabled organizations

When an organization is disabled (i.e. it does not have an active subscription), the API will return a 403 Forbidden response:

```json
{
  "status": "failure",
  "errors": {
    "organization": ["Your organization is disabled. Upgrade to keep using the API: https://adreform.com/o/_/billing"]
  }
}
```

## The Campaign object

The `campaign` object tells us where to store the ad. You must include either `campaign.id` *or* `campaign.name` in the request.

{% hint style="info" %}
Using `campaign.name` is the most common approach, as we will find *or* create a Campaign using this key.
{% endhint %}

### `campaign.id`

The Ad Reform ID for an existing Campaign. Use this if you know the specific ID for the campaign you want to use.

### `campaign.name`

The name you want to use for the Campaign. If a Campaign already exists with this name, we'll use that, otherwise we'll create a new one.

{% hint style="info" %}
Note: Campaign names cannot be longer than 250 characters. A `campaign.name` longer than 250 characters will be truncated automatically.
{% endhint %}

## The Ad object

The `ad` object contains the ad creative data.

### `ad.lookup_key` (optional)

A unique identifier for this ad. Use this to make requests idempotent - if an ad already exists with this `lookup_key` in the campaign, we'll return that ad instead of creating a new one.

### `ad.name` (optional)

A human-readable name for the ad.

### `ad.media` (required for new ads)

The ad creative content. Contains:

* `type`: Either `"url"` or `"html_tag"`
* `content`: The URL or HTML tag content

{% hint style="warning" %}
The `html_tag` content cannot exceed 1MB in size.
{% endhint %}

## Media Types

### URL (`type: "url"`)

Use this for ad tag URLs that resolve to a creative. The URL will be fetched and rendered to capture the ad preview.

```json
{
  "media": {
    "type": "url",
    "content": "https://example.com/ad-tag?size=300x250"
  }
}
```

### HTML Tag (`type: "html_tag"`)

Use this for raw HTML ad tags (like script tags or iframe embeds).

```json
{
  "media": {
    "type": "html_tag",
    "content": "<script src=\"https://example.com/ad.js\"></script>"
  }
}
```

## Subscribers (optional)

You can optionally subscribe to notifications when the ad preview is captured by including a `subscribers` array.

### Webhook Subscriber

Receive an `ad.uploaded` webhook event when the ad preview is ready:

```json
{
  "subscribers": [
    {
      "webhook": {
        "url": "https://your-endpoint.com/webhooks"
      }
    }
  ]
}
```

{% hint style="warning" %}
Webhook subscribers only receive notifications when a **new ad is created** (201 Created response). If an existing ad is returned via `lookup_key` (200 OK response), no webhook is sent. The existing ad data is returned in the API response, so you can use it directly.
{% endhint %}

### S3 Subscriber

Push the ad preview image to your S3 bucket when ready:

```json
{
  "subscribers": [
    {
      "s3": {
        "bucket": "your-bucket-name",
        "region": "us-east-1",
        "key_prefix": "ads/"
      }
    }
  ]
}
```

{% hint style="info" %}
S3 subscriptions require an S3 connector to be configured for your organization.
{% endhint %}

## Response

### Success Response (201 Created)

For newly created ads:

```json
{
  "type": "ad",
  "id": "uuid-of-created-ad",
  "name": "Ad Name",
  "lookup_key": "your-lookup-key",
  "status": "pending",
  "category": "display",
  "kind": "url",
  "campaign_id": "uuid-of-campaign",
  "created_at": "2026-01-17T10:30:00Z",
  "html_url": "https://app.adreform.com/o/your-org/campaigns/uuid/ads/uuid"
}
```

### Success Response (200 OK)

When an existing ad is returned via `lookup_key`, the response contains the ad's current state:

```json
{
  "type": "ad",
  "id": "uuid-of-existing-ad",
  "name": "Ad Name",
  "lookup_key": "your-lookup-key",
  "status": "uploaded",
  "category": "display",
  "kind": "url",
  "campaign_id": "uuid-of-campaign",
  "created_at": "2026-01-15T10:30:00Z",
  "html_url": "https://app.adreform.com/o/your-org/campaigns/uuid/ads/uuid"
}
```

### Status Values

| Status             | Description                             |
| ------------------ | --------------------------------------- |
| `pending`          | Ad created, preview capture in progress |
| `uploaded`         | Preview capture completed successfully  |
| `not_found`        | Media URL could not be found            |
| `net_read_timeout` | Network timeout during capture          |
| `generic_error`    | Other capture error                     |

### Error Response (422 Unprocessable Entity)

```json
{
  "status": "failure",
  "errors": {
    "media.content": ["exceeds maximum size of 1MB"]
  }
}
```

## Example Requests

### Create ad with URL media

```bash
curl -X POST "https://app.adreform.com/api/v1/orgs/your-org/ads" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "campaign": {
      "name": "Q1 2026 Campaign"
    },
    "ad": {
      "name": "Banner 300x250",
      "lookup_key": "banner-300x250-v1",
      "media": {
        "type": "url",
        "content": "https://example.com/ads/banner.png"
      }
    }
  }'
```

### Create ad with HTML tag and webhook subscriber

```bash
curl -X POST "https://app.adreform.com/api/v1/orgs/your-org/ads" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "campaign": {
      "name": "Q1 2026 Campaign"
    },
    "ad": {
      "name": "Rich Media Ad",
      "media": {
        "type": "html_tag",
        "content": "<script src=\"https://example.com/rich-media.js\"></script>"
      }
    },
    "subscribers": [
      {
        "webhook": {
          "url": "https://your-endpoint.com/ad-webhook"
        }
      }
    ]
  }'
```

### Idempotent request (returns existing ad)

```bash
# First request creates the ad (201 Created)
curl -X POST "https://app.adreform.com/api/v1/orgs/your-org/ads" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "campaign": {"name": "My Campaign"},
    "ad": {
      "lookup_key": "my-unique-key",
      "media": {"type": "url", "content": "https://example.com/ad.png"}
    }
  }'

# Retry request returns existing ad (200 OK)
curl -X POST "https://app.adreform.com/api/v1/orgs/your-org/ads" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "campaign": {"name": "My Campaign"},
    "ad": {
      "lookup_key": "my-unique-key",
      "media": {"type": "url", "content": "https://different-url.com"}
    }
  }'
# ^ Returns the existing ad, ignores the different media
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.adreform.com/reference/api-reference/create-an-ad.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
