Every API here is available over the APIs.io API and to AI agents over MCP.
{
"openapi": "3.1.0",
"info": {
"title": "Growomat Public API",
"version": "3.1.0",
"description": "Welcome to the **Growomat API**: the same engine that runs the Growomat app, open to your own\ncode and to AI agents.\n\nEverything Growomat does across **Google Ads**, **Microsoft Advertising**, **Reddit Ads**, **Meta Ads**, **ChatGPT Ads** and **LinkedIn Ads** is here. Create businesses, campaigns,\nad groups, ads and keywords; attach images and video; set schedules and locations; push it all\nlive with a sync; then read back performance, delivery status and conversions. This is the\nstable, versioned public surface at `/api/v1`.\n\n## Base URL\n\nEvery path in this reference is shown in full (`/api/v1/…`) and is served from:\n\n```\nhttps://api.growomat.com\n```\n\n## Authentication\n\nIn the app, open **Tools → API** (`/developers/api`) and create a personal access token. Give it\na name, an expiry of up to one year and only the scopes it needs. The token is shown once and\nlooks like `grow_…`. Send it as a bearer token on every request:\n\n```\nAuthorization: Bearer grow_your_token\n```\n\nEach operation names the scope it needs (`campaigns:read`, `sync:write`, and so on). A token\nwithout that scope gets `403 INSUFFICIENT_SCOPE`. Tokens work only on `/api/v1`.\n\n**Website keys** (`grow_pub_…`) are a second, much narrower credential for code that runs in a\nbrowser. They carry no scopes and reach only *Identify a visitor* and *Report conversion\nevents*; every other operation refuses them with `401`.\n\n## Spaces\n\nYour campaigns live in **Spaces**: one personal Space, plus any shared Spaces you belong to.\nEvery operation runs inside one Space. Send `X-Space-Id: spc_…` to choose it, or omit the header\nfor your personal Space. *List your Spaces* returns the ids. Ad platform connections belong to\nthe account, not the Space, so any Space can sync to any connected platform.\n\n## How a campaign goes live\n\nNothing you create through this API spends money until you sync it.\n\n1. Check *List ad platform connections*: a platform must be `ready` to sync to, and budgets are\n judged in its account currency.\n2. Create a business, then a campaign (it starts `PAUSED`) with `_platformBudgets` saying where\n it runs, then its ad groups, ads and keywords. Platform-only settings go in `_native`.\n A campaign created as `DRAFT` is held back until *Confirm a draft campaign*.\n3. Upload the images the formats need (*Create an asset* takes the bytes inline) and attach\n them with *Ad assets* or *Campaign assets*.\n4. Set the schedule (*Set the ad schedule*) and the locations (*Set geo targeting*).\n5. Call *Validate a campaign before export* and fix every `requirement` finding; *Preview a\n campaign's ads* shows each ad as it will be sent.\n6. Call *Preview a sync plan* to see what would be imported, exported and removed. Keep its\n `planHash`.\n7. Call *Trigger a sync* with that `planHash`. It returns a `syncId` at once.\n8. Poll *Get a sync* until `effectiveStatus` is `completed`, `partial`, `failed` or `cancelled`.\n9. When you want the campaign to serve, set its `status` to `ENABLED` and sync again.\n\nFrom then on *Get a campaign* tells you whether it is serving (`deliverySummary`) and, when it\nis not, why (`reasons[]`).\n\n## Responses and errors\n\nEvery success is wrapped:\n\n```json\n{ \"data\": { … }, \"meta\": { \"nextCursor\": \"…\", \"total\": 42 } }\n```\n\n`meta` appears on list responses. Every error uses one shape:\n\n```json\n{ \"error\": { \"code\": \"NOT_FOUND\", \"message\": \"Campaign not found\", \"details\": { … } } }\n```\n\n| Status | `error.code` | Meaning |\n| --- | --- | --- |\n| 400 | `VALIDATION_ERROR` | A field is wrong; `details.issues[]` names each one. |\n| 400 | `INVALID_REQUEST` | The body is not valid JSON or has the wrong shape. |\n| 401 | `INVALID_TOKEN` | Missing, invalid, expired or revoked token. |\n| 402 | `LIMIT_REACHED` | The plan does not include this, or its monthly allowance is spent. |\n| 403 | `INSUFFICIENT_SCOPE` | The token lacks the scope this operation needs. |\n| 403 | `READ_ONLY_MODE` | The plan can look at everything and change nothing. |\n| 403 | `NOT_A_MEMBER` | You are not a member of the Space in `X-Space-Id`. |\n| 404 | `NOT_FOUND` | No such resource in the active Space. |\n| 404 | `SPACE_NOT_FOUND` | `X-Space-Id` names a Space that does not exist. |\n| 409 | *varies* | The request conflicts with current state; see the operation. |\n| 429 | `RATE_LIMITED` | Too many requests; wait for `Retry-After`. |\n\nOperation-specific codes (for example `SYNC_ALREADY_ACTIVE`) are listed under **Errors** on each\noperation.\n\n## Pagination\n\nList operations take `limit` (default 50, maximum 200) and `cursor`. Follow `meta.nextCursor`\nuntil it is `null`.\n\n## Rate limits\n\nEvery account has a daily request allowance that depends on its plan, shared by all of its\ntokens and reset at midnight UTC, plus a short per-minute burst limit. Past either, requests\nreturn `429 RATE_LIMITED` with a `Retry-After` header. Website keys have their own, separate\nallowance.\n\n## Official SDKs\n\nTyped clients are generated from this exact document, so they cannot drift from the API.\n\n- **TypeScript**: `npm install @defoai/growomat`\n- **Python**: `pip install growomat` (alpha)\n- **Go, PHP and Java**: on the way.\n\n```ts\nimport { Growomat } from '@defoai/growomat';\n\nconst growomat = new Growomat({ token: process.env.GROWOMAT_TOKEN });\nawait growomat.campaigns.create({ name: 'Spring Launch', budget: 50 });\n```\n\nEvery operation below shows a snippet per language. For languages still on the way, the snippet\nshows the shape the client will have.\n\n## For AI agents\n\nIf you are an agent working from this document:\n\n- Start with *Introspect the current token* to learn your scopes and the account time zone.\n- Take ids from list operations; never guess them. Platform ids are exactly the values in the\n platform enums of this document.\n- Prefer the dry-run forms: `dry_run: true` on schedules and geo targeting, *Preview a sync plan*\n before *Trigger a sync*, and `planHash` to bind the sync to what you previewed.\n- A `409` is a decision, not a retry. Read `error.code` and the operation's **Errors** list.\n- When a campaign is not serving, relay `deliverySummary.reasons[]`; it is the explanation.\n\n## Versioning\n\n`/api/v1` is stable. New fields, operations and enum values arrive without a change to the path;\n`info.version` records them. Ignore fields you do not know."
},
"servers": [
{
"url": "https://api.growomat.com",
"description": "Production"
},
{
"url": "https://{host}",
"description": "Environment-templated base URL",
"variables": {
"host": {
"default": "api.growomat.com",
"enum": [
"api.growomat.com",
"api-ads-staging.defoai.com"
],
"description": "api.growomat.com is production; api-ads-staging.defoai.com is staging."
}
}
}
],
"tags": [
{
"name": "meta",
"x-displayName": "Start here",
"description": "Check your token, find your Spaces, see which ad platforms are connected and download this document."
},
{
"name": "businesses",
"x-displayName": "Businesses",
"description": "The advertiser: its website, what it does and its brand voice. Campaigns belong to a business, and every piece of generated copy is written for one."
},
{
"name": "campaigns",
"x-displayName": "Campaigns",
"description": "The unit of budget, targeting and delivery. A campaign is created locally and reaches the ad platforms only when you sync it. Delivery status on every campaign tells you whether it is serving and, if not, why."
},
{
"name": "ad-groups",
"x-displayName": "Ad groups",
"description": "Groups of ads and keywords inside a campaign, with a default bid."
},
{
"name": "ads",
"x-displayName": "Ads",
"description": "The creative: headlines, descriptions, the landing page, and extensions such as sitelinks and callouts."
},
{
"name": "keywords",
"x-displayName": "Keywords",
"description": "The search terms an ad group bids on, with match type and optional negative keywords."
},
{
"name": "assets",
"x-displayName": "Assets",
"description": "Images, logos and videos, uploaded or registered by URL, and attached to campaigns, ad groups or ads."
},
{
"name": "campaign-assets",
"x-displayName": "Campaign assets",
"description": "Which asset plays which role in a campaign."
},
{
"name": "ad-assets",
"x-displayName": "Ad assets",
"description": "Which asset plays which role in an ad or ad group; the level display, video and Performance Max formats read from."
},
{
"name": "media",
"x-displayName": "Media requirements",
"description": "What each platform and campaign type needs before it can serve."
},
{
"name": "angles",
"x-displayName": "Angles and concepts",
"description": "Angles are the claims your ads argue; concepts are ways of making a claim. Stamp ads with them and Growomat reports which claim wins, and how long a test will take."
},
{
"name": "competitors",
"x-displayName": "Competitor research",
"description": "The competitors of a business: what they publish in the public ad libraries, which claims and offers they lead with, and where they beat you in your own auctions. Library data shows what was published, never what worked."
},
{
"name": "sync",
"x-displayName": "Sync",
"description": "The only way anything reaches an ad platform. Preview the plan, trigger the sync, poll the record, and unlink when you need a clean slate."
},
{
"name": "budget",
"x-displayName": "Budget and Emergency Brake",
"description": "The account budget and the brake that pauses campaigns automatically when real spend passes it."
},
{
"name": "growth-manager",
"x-displayName": "Growth manager",
"description": "What the growth manager last told the Space, and what it still has to report back on. Read-only."
},
{
"name": "reports",
"x-displayName": "Reports",
"description": "Performance over time and per entity, account totals, and what the platforms currently refuse to show."
},
{
"name": "conversions",
"x-displayName": "Conversions",
"description": "Tell the ad platforms what your clicks turned into: offline conversions, first-party identities, conversion events from your stack, adjustments, and the health of the loop."
},
{
"name": "content",
"x-displayName": "Social content",
"description": "Research-grounded social posts, drafted for a business, interviewed, reviewed and confirmed."
}
],
"x-tagGroups": [
{
"name": "Start here",
"tags": [
"meta"
]
},
{
"name": "Campaign structure",
"tags": [
"businesses",
"campaigns",
"ad-groups",
"ads",
"keywords"
]
},
{
"name": "Creative and media",
"tags": [
"assets",
"campaign-assets",
"ad-assets",
"media",
"angles"
]
},
{
"name": "Go live",
"tags": [
"sync",
"budget"
]
},
{
"name": "Measure",
"tags": [
"reports",
"conversions",
"growth-manager"
]
},
{
"name": "Market research",
"tags": [
"competitors"
]
},
{
"name": "Social content",
"tags": [
"content"
]
}
],
"components": {
"securitySchemes": {
"ApiToken": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "Growomat API Token (grow_...)",
"description": "A personal access token created in the app under **Tools → API** (`/developers/api`). Carries the scopes it was created with."
},
"PublishableKey": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "Growomat website key (grow_pub_...)",
"description": "The credential that is safe to put in a public web page. It carries no scopes: its capability is the credential class itself, and it reaches exactly two operations, *Identify a visitor* and *Report conversion events*. Every other operation, including every read, refuses it with the same `401` an unknown credential gets.\n\nA key is bound to one Space and, optionally, to a list of websites it may be used from. It is metered on its own allowance and never draws on the account request quota.\n\nOn *Report conversion events* a website key runs a constrained form: the source is set by the server, adjustments are refused, conversion actions are never created, and the batch is capped. Sales it reports are recorded as unverified claims, visible to the account owner, never sent to an ad platform and never counted in a report, until a server-side source confirms the same `orderId`."
}
},
"schemas": {
"SuccessEnvelope": {
"type": "object",
"required": [
"data"
],
"description": "The shape of every successful response.",
"properties": {
"data": {
"description": "The result: an object for single resources, an array for lists."
},
"meta": {
"type": "object",
"description": "Present on list responses.",
"properties": {
"nextCursor": {
"type": [
"string",
"number",
"null"
],
"description": "Pass as `cursor` to get the next page. `null` on the last page."
},
"total": {
"type": "integer",
"description": "Number of items in this page."
}
}
}
}
},
"ErrorEnvelope": {
"type": "object",
"required": [
"error"
],
"description": "The shape of every error response.",
"properties": {
"error": {
"type": "object",
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "string",
"description": "Stable machine-readable code, for example `NOT_FOUND`. Safe to branch on."
},
"message": {
"type": "string",
"description": "Human-readable explanation. May change; do not parse it."
},
"details": {
"description": "Extra context, when there is any: validation issues, conflicting ids, and so on."
}
}
}
}
},
"LimitReachedBody": {
"type": "object",
"required": [
"error",
"reason",
"action"
],
"description": "The body of a `402`: a plan limit was hit. `reason` says which kind, because the four kinds call for different fixes.",
"properties": {
"error": {
"type": "string",
"enum": [
"LIMIT_REACHED"
]
},
"reason": {
"type": "string",
"enum": [
"NOT_IN_PLAN",
"QUOTA_SPENT",
"SUBSCRIPTION_EXPIRED",
"PLAN_UNAVAILABLE"
],
"description": "`QUOTA_SPENT`: the allowance exists, has been used up, and comes back; this is the only value with a real `cap`, `currentUsage` and `resetAt`. `NOT_IN_PLAN`: the plan does not include this capability at all, so those three fields are `null`; there is nothing to wait for. `SUBSCRIPTION_EXPIRED`: the subscription lapsed; renewing restores it. `PLAN_UNAVAILABLE`: the account references a plan that no longer exists; contact support, no purchase resolves it."
},
"action": {
"type": "string",
"description": "The metered action that was refused, for example `content_draft`."
},
"cap": {
"type": [
"integer",
"null"
],
"description": "The allowance. `0` means unlimited; `null` means there is no allowance to report. The two are not the same."
},
"currentUsage": {
"type": [
"integer",
"null"
],
"description": "How much of the allowance has been used this period."
},
"capField": {
"type": [
"string",
"null"
],
"description": "The plan field this action is metered against, for example `monthlyResearchQueries`, so a client can find the cheapest plan that raises this particular allowance."
},
"resetAt": {
"type": [
"integer",
"null"
],
"description": "When the allowance refills, as epoch milliseconds. `null` for every reason except `QUOTA_SPENT`."
},
"upgradeUrl": {
"type": "string",
"description": "Where to change the plan."
}
}
},
"ScheduleWindow": {
"type": "object",
"required": [
"day",
"startHour",
"endHour"
],
"description": "One block of time on one day of the week during which ads may show.",
"properties": {
"day": {
"type": "string",
"enum": [
"MONDAY",
"TUESDAY",
"WEDNESDAY",
"THURSDAY",
"FRIDAY",
"SATURDAY",
"SUNDAY"
],
"description": "The day of the week."
},
"startHour": {
"type": "integer",
"minimum": 0,
"maximum": 23,
"description": "Hour the window opens (0 to 23), in the schedule's time zone."
},
"startMinute": {
"type": "string",
"enum": [
"ZERO",
"FIFTEEN",
"THIRTY",
"FORTY_FIVE"
],
"default": "ZERO",
"description": "Minute the window opens, in quarter hours."
},
"endHour": {
"type": "integer",
"minimum": 0,
"maximum": 24,
"description": "Hour the window closes (0 to 24). `24` means end of day."
},
"endMinute": {
"type": "string",
"enum": [
"ZERO",
"FIFTEEN",
"THIRTY",
"FORTY_FIVE"
],
"default": "ZERO",
"description": "Minute the window closes, in quarter hours."
},
"bidModifier": {
"type": "number",
"minimum": 0.1,
"maximum": 10,
"description": "Bid multiplier for this window; `1.0` means no change. Platforms that express this as a percentage receive the converted value, clamped to their range; a clamp is reported as a `BID_ADJUSTMENT_CLAMPED` warning."
}
}
},
"StructuredSchedule": {
"type": "object",
"required": [
"windows"
],
"description": "A weekly ad schedule: up to 84 windows and the time zone they are in.",
"properties": {
"windows": {
"type": "array",
"maxItems": 84,
"items": {
"$ref": "#/components/schemas/ScheduleWindow"
},
"description": "The windows during which ads may show."
},
"timezone": {
"type": "string",
"description": "IANA time zone the hours are in, for example `Europe/Berlin`. Defaults to the account time zone."
}
}
},
"CampaignAdScheduleField": {
"description": "The campaign's ad schedule, as a preset name (`ALL_HOURS_ALL_DAYS`, `BUSINESS_HOURS`, `CUSTOM`) or as a `StructuredSchedule`. On read, `adSchedule` returns what you last wrote and `adScheduleStructured` always holds the expanded weekly schedule. `CUSTOM` is treated as `ALL_HOURS_ALL_DAYS`.",
"oneOf": [
{
"type": "string",
"enum": [
"ALL_HOURS_ALL_DAYS",
"BUSINESS_HOURS",
"CUSTOM"
]
},
{
"$ref": "#/components/schemas/StructuredSchedule"
}
]
},
"SetAdScheduleWindow": {
"type": "object",
"required": [
"days",
"start",
"end"
],
"description": "A window in human terms: which days, from when, until when.",
"properties": {
"days": {
"type": "array",
"items": {
"type": "string"
},
"description": "Day tokens: `MON` to `SUN`, full day names, `WEEKDAYS` or `WEEKEND`."
},
"start": {
"type": "string",
"description": "Start time as `HH:MM`. Minutes must be `00`, `15`, `30` or `45`."
},
"end": {
"type": "string",
"description": "End time as `HH:MM`; `24:00` is allowed. An end at or before the start means the window crosses midnight and is split automatically."
},
"bidModifier": {
"type": "number",
"minimum": 0.1,
"maximum": 10,
"description": "Bid multiplier for this window; `1.0` means no change."
}
}
},
"SetAdSchedule": {
"type": "object",
"description": "Human-friendly ad schedule input. Provide a preset and/or windows; the server expands them, splits windows that cross midnight, resolves the time zone and stores the result as `adScheduleStructured`.",
"properties": {
"timezone": {
"type": "string",
"description": "`local` (the searcher's own time zone where the platform supports it, otherwise account time with a warning), `UTC`, or an IANA zone such as `Europe/Berlin`. A zone that differs from the account zone is converted into account-time hours before storing."
},
"windows": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SetAdScheduleWindow"
},
"description": "The windows to set."
},
"preset": {
"type": "string",
"enum": [
"ALL_HOURS_ALL_DAYS",
"BUSINESS_HOURS",
"OVERNIGHT",
"WEEKENDS",
"PHARMACY_OPEN_HOURS"
],
"description": "A ready-made schedule. Can be combined with `windows`."
},
"platform": {
"type": "string",
"enum": [
"google-ads",
"microsoft-ads",
"reddit-ads",
"meta-ads",
"openai-ads",
"linkedin-ads"
],
"description": "Which platform's time zone rules to apply when the campaign runs on several. Inferred when omitted."
},
"dry_run": {
"type": "boolean",
"description": "When `true`, return the expanded schedule and warnings without changing the campaign."
}
}
},
"SetGeoTargetingArea": {
"description": "A named place, as a plain string or as an object with hints for resolving it.",
"oneOf": [
{
"type": "string",
"description": "A place name, for example \"Germany\", \"Berlin\" or \"Mitte\"."
},
{
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"type": "string",
"description": "The place name."
},
"type": {
"type": "string",
"description": "What kind of place to look for, for example `CITY` or `DISTRICT`."
},
"countryCode": {
"type": "string",
"description": "ISO country code to search within."
}
}
}
]
},
"SetGeoTargetingRadius": {
"type": "object",
"required": [
"radius"
],
"description": "A circle around a point, given by coordinates or by a place name.",
"properties": {
"around": {
"type": "string",
"description": "Place name to centre on; resolved to coordinates on the server. Alternative to `lat` and `lng`."
},
"lat": {
"type": "number",
"minimum": -90,
"maximum": 90,
"description": "Latitude of the centre."
},
"lng": {
"type": "number",
"minimum": -180,
"maximum": 180,
"description": "Longitude of the centre."
},
"radius": {
"type": "number",
"exclusiveMinimum": 0,
"description": "Radius of the circle."
},
"unit": {
"type": "string",
"enum": [
"km",
"mi"
],
"default": "km",
"description": "Unit of `radius`."
},
"name": {
"type": "string",
"description": "Display label for the circle."
},
"countryCode": {
"type": "string",
"description": "ISO country code to resolve `around` within."
}
}
},
"SetGeoTargeting": {
"type": "object",
"description": "Human-friendly location input. Provide the structured `geoTargets` array, or `include`, `exclude` and `radii` lists of place names that the server resolves, or only `locationIntent` to change that alone. `dry_run: true` returns the resolved rows and a plain-language sentence describing the effective targeting, without changing the campaign.",
"properties": {
"geoTargets": {
"type": "array",
"items": {
"$ref": "#/components/schemas/GeoTarget"
},
"description": "The full structured list, replacing the current one."
},
"include": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SetGeoTargetingArea"
},
"description": "Places to show ads in."
},
"exclude": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SetGeoTargetingArea"
},
"description": "Places to carve out of the included set."
},
"radii": {
"type": "array",
"items": {
"$ref": "#/components/schemas/SetGeoTargetingRadius"
},
"description": "Circles to show ads in."
},
"locationIntent": {
"type": "string",
"enum": [
"PRESENCE",
"PRESENCE_OR_INTEREST"
],
"description": "Who counts as being in the targeted places. See `CampaignCreate.locationIntent`."
},
"dry_run": {
"type": "boolean",
"description": "When `true`, preview the resolved set and the sentence without changing the campaign."
}
}
},
"GeoTarget": {
"type": "object",
"required": [
"kind",
"name"
],
"description": "One row of a campaign's location targeting: a named area or a radius circle, included or excluded.",
"properties": {
"id": {
"type": "string",
"description": "Stable row id. Generated when omitted."
},
"kind": {
"type": "string",
"enum": [
"AREA",
"RADIUS"
],
"description": "`AREA` is a named place, from a country down to a postcode. `RADIUS` is a circle around a point."
},
"negative": {
"type": "boolean",
"default": false,
"description": "`true` excludes this row from the included set. Not allowed on `RADIUS` rows (`400 RADIUS_EXCLUSION_UNSUPPORTED`), and a set where every row is negative is rejected (`400 ONLY_EXCLUSIONS`)."
},
"bidAdjustment": {
"type": "number",
"description": "Percent bid adjustment for this row. Included rows only."
},
"reach": {
"type": "number",
"description": "Audience estimate as reported by the platform, when it gave one. Read-only; never estimated by Growomat."
},
"criterionId": {
"type": [
"string",
"null"
],
"description": "Areas: the Google Ads geo target id."
},
"msLocationId": {
"type": [
"string",
"null"
],
"description": "Areas: the Microsoft Advertising location id."
},
"canonicalId": {
"type": [
"string",
"null"
],
"description": "Areas: Growomat's platform-independent id for the place."
},
"name": {
"type": "string",
"description": "Display name of the place or circle."
},
"type": {
"type": "string",
"enum": [
"COUNTRY",
"REGION",
"METRO",
"CITY",
"DISTRICT",
"POSTAL_CODE",
"AIRPORT",
"UNIVERSITY",
"GLOBAL"
],
"description": "Areas only: what kind of place this is."
},
"countryCode": {
"type": [
"string",
"null"
],
"description": "Areas: ISO 3166-1 country code."
},
"lat": {
"type": "number",
"minimum": -90,
"maximum": 90,
"description": "Circles only: latitude of the centre."
},
"lng": {
"type": "number",
"minimum": -180,
"maximum": 180,
"description": "Circles only: longitude of the centre."
},
"radius": {
"type": "number",
"exclusiveMinimum": 0,
"description": "Circles only. Clamped to the platform's range (1 to 500) when pushed; a clamp is reported as a `RADIUS_CLAMPED_TO_MIN` warning."
},
"unit": {
"type": "string",
"enum": [
"km",
"mi"
],
"default": "km",
"description": "Circles only: the unit of `radius`."
}
}
},
"BiddingStrategy": {
"type": "object",
"description": "How the platform bids for this campaign.",
"properties": {
"type": {
"type": "string",
"enum": [
"MANUAL_CPC",
"ENHANCED_CPC",
"MAXIMIZE_CLICKS",
"MAXIMIZE_CONVERSIONS",
"MAXIMIZE_CONVERSION_VALUE",
"TARGET_CPA",
"TARGET_ROAS",
"TARGET_IMPRESSION_SHARE"
],
"default": "MAXIMIZE_CLICKS",
"description": "The bidding strategy. Target strategies read their target from the matching field below."
},
"targetCpa": {
"type": "number",
"exclusiveMinimum": 0,
"description": "Target cost per acquisition, for `TARGET_CPA`."
},
"targetRoas": {
"type": "number",
"exclusiveMinimum": 0,
"description": "Target return on ad spend as a ratio (2.5 means 250%), for `TARGET_ROAS`."
},
"targetImpressionShare": {
"type": "number",
"minimum": 0,
"maximum": 100,
"d
# --- truncated at 32 KB (708 KB total) ---
# Full source: https://artifacts.apis.io/growomat-com/openapi/_original/growomat-com-api-openapi.json