---
title: "Create an ad"
method: POST
path: "/api/v1/ads"
tags: ["ads"]
---

# Create an ad

`POST /api/v1/ads`

## Request

- Base URL: `https://api.growomat.com`
- URL: `https://api.growomat.com/api/v1/ads`
- Auth: HTTP bearer

Creates an ad in an ad group. The default format is a responsive search ad built from `headlinePart1`, `headlinePart2`, `headlinePart3`, `description`, `description2` and `finalUrl`. Platforms whose creative is a separate object (a promoted post, for example) read `creative` instead.

An ad is shown only once its campaign has been synced and is serving. Changes reach the ad platforms on the next sync.

**Errors**

- `400 VALIDATION_ERROR`: the body failed validation; `details.issues[]` names each field.
- `409 COMPETITOR_SIMILARITY`: the copy is too close to an ad by a competitor this business tracks, or names one. `details` says which ad it collided with and whether `overridable` is true; resend with `competitorSimilarityAck: true` to save manual copy anyway (recorded on the ad). Generated copy can never override.

## Headers

- `X-Space-Id` string

## Request body

- AdCreate: An ad. The headline and description fields describe a responsive search ad; other formats read `creative` and the attached assets.
  - `angleId` string: The angle (claim) this ad argues. Results are reported per angle.
  - `conceptId` string: The concept (way of making the claim) this ad is a rendition of.
  - `angleProvenance` 'generated' | 'user' | 'inferred': Where the angle stamp came from: written by Growomat, set by you, or inferred from the copy. Defaults to `user` when you set `angleId` yourself.
  - `competitorSimilarityAck` boolean: Save manual copy that the competitor similarity check flagged. Only honoured when the refusal said `overridable: true`; the decision is recorded on the ad.
  - `_native` NativeExtensions: Settings that exist on one platform only, keyed by platform id (for example `google-ads`). Each value is a free-form object that is stored and returned exactly as sent; keys Growomat does not know are kept. Absent by default. Keys read by platforms whose ad is a post (on the ad, with the campaign as fallback): `postType` (`TEXT` or `IMAGE`), `postTitle`, `postBody`, `callToAction`, `imageUrl`, `videoUrl`, `videoThumbnailUrl`, `carouselCards` (`[{ imageUrl, destinationUrl }]`), `communities`, `interests`. Delivery keys read on the campaign: `objective`, `goalType`, `startTime`, `endTime`, `optimizationGoal`, `conversionPixelId`.
  - `adGroupId` string, required: The ad group this ad belongs to.
  - `campaignId` string, required: The campaign of that ad group.
  - `type` 'RESPONSIVE_SEARCH_AD' | 'RESPONSIVE_DISPLAY_AD' | 'PERFORMANCE_MAX_ASSET_GROUP' | 'VIDEO_AD' | 'REDDIT_TEXT_POST' | 'REDDIT_IMAGE_POST' | 'REDDIT_VIDEO_POST' | 'REDDIT_CAROUSEL_POST' | 'META_LINK_AD' | 'META_IMAGE_AD' | 'META_VIDEO_AD' | 'META_CAROUSEL_AD' | 'OPENAI_CHAT_CARD' | 'OPENAI_PRODUCT_AD_TEMPLATE' | 'LINKEDIN_SINGLE_IMAGE' | 'LINKEDIN_TEXT_AD' | 'LINKEDIN_VIDEO' | 'LINKEDIN_CAROUSEL' | 'LINKEDIN_DOCUMENT': Ad format. Defaults from the campaign type per platform; ads imported from a platform record the format that actually serves.
  - `creative` AdCreative: Instructions for platforms whose ad is a separate creative object, such as a promoted post. Platforms that build the ad from the headline and description fields ignore it.
    - `format` string: Ad format hint. Normally derived from the campaign type.
    - `mediaAssetIds` string[]: Assets to build the creative from.
    - `body` string: Post body. Defaults to the ad's remaining copy.
    - `imageUrl` string, uri: Image to use, by URL.
    - `videoUrl` string, uri: Video to use, by URL.
    - `thumbnailUrl` string, uri: Still shown before a video plays. Required by platforms that mandate one.
  - `headlinePart1` string, required: First headline. Up to 30 characters on search platforms.
  - `headlinePart2` string, required: Second headline. Up to 30 characters on search platforms.
  - `headlinePart3` string: Third headline, optional. Up to 30 characters on search platforms.
  - `description` string, required: First description line. Up to 90 characters on search platforms.
  - `description2` string: Second description line, optional. Up to 90 characters on search platforms.
  - `finalUrl` string, uri, required: The landing page.
  - `path1` string: First display-path segment shown after the domain in the ad.
  - `path2` string: Second display-path segment.
  - `status` 'ENABLED' | 'PAUSED' | 'REMOVED': A paused ad is not shown.
  - `sitelinks` Sitelink[]: Extra links shown under the ad.
    - `text` string: Link text.
    - `url` string: Where the link goes.
    - `description1` string: First description line.
    - `description2` string: Second description line.
    - `trackingUrlTemplate` string: Tracking template for this link.
    - `urlCustomParameters` object: Custom `{_name}` parameters for the tracking template.
  - `callouts` string[]: Short phrases shown under the ad, for example "Free shipping".
  - `structuredSnippets` StructuredSnippet[]: Labelled lists shown under the ad.
    - `header` string, required: The list label, from the platform's fixed set (for example `Services`, `Brands`).
    - `values` string[], required: The list entries.
  - `customLabels` string[]: Free-form labels for your own grouping and reporting.
  - `trackingUrlTemplate` string: Tracking template for this ad; overrides the ad group's and the campaign's.
  - `finalUrlSuffix` string: Query string appended to the final URL; overrides the ad group's and the campaign's.
  - `urlCustomParameters` object: Custom `{_name}` parameters available to tracking templates.

## Response `200`

OK. The result is in `data`. List responses also carry `meta` with `nextCursor` and `total`.

- SuccessEnvelope: The shape of every successful response.
  - `data` unknown, required
  - `meta` object: Present on list responses.
    - `nextCursor` union: Pass as `cursor` to get the next page. `null` on the last page.
      - string
      - number
    - `total` integer: Number of items in this page.

## Other responses

- `201`: Created. The new resource is in `data`.
- `400`: The request is malformed or fails validation. `error.code` is `VALIDATION_ERROR` (with `details.issues[]` naming each field), `INVALID_REQUEST` (the body is not valid JSON or has the wrong shape), or one of the specific codes listed in this operation's description.
- `401`: The bearer token is missing, invalid, expired or revoked (`INVALID_TOKEN`).
- `403`: The token lacks the scope this operation needs (`INSUFFICIENT_SCOPE`), the plan is read-only (`READ_ONLY_MODE`), or the caller is not a member of the Space named in `X-Space-Id` (`NOT_A_MEMBER`).
- `404`: No such resource in the active Space (`NOT_FOUND`), or `X-Space-Id` names a Space that does not exist (`SPACE_NOT_FOUND`).
- `409`: The request conflicts with the current state, for example a delete blocked by dependents or a sync already running. This operation's description names the `error.code` values it can return.
- `429`: Too many requests (`RATE_LIMITED`). Wait for the number of seconds in the `Retry-After` header, then retry.

## Changes

- **2026-10-20** `f02611b531aa`: 1 info
  - added the new optional request property `competitorSimilarityAck`

[Operation history](https://skmtc.dev/growomat/apis/growomat-public-api/changes/api/v1/ads/post.md)

---

[API](https://skmtc.dev/growomat/apis/growomat-public-api.md) · [All operations](https://skmtc.dev/growomat/apis/growomat-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/growomat/apis/growomat-public-api/revisions/f02611b531aa?raw)
